This extension is inspired by all the yii extensions that aim to improve saving of related records. It allows you to assign related records especially for MANY_MANY relations more easily. It puts together the awesomeness of all the extensions mentionend below (see headline "Feature comparison"). It comes with 100% test coverage and well structured and clean code so it can savely be used in enterprise production enviroment.
extensions/yiiext/behaviors/activerecord-relation/
under your application root directory.git submodule add https://github.com/yiiext/activerecord-relation-behavior.git extensions/yiiext/behaviors/activerecord-relation
behaviors()
method.<?php
public function behaviors()
{
return array(
'activerecord-relation'=>array(
'class'=>'ext.yiiext.behaviors.activerecord-relation.EActiveRecordRelationBehavior',
),
);
}
We have two ActiveRecord classes (the ones from Yii definitive guide):
<?php
class Post extends CActiveRecord
{
// ...
public function relations()
{
return array(
'author' => array(self::BELONGS_TO, 'User', 'author_id'),
'categories' => array(self::MANY_MANY, 'Category', 'tbl_post_category(post_id, category_id)'),
);
}
}
class User extends CActiveRecord
{
// ...
public function relations()
{
return array(
'posts' => array(self::HAS_MANY, 'Post', 'author_id'),
'profile' => array(self::HAS_ONE, 'Profile', 'owner_id'),
);
}
}
Somewhere in our application code we can do:
<?php
$user = new User();
$user->posts = array(1,2,3);
$user->save();
// user is now author of posts 1,2,3
// this is equivalent to the last example:
$user = new User();
$user->posts = Post::model()->findAllByPk(array(1,2,3));
$user->save();
// user is now author of posts 1,2,3
$user->posts = array_merge($user->posts, array(4));
$user->save();
// user is now also author of post 4
$user->posts = array();
$user->save();
// user is not related to any post anymore
$post = Post::model()->findByPk(2);
$post->author = User::model()->findByPk(1);
$post->categories = array(2, Category::model()->findByPk(5));
$post->save();
// post 2 has now author 1 and belongs to categories 2 and 5
// adding a profile to a user:
$user->profile = new Profile();
$user->profile->save(); // need this to ensure profile got a primary key
$user->save();
Enable/Disable/Reset the relations to save. To do this you can use:
$model->withoutRelations('relation name 1', 'relation name 2',…)->save()
…will save all $model
relations except the ones passed to the scope.
$model->withRelations('relation name 1', 'relation name 2',…)->save()
…will save only the $model
relations passed to the scope
$model->resetRelations()->save()
…will clear the former scopes, and save all the relations of $model
$model->author_id
it will have no effect since ARRelationBehavior will overwrite it
with null if there is no related record or set it to related records primary key.
Instead simply assign the value to the relation itself: $model->author = 1;
/ $model->author = null;
$model->reload()
to force reloading of related records. Or load related records with forcing reload:
$model->getRelated('relationName',true)
.$post->author = $user;
,
$user->posts
will not be updated automatically (might add this as a feature later).You have assigned a record to a relation which has not been saved (it is not in the database yet).
Since ActiveRecord Relation Behavior needs its primary key to save it to a relation table, this will not work.
You have to call ->save()
on all new records before saving the related record.
You can only assing arrays to HAS_MANY and MANY_MANY relations, assigning a single record to a ..._MANY relation is not possible.
You tried to assign primary key value X to a relation, but X does not exist in your database.
Inspired by and put together the awesomeness of the following yii extensions:
these are the extensions mentioned above
reviewed but did not take something out:
Many thanks to the authors of these extensions for inpiration and ideas.
This behavior is covered by unit tests with 100% code coverage (ECompositeDbCriteria is currently not covered since composite pks are not fully supported yet). To run the unit tests you need phpunit installed and the test class requires php 5.3 or above.
git clone https://github.com/yiisoft/yii.git yii
ln -s ../../path/to/yii yii
phpunit EActiveRecordRelationBehaviorTest.php
or if you want coverage information in html,
run phpunit --coverage-html tmp/coverage EActiveRecordRelationBehaviorTest.php
It uses CActiveRecord::hasRelated()
to check if a relation has been
loaded or set and will only save if this is the case.
It will re-save if you loaded and did not change, since it is not able
to detect this.
But re-saving does not mean entries in MANY_MANY table get deleted and
re-inserted. It will only run a delete query, that does not match any rows if you
did not touch records, so no row in db will be touched.
Currently not, will add this feature in the future: issue #16.
Currently you have to load all and re-assign the array. Will add an api for this; issue #16.
Comments