So, it works! I wanted to explain some improvements over CI’s current active record system. First of all is how the modeling system works. This system supports anything that can be classified into a model, and the database is the obvious default one.
There are several things involved: Finders, Models, ModelDefinitions, Records, and RecordSets.
- Models are the high level things, eg: the DatabaseModel.
- Finders go and find things, in the case of the database, they will find row(s).
- Records and RecordSets are returned by the finder functions. A record set is simply an iterator that returns a Record on each iteration.
Why use this active records system instead of code igniters?
First of all, code igniter’s current system is lacking. First, a CI_DB_active_record instance class is passed around and used to construct queries. This seems good, except for the fact that if you are building more than one query at the same time, you will end up having merged queries. Second, $this->load->model(). This function sets the model name to an instance variable—eg:$this->model_name—which is available everywhere. Again, this seems like a convenience but it’s not. Consider having to write $this->model_name-> over and over again instead of $model_name->. It’s a small difference on a small scale, but in an enterprise application it make a huge difference to programmer patience and readability. Third, again with the load->model(). Because it sets it to an instance variable, you end up losing track of which resources you have loaded. If the function were an explicit return, then the models that they return would enter and leave scope in a predicable way, and by having to assign variables to them, when refactoring it would be obvious that you have a defined variable that isn’t in use. (this last point is really just nitpicking).
Well, whatever for criticisms. That won’t get anyone anywhere and by and large code igniter is a solid product. So, here’s the functionality I have added:
First, a new controller: ActiveController. This controller has two new methods: getFinder and setDatasource. The default datasource/model is ‘database’, but if you make any others, they are easy to work in. getFinder will look for /application/libraries/ActiveRecord/models/<datasource>/model.php. That is the main file for any model. The reason for the new controller is that you can use this extension and *not* break any existing code because the CI_Loader class is not extended.
To use finders, you look for a ‘ModelDefinition’. In the case of databases, model definitions serve to describe database tables. They define everything: the name, primary key, all the columns, the foreign keys, and the relations. There is another special feature of model definitions and that is their prepareSelect function. This function allows the programmer to add things to a query object that is used every time a finder function is called to fetch row(s).
The model definitions supply 3 functions for dealing with table relations: hasOne, hasMany, and hasForeignKey. Within a model definition’s init() function, the programmer can do things such as the following:
$this->hasOne(model name [, alias]);
$this->hasMany(model name [, alias]);
$this->hasMany(model name [, alias])[->through(model name)]
$this->hasForeignKey(this table column, <other model name>.<other column name>);
Note: ->through() can be chained as many times as you want.
So, some examples… Imagine a forum: categories, forums, threads.
class CategoriesDefinition extends DatabaseModelDefinition {
public function init() {
$this->category_id('int')
->length(10)
->primary()
->increment();
$this->hasMany('forums');
$this->hasMany('threads')->through('forums');
}
}