Sunday, April 15, 2012

Table Inheritance with Yii ActiveRecord

Need to use Table Inheritance with ActiveRecord? Here's how:

This is Single Table Inheritance only.

Imagine that you want People contacts and Organization contacts. You don't want to keep them in different tables, as sometimes you sell to people, and sometimes you sell to organizations, and you want to keep a single (foreign) key to the party that you sold to.

We will create a class called Party, with subtypes Person and Organization. They will share some of Party's rules and relationships.

Here is the database schema:

CREATE TABLE `tbl_party` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `type` char(1) NOT NULL comment 'p=person,o=organization',
 `name` varchar(255) NOT NULL comment 'surname if a person',
 `given_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ;

and here is the model code:

class Party extends CActiveRecord {

    public static function model($className=__CLASS__){
 return parent::model($className);
    }

    public function tableName(){
 return 'tbl_party';
    }

    

    public function relations(){
 return array(
     
 );
    }

    public function rules(){
 return array(
     array('name,type','required'),
     array('type','in', 'range'=>array('o','p'))
 );
    }
}

class Person extends Party {

    /* this is required for subtypes */
    public static function model($className=__CLASS__){
 return parent::model($className);
    }

    public function defaultScope(){
        /* only read Parties that are People */
 return array(
     'condition'=>"type='p'"
 );
    }

    public function relations(){
        /* combine parent and own relations. can have 'People' only relations  */

 $parentRelations = parent::relations();

 $myRelations = array(

 );

 return array_merge($myRelations, $parentRelations);
    }

    public function rules(){
        /* combine parent and own rules */

 $parentRules = parent::rules();

 $myRules = array(
     array('type', 'default', 'value'=>'p'), /* set type to Person */
     array('type', 'in', 'range'=>array('p')), /* allow only Person type */
     array('given_name', 'required') /* new rule for this subtype only */
 );

 /* you want to apply parent rules last, delete them here if necessary */
 return array_merge($myRules, $parentRules);
    }

}

class Organization extends Party {

    public static function model($className=__CLASS__){
 return parent::model($className);
    }

    public function defaultScope(){
 return array(
     'condition'=>"type='o'"
 );
    }

    public function relations(){
 $parentRelations = parent::relations();

 $myRelations = array(
     
 );

 return array_merge($myRelations, $parentRelations);
    }

    public function rules(){
 $parentRules = parent::rules();

 $myRules = array(
     array('type', 'default', 'value'=>'o'),
     array('type', 'in', 'range'=>array('o'))
 );

 /* you want to apply parent rules last. delete them if necessary */
 return array_merge($myRules, $parentRules);
    }
}

5 comments:

  1. Thanks, I was looking for this and didn't even realise it ;)

    ReplyDelete
  2. Thank you, this is a good idea to me.

    ReplyDelete
  3. That is fuckin' ugly, unreadable workaround idea.

    INHERITANCE is one of the most important aspects of OOP. To use inheritance in relational databases, one should use FOREIGN KEYS.

    If Yii can't use that mechanism it is made the WRONG way. And I am so disappointed to see that kind of ideas, which makes me think to switch to another framework.

    ReplyDelete