The CAdvancedArBehavior extension adds up some functionality to the default possibilites of yii´s ActiveRecord implementation. At the moment it is able to automatically save MANY_MANY relation objects when save()-ing an Object.
Version 0.3 added 25. 05. 2011 by thyseus
To use this extension, just copy this file to your extensions/ directory, add 'import' => 'application.extensions.CAdvancedArBehavior', [...] to your config/main.php and add this behavior to each model you would like to inherit the new possibilities.
public function behaviors(){ return array( 'CAdvancedArBehavior' => array( 'class' => 'application.extensions.CAdvancedArBehavior')); }
When we have defined a MANY_MANY relation in our relations() function, we are now able to add up instances of the foreign Model on the fly while saving our Model to the Database. Let´s assume the following Relation:
Post has: 'categories'=>array(self::MANY_MANY, 'Category', 'tbl_post_category(post_id, category_id)')
Category has: 'posts'=>array(self::MANY_MANY, 'Post', 'tbl_post_category(category_id, post_id)')
Now we can use the attribute 'categories' of our Post model to add up new rows to our MANY_MANY connection Table:
$post = new Post(); $post->categories = Category::model()->findAll(); $post->save();
This will save our new Post in the table Post, and in addition to this it updates our N:M-Table with every Category available in the Database.
We can further limit the Objects given to the attribute, and can also go the other Way around:
$category = new Category(); $category->posts = array(5, 6, 7, 10); $category->save();
We can pass Object instances like in the first example, or a list of integers that representates the Primary key of the Foreign Table, so that the Posts with the id 5, 6, 7 and 10 get´s added up to our new Category.
5 Queries will be performed here, one for the Category-Model and four for the N:M-Table tbl_post_category. Note that this behavior could be tuned further in the future, so only one query get´s executed for the MANY_MANY Table.
We can also pass a single object or an single integer:
$category = new Category(); $category->posts = Post::model()->findByPk(12); $category->posts = 12; $category->save();
Version 0.2 Code Cleanup, Bugfixes and added save() support
Total 18 comments
Thanks for the Behavior.. I've been using it for a couple of projects already, and will keep doing so in the future.
One thing to note: If using MSSQL you have to delete the "ignore" keyword from 193 so it looks like this:
rho
I fell in trouble, when I had to save several additional fields in connection table. Information scheme is the next:
A -- C(a_id, b_id , field_1, field_2) -- B .
So problem: every time, when I save model A, all connections are reset, and all info in additional fields become lost. Even when any connection was modified.
In code I found that, on every saving of model A, all connections are removing from DB, and new created, according to POST data.
I made several changes in the code. Now connections are modified only in case, when new are added or existing removed (queries are optimized to affect only on modified records). So any additional infromation will not be lost.
Here is the code:
You should change the classname to CAdvancedArBehavior instead of CAdvancedArbehavior for the yii convention
also you can just save the file in components and in you models add this:
public function behaviors(){ return array( 'CSaveRelationsBehavior' => array( 'class' => 'application.components.CAdvancedArBehavior' ) ); }no need to load it in the main config
Nice extension @thyseus, really helpful. Thanks also to @tetele for the tips, you should consider to update it. Cheers, Pablo.
I have 2 small bug reports:
a typo: line 142 in v0.3 should read
primaryKeyinstead ofPrimaryKey(notice the capitalization)line 165 in v0.3 should look like this:
The reason is that not always the field in the m2m table is the same as the field in the object table. For instance, I name all of my PKs simply
id. So I have$post->idand$category->id, but the m2m table haspost_idandcomment_idfieldsIt currently fails when I have the following table setup:
Item1PrimaryKey:idItem2PrimaryKey:idItem1_Item2Foreign Keys:item1_id-item2_idThis is the trouble spot in
writeRelation():It works when I change it to this:
This should let you have the foreign key column names in the relation table be different from the primary key in the main tables.
In version 0.4 line 180
if(isset($this->owner->$key)) foreach($this->owner->$key as $foreignobject)
the foreach fails if the key is set and it is empty string.
The documentation of http://www.yiiframework.com/doc/api/1.1/CHtml#activeCheckBoxList-detail says In case no selection is made, the corresponding POST value is an empty string.
Please fix by adding is_array check too. Thank you. And please post the updated 0.4 version here too.
to fix it now refer to http://code.google.com/p/yii/issues/detail?id=2412
same issue as waterloomatt. I have just added "if(!empty($foreignobject))" on line 166 of CAdvencedArBehavior and it works like a charm.
original code
new code
I have a Mortgage application where A Mortgage can have many applicants and a applicant can have many mortgages. I also want to know who is the primary applicant of the mortgage.
So the middle tbl needs to have mortgage_id , person_id, primary_applicant (a flag).
How do I use this extension to save the primary_applicant flag ?
Found a clean-ish solution to the "checkBoxList forgetting posted values" problem. See this post for details.
a solution would be to use the Relation Widget, that is tested to work good with CAdvancedArBehavior:
extension: http://www.yiiframework.com/extension/relation/
svn: http://code.google.com/p/yii-user-management/source/browse/trunk/user/components/Relation.php
Hi,
Nice extension. The problem: The checkBoxList is empty after validation fails on the parent model. The question: Has anyone found a solution to re-populating the checkBoxList after validation fails?
My view:
I did the following but am not able to save() many-many relationships.
The last statement throws an error saying 'Undefined offset: 0'. The relations are defined as:
Watchlist model:
Stock model:
Great extension, but can you tell me if it updates unchanged records. I imagine a that a loop to check this before going to the DB would be a great saving. I had a look through the code and could not see such a loop but there is a very good chance I have missed it. Top marks though.
Seems to work great, only problem I have is that it doesn't seem to work with $model->attributes assignment.
The relation I have is $model->extensions.
If I do this (which is the default) when updating:
$model->extensions isn't updated, even though $_POST['Foo']['extensions'] exists and is an array. I ended up doing this:
if(isset($_POST['Foo']['extensions'])){ $model->extensions = $_POST['Foo']['extensions']; unset($_POST['Foo']['extensions']); } else $model->extensions = Array();Also had to update lines 109 and 114 per rafa.informatica's review, in order to get it to remove all if the array is empty
very good job, I change the lines 109 and 114 ... to delete the relationships when the user deselects all checkbox
109 - else //if (is_array($this->owner->$key)&& $this->owner->$key != array())
114 - foreach((array)$this->owner->$key as $foreignobject)
the php file is corrupt.. all i see is garbage characters.. plz save in UTF 8 :P
Leave a comment
Please login to leave your comment.