Extending an ActiveRecord model

Introduction

Let's say that we have an ActiveRecord model that we want to refine with extra data, we could go and add columns to the database and include them in the ActiveRecord model.

Example

We have an ActiveRecord class called 'vehicle':

class vehicle extends CActiveRecord {
  public $manufacturer;
  public $manufacture_date;
  public $price;
...
// rest of normal active record stuff
...
}

Now lets say we want some sub classes of vehicle,

class car extends vehicle{
  public $number_of_wheels;
  public $number_of_doors;
  public $hasSunroof;
}
class boat extends vehicle{
  public $number_of_hulls;
  public $hasSail;
}
class plane extends vehicle{
  public $maximum_altitude;
  public $number_of_seats;
}

Adding all of the these extra columns for all the subclasses to the database will cause the table to become very big very quickly, and only a handful of the columns on each row would be populated. As long as we're not too worried about sorting and searching the extra data we can include it as a single extra text column in the original vehicle table - we'll call it 'params'. Now we can extend the original vehicle class as follows:

class vehicleExtended extends vehicle{
  /**
   * @var string The name of the class
   */
  public $name;

  protected $_viewFile

  /**
   * We can have different view files for the different classes
   * @return string The view file location
   */
  public function getViewFile() {
      return $this->_viewFile;
  }
  /**
   * Returns the static model of the specified AR class.
   * @param string $className active record class name.
   * @return VisitServiceItem the static model class
   */
   public static function model($className = __CLASS__) {
      return parent::model($className);
   }
  /**
    * Saves meta data in the params attribute
    * @return boolean
    */
  public function beforeSave() {
     $this->name = get_class($this);
     $vars = get_object_vars($this);
     $this->params = json_encode($vars);
     return parent::save($runValidation, $attributes);
  }
  /**
    * Creates the correct class type and populates the meta data
    * @param array $attributes
    * @return CActiveRecord
    */
   public function instantiate($attributes) {
       $vars = json_decode($attributes['params'], true);
       $type = $vars['name'];
       $subClass = new $type;
       foreach ($vars as $key => $var) {
           if (property_exists($subClass, $key)) {
               $subClass->$key = $var;
           }
       }
       return $subClass;
   }
}

Now all we need to do is change the subclass definitions to extend from this new class

class car extends vehicleExtended{
  public $number_of_wheels;
  public $number_of_doors;
  public $hasSunroof;
  protected $_viewFile = 'application.views.vehicles.car';
}
class boat extends vehicleExtended{
  public $number_of_hulls;
  public $hasSail;
  protected $_viewFile = 'application.views.vehicles.boat';
}
class plane extends vehicleExtended{
  public $maximum_altitude;
  public $number_of_seats;
  protected $_viewFile = 'application.views.vehicles.plane';
}

As I said, this works well when the extra data just needs to be saved and not indexed. Any comments welcome.

2 3
7 followers
Viewed: 29 269 times
Version: 1.1
Category: How-tos
Written by: Bogsey
Last updated by: Bogsey
Created on: Feb 18, 2013
Last updated: 11 years ago
Update Article

Revisions

View all history

Related Articles