< Back

How to Make CodeIgniter Active Record More Like Rails

Warning:  The code referenced in this post is by no means complete and is most likely fatally flawed.  It's a result of a turbulent morning of caffeine-induced development.

With that said, CodeIgniter is an excellent open source MVC (Model View Controller) PHP framework.  I like it.  It does lots of cool things and is incredibly flexible. 

However, after a brief foray into Ruby on Rails development, the igniter framework does do things differently.  Chief among these differences is Active Record.  

If you haven't heard of Active Record before, Wikipedia gives a thorough explanation of it here.  In a nutshell, it speeds up development / prototyping time by providing the developer a way persist business objects without having to write any SQL.  Here's an example of how you would save an object:

$user = new User(); // new up User business object 

// set some properties 
$user->name = "Gary";
$user->email = "gary@teamamerica.com"; 

// write the new User to the data store 
$user->save(); 

Naturally, there are already mature PHP Activerecord implementations, like, for instance, PHP Activerecord.  But, I'm stubborn and had nothing better to do, so let's get started.

1. Create an "Active Record" Ready Model

Instead of extending CodeIgniter's native Model base class, we are going to build our own, which our new classes with inherit from.  Below is how our User model would be reconstructed:

class User extends ModelBase {
 // model properties -- these directly relate to the database User table fields
 public $id;
 public $name;
 public $email;
 public $password;
 
 // call the parent constructor, which sets up our Model for persistence
 function __construct() {
  parent::__construct();
 }
}

As you can see, there is really nothing special about the Model class. In fact, you could easily remove the property declarations and have them auto-generate from the database schema. Most of the heavy lifting is handled by the ancestor.

2. Build the Parent Class

For simplicity's sake, I'm providing a trimmed down version of the class below.  The full class can be downloaded from the demo link above.  Here's what you could start with:

class ModelBase {
 protected $CI; // code igniter super object
 private $table_name; // db table name 
 private $table_fields; // collection of table fields
 
 function __construct($table_name = null) {
  // get CodeIgniter global obj
  $this->initialize_datasource();
  
  // assign db table name for current BO
  $this->set_table_name($table_name);
  
  // serialize db fields into searchable string, needed for validating updates and inserts
  $this->set_table_fields();
 }
 
 public function get($id) {
  $query = $this->CI->db->query("SELECT *
   FROM $this->table_name
   WHERE id = $id");
      
  if ($query->num_rows() > 0) {
      $row = $query->row(); 
  
   $class_vars = get_object_vars($row);

   foreach ($class_vars as $name => $value) {
    // if property exists that matches row name, then set value
    $this->set_property($name, $value);
   }
  }
 }
 
 public function save() {
  // build collection of field and values based on BO props
  $class_vars = get_object_vars($this);

  foreach ($class_vars as $name => $value) {
   if($this->field_exists($name)) {
    $this->CI->db->set($name, $this->$name);
   }
  }
  
  // if id is present, update record, otherwise insert a new row
  if(empty($this->id)) {
   $this->CI->db->insert($this->table_name);
  }
  else {
   $this->CI->db->where(array('id' => $this->id));
   $this->CI->db->update($this->table_name);
  }
 }
 
 private function initialize_datasource() {
  $this->CI =& get_instance(); 
  $this->CI->load->database(); // load database class
 }
 
 private function set_property($name, $value, $obj = null) {
  $obj = empty($obj) ? $this : $obj;
  
  if (isset($obj->$name) || property_exists($obj, $name)) {
   $obj->$name = $value;
  }
 }
 
 private function set_table_name($table_name) {
  if(empty($table_name))
   $this->table_name = get_class($this);
  else
   $this->table_name = $table_name;
 }
 
 private function set_table_fields() {
  $query = $this->CI->db->query("SHOW COLUMNS FROM $this->table_name");
      
  if ($query->num_rows() > 0) {
   foreach ($query->result() as $row) {
    $this->table_fields .= $row->Field . "|";
   }
  }
 }
 
 private function field_exists($name) {
  return strpos($this->table_fields, $name) > -1 ? true : false;
 }
}

The guts of ModelBase object are the get() and save() methods (I do have a shoddily-implemented "find" method returns a collection — it's part of the downloadable demo).

The get method, when fired on a User object, will query the User table looking for a record of a particular "id". Then, it will assign each returned column's value to the corresponding User model property, only if its stubbed out in the class.

The save method should handle creating new business objects and updating those objects over time.  It makes this distinction by checking whether the object has an ID.  Obviously, this isn't much of a failsafe, so a proper implementation might verify the existence of the object in the datasource.

Crack an Odoul's, I'm Spent

If you made it this far and resisted the urge to sledgehammer your monitor, please let me know of your thoughts.  Specifically, how you would choose to implement it.  And, if you forgot to download the demo and since I'm a bit OCD about duplicating links and such, you're going to have to scroll to the top and download it there.  My sincere apologies.

Connect with us

Thank You!

We really appreciate your interest in what we do.

We'll get back to you as soon as we can.