Skip to content
World Wide Web Server edited this page Jul 4, 2012 · 44 revisions

This page details an implementation of Active Record for CodeIgniter that more accurately mimics the way Ruby on Rails works with models. It allows you to abstract large amounts of the searching and creation code out of your models, and can help with streamlining your controller code as well.

File:Activerecord.php.zip

[b]NOTE! Due to some of the code this technique uses, it will only work if you are running PHP5![/b]

[h3]Implementation[/h3]

To begin using the ActiveRecord class, download the most recent version and save it into your [b]/application/libraries[/b] folder. This file replaces the CodeIgniter Model class, and all your models will inherit from it.

Next, edit [b]/application/config/autoload.php[/b] and make sure that both the ActiveRecord and database libraries are automatically loaded when CodeIgniter runs:

[code] $autoload['libraries'] = array('activerecord', 'database'); [/code]

You are now ready to start using the ActiveRecord class in your application.

[h3]Convention Over Configuration[/h3]

Before we get onto actually using the class, let's talk a little about the approach Ruby on Rails takes to models and database design, and in particular its principle of "Convention over configuration".

When creating your database tables, there are certain conventions that the ActiveRecord class assumes you to be following. These are:

  • Every model maps to a database table
  • The table name will be the lowercase pluralised model name
  • Every table's unique key will be named [b]id[/b]
  • Relationships between tables will be handled by a separate relationship table, named for the two tables it connects, in alphabetical order, separated by an underscore ("_")

Some examples to clarify those three requirements:

  • I have a model named [b]Page[/b], so the table it maps to is named [b]pages[/b]
  • I have a model named [b]Person[/b], so the table it maps to is named [b]people[/b]
  • The [b]pages[/b] table contains fields: ** id ** title ** content
  • The [b]people[/b] table contains fields: ** id ** first_name ** last_name
  • Relationships between people and pages are held in a table named [b]pages_people[/b]
  • The [b]pages_people[/b] table contains fields: ** page_id ** person_id

Hopefully that all makes sense and seems fairly straightforward.

[h3]Creating A Model[/h3]

Continuing the example above, let's create our Page model:

[code] <?php if (!defined('BASEPATH')) exit('No direct script access allowed');

class Page extends ActiveRecord {

function __construct()
{
    parent::ActiveRecord();
    $this->_class_name = strtolower(get_class($this));
    $this->_table = $this->_class_name . 's';
    $this->_columns = $this->discover_table_columns();
}

}

?> [/code]

This is the basic template for all models using ActiveRecord - and, in many cases, that is all you will need to write in the model file! When the object is instantiated, it stores some meta information about itself: its class name, table name, and the columns that exist in its table.

Models for non-standard plurals are only slightly different - here's our Person model:

[code] <?php if (!defined('BASEPATH')) exit('No direct script access allowed');

class Person extends ActiveRecord {

function __construct()
{
    parent::ActiveRecord();
    $this->_class_name = strtolower(get_class($this));
    $this->_table = 'people';
    $this->_columns = $this->discover_table_columns();
}

}

?> [/code]

The only difference is that we need to hardcode the table name instead of extrapolating it from the class name.

[h3]Using an ActiveRecord model in your Controller[/h3]

Now that we have created models to represent our pages and people, let's use them in a controller. Our application will be a simple wiki, where there are multiple pages which have been created by multiple people. Firstly, let's setup our basic controller template:

[code] <?php

class Wiki extends Controller {

function __construct()
{
    parent::Controller();
    $Page =& $this->load->model('Page');
    $Person =& $this->load->model('Person');
}

function index()
{
    echo "Welcome to my wiki!";
}

} ?> [/code]

Fairly straightforward so far, but note that we have created instances of our two models as global variables inside our controller. They will now be available within our functions.

The most basic functionality we need is to be able to view a page - we'll assume that the URL structure we want is [b]/wiki/view/123[/b], where "123" is the id of the page we want to see:

[code] function view($id) { $data['page'] = $this->Page->find($id); $this->load->view('single_page', $data); } [/code]

Pretty cool, right? We didn't have to write anything special in our [b]Page[/b] model to be able to use a neat [b]find()[/b] method. But suddenly we realise that our URL structure isn't that great - most wikis use the page name, not the database ID! Not a problem, we just need to make a simple change:

[code] function view($name) { $data['page'] = $this->Page->find_by_name($name); $this->load->view('single_page', $data); } [/code]

That's right - you can use [b]find_by_[i]fieldname/i[/b] to query your data using any field name you want! (Actually there are probably encoding issues, or at the very least you'd want to prevent the use of spaces or special characters in the [b]name[/b] field, but the principle is sound.)

With both of the above [b]view()[/b] functions, the View file you load would look something like this:

[code]

<?= $page->title ?>

<?= auto_typography($page->content) ?> [/code]

[h3]Retrieving and displaying Relationships[/h3]

Let's modify the above example to include the name of the author(s) of the page. Remembering what we said earlier about relationships between tables, we know that there is a table named [b]pages_people[/b] that contains the relationships between all of the pages and people in the database. But how do we write a complicated MySQL JOIN query to find the right data? With ActiveRecord, we don't need to:

[code] function view($name) { $data['page'] = $this->Page->find_by_name($name); $data['page']->fetch_related_people('person'); $this->load->view('single_page', $data); } [/code]

And now in our view file:

[code]

<?= $page->title ?>

<?= auto_typography($page->content) ?>

Authors: <?php foreach ($page->people as $person): echo $person->first_name . ' ' . $person->last_name . ', '; endforeach; ?>

[/code]

With one additional line, we have retrieved an array of [b]Person[/b] objects representing all the people linked to the current page. (NB: The [b]fetch_related_[i]objecttype/i[/b] method only needs an argument if the singular of the object cannot be divined from the plural.)

PAGE UNDER CONSTRUCTION

Clone this wiki locally