Multi-parent child in Yii?

In enterprise software like Vantive and Peoplesoft, there were always common entities like Address and Notes which could be applied to many parent objects while being a common object. In some places this was called "Multi-Parent Child". Here is an example database schema:


CREATE TABLE tbl_notes (

id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,

parentId INTEGER NOT NULL,

parentType VARCHAR(20),

subject VARCHAR(100),

description TEXT

)


CREATE TABLE tbl_company (

id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,

...

)


CREATE TABLE tbl_person (

id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,

...

)

With some data in tbl_notes like this:


INSERT INTO `tbl_notes` (`id`,`parentId`,`parentType`,`subject`,`description`)

VALUES

	(1, 1, 'company', 'ready to buy', 'Lets be aggressive with pricing'),

	(2, 1, 'person', 'important contact', 'He is not busy at lunch, call 15m before noon when scheduling'),

	(3, 2, 'company', 'fishing', 'I suspect this company is not really interested in our product');



It’s not clear to me that this is easy to model using Yii’s Relational ActiveRecord model. I am a bit inflexible on a paradigm shift because this data is already encoded, if I were to change the data structure, we would not be able to phase in new modules in Yii, but would need to rollout with a big cut over (not happening) or sync data (costly, error-prone) between the current framework and Yii.

It’s possible via using ON with entity name in condition for HAS_MANY relation.

The way I solved that issue with a multi-father/children category scenario is to create scopes and functions that ‘mimic’ the lazy-loading approach, in order to follow KIS





// to get the children...

protected $_children;


public function getChildren(){

	if(null === $this->_children){

		$this->_children = Category::model()->findAll('parentId='.$this->id);

	}

	return $this->_children;

}

// to get father

public function getFather(){

	

	if($this->parentId!=0)  // isnt root?

	{


		$model = Category::model()->findByPk($this->parentId);

		return $model;

	}

	return null;

}

// to play

// scope to get root categories

public function whereRoot(){

	$this->getDbCriteria()->addCondition('parentId=0');

		

	return $this;

}

protected function loopChildren($models, $side='', $id='-1'){

	$items = array();

	foreach($models as $model){

		if($id == $model->id) continue;

		$items[] = array('id'=> $model->id,'name'=> $side.$model->name);

		if($model->children){

			$items= CMap::mergeArray($items,$this->loopChildren($model->children,$side.'--', $id));

		}

	}

	return $items;

}

// building an array to be used with a dropdown list

public function getTreeListArray($id='-1'){

	$models = self::model()->whereRoot()->findAll();

		

	$items = array();

	if($models){

		$items = $this->loopChildren($models, '', $id);

	}

		

	return $items;

}



Maybe this approach helps you to get some ideas…

BTW, welcome to the forum

Thanks, I’m very impressed with Yii.

Can you please elaborate?

Would I just code the relations method with a HAS_MANY for tbl_company, tbl_person, etc (and respectively BELONGS_TO for tbl_notes) and then use a relational query operator "ON" like this:


$notes = Company::model()->with('Notes')->findAll(array('on'=>'parentType="company"');



Or is it possible to bake this directly into the relations method? ie, would this work?:


//relations for Company

public function relations() {

 return array(

	'notes'=>array(self::HAS_MANY,'Notes',parentId,'on'=>'parentType="company"'),

	...

}

Wow, Antonio, this is way too much work… for the size of the application I’m building, I’d rather just use DAO for these kinds of tables if this is the alternative :slight_smile:

You can actually create a behavior or extend from CActiveRecord to those models where you require that… Nevertheless, too much work? Is only two functions and two examples of use. Plus, if you are using DAO and your app is huge, is a better option anyway. :)

No, it’s not.

You can use Antonios excellent code in your future projects - maybe turn it into a behavior (like he said).

I am sure that you’ll end up doing even more work ‘just using DAO’ :)

By the way:

Is this question Yii 1.0 ?

If it is, you really should upgrade to Yii 1.1 - if it isn’t, this topic doesn’t belong in the 1.0 forum.

I tend to skip this forum altogether for that reason…