Extending an ActiveRecord model

You are viewing revision #3 of this wiki article.
This version may not be up to date with the latest version.
You may want to view the differences to the latest version.

next (#4) »

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);
   }
  /**
   * Overrides the ActiveRecord save to insert the extra parameters
   * This class has into JSON text and save it in the data base
   */
  public function save($runValidation = true, $attributes = null) {
     $this->name = get_class($this);
     $vars = get_object_vars($this);
     $this->params = json_encode($vars);
     return parent::save($runValidation, $attributes);
  }
  /**
   * Overrides the ActiveRecord function to read the extra params and
   * create a new class based on the data and return the sub class
   */
  public function findByPk($pk, $condition = '', $params = array()) {
     $result = parent::findByPk($pk, $condition, $params);
     if ($result !== null) {
         $extendedVars = json_decode($result->params, true);
         $type = $extendedVars['name'];
         $subClass = new $type;
         foreach ($extendedVars as $key => $var) {
             if (property_exists($subClass, $key)) {
                 $subClass->$key = $var;
             }
         }
         $subClass->attributes = $result->attributes;
         return $subClass;
     }
     return false;
   }
}

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: 19 304 times
Version: Unknown (update)
Category: How-tos
Written by: Bogsey
Last updated by: Bogsey
Created on: Feb 18, 2013
Last updated: 5 years ago
Update Article

Revisions

View all history

Related Articles