Yii 1.1: Extending an ActiveRecord model

8 followers

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.

Total 3 comments

#12140 report it
schmunk at 2013/02/28 02:09pm
Behaviors?

Can't this be done with behaviors?

#11991 report it
Bogsey at 2013/02/19 11:24am
Good idea

Thanks Ryadnov, didn't see that article. I've changed this to reflect it.

#11980 report it
Ryadnov at 2013/02/18 06:40am
good article, but ...

see this article http://www.yiiframework.com/wiki/198/single-table-inheritance for move your code from findByPk to instantiate function

And use beforeSave function instead save

Leave a comment

Please to leave your comment.

Write new article