WithRelatedBehavior
This behavior allows you to validate, insert, update and save a model along with models from its relations. It supports all relation types. All DB queries are wrapped into transactions automatically but there's a support for manual transaction handling. Composite keys are supported as well.
Installation and configuration
Copy behavior to extensions/wr directory located inside your application and add
it to the model of your choice the following way:
... public function behaviors() { return array( 'withRelated'=>array( 'class'=>'ext.wr.WithRelatedBehavior', ), ); } ...
Models that will be used in this doc examples
In the models below real DB fields are marked with @property.
Post
class Post extends CActiveRecord { /** * @property integer id * @property integer author_id * @property string title * @property string content */ ... public function relations() { return array( 'author'=>array(self::BELONGS_TO,'User','author_id'), 'comments'=>array(self::HAS_MANY,'Comment','article_id'), 'tags'=>array(self::MANY_MANY,'Tag','post_tag(post_id,tag_id)'), ); } ... }
Comment
class Comment extends CActiveRecord { /** * @property integer id * @property integer post_id * @property string content */ ... }
Tag
class Tag extends CActiveRecord { /** * @property integer id * @property string name */ ... }
User
class User extends CActiveRecord { /** * @property integer id * @property string username * @property string password * @property string email */ ... public function relations() { return array( 'profile'=>array(self::HAS_ONE,'Profile','user_id'), ); } ... }
Profile
class Profile extends CActiveRecord { /** * @property integer user_id * @property string firstname * @property string lastname */ ... }
Format of the $data parameter for all methods where it's used
This parameter accepts an associative array where values are attribute or relation names.
$post->withRelated->save(true,array( 'id','title', // model attributes 'comments','tags' // model relations ));
The name of the relation can be specified as a key. In this case its value is another
$data array with the same rules. So you there's no limit in how many times you can
nest these.
$post->withRelated->save(true,array( 'comments'=>array( 'id','content', // comments relation attributes 'author', // author relation inside comments relation models ), ));
Note: If you'll not specify any attributes, all attributes will be saved. For relations it's the opposite: you should specify relations explicitly in order for these to be saved.
Usage
Relation types
HAS_ONE
$user=new User; $user->username='creocoder'; $user->email='creocoder@gmail.com'; $user->profile=new Profile; $user->profile->firstname='Alexander'; $user->profile->lastname='Kochetov'; $user->withRelated->save(true,array('profile'));
HAS_MANY
$post=new Post; $post->title='Relational saving is not a dream anymore.'; $post->content='Since WithRelatedBehavior released...'; $comment1=new Comment; $comment1->content='Was it hard?'; $comment2=new Comment; $comment2->content='Yes, but we made it.'; $post->comments=array($comment1,$comment2); $post->withRelated->save(true,array('comments'));
MANY_MANY
$post=new Post; $post->title='Relational saving is not a dream anymore.'; $post->content='Since WithRelatedBehavior released...'; $tag1=new Tag; $tag1->name='relation'; $tag2=new Tag; $tag2->name='save'; $post->tags=array($tag1,$tag2); $post->withRelated->save(true,array('tags'));
BELONGS_TO
$post=new Post; $post->title='Relational saving is not a dream anymore.'; $post->content='Since WithRelatedBehavior released...'; $post->author=new User; $post->author->username='creocoder'; $post->author->email='creocoder@gmail.com'; $post->withRelated->save(true,array('author'));
Note: As you can see, API stays the same no matter which relation type is used.
Also it worth mentioning that a transaction is started before saving if DB supports it.
If transaction was already started manually, behavior detects it and doesn't
start its own transaction. By default, same as CActiveRecord::save() does,
WithRelatedBehavior::save() validates data and starts saving it only if all
models it's going to save are valid. You can disable validation by passing false
to $runValidation parameter.
Advanced usage
Using custom update strategies
TBD.
An advanced usage example
$post=new Post; $post->title='Post title'; $post->content='Post content'; $user=new User; $user->username='someuser'; $user->email='someuser@example.com'; $user->profile=new Profile; $user->profile->firstname='Vasya'; $user->profile->lastname='Pupkin'; $post->author=$user; $comment1=new Comment; $comment1->author=$user; $comment1->content='Some content 1'; $comment2=new Comment; $comment2->author=$user; $comment2->content='Some content 2'; $post->comments=array($comment1,$comment2); $tag1=new Tag; $tag1->name='tag1'; $tag2=new Tag; $tag2->name='tag2'; $post->tags=array($tag1,$tag2); $post->withRelated->save(true,array( 'author'=>array( 'profile', ), 'comments'=>array( 'author', ), 'tags', ));
In order to save post and related models an extension builds saving plan first.
In the example above that before saving we need to save user model and its related
profile. After doing it we'll be able to save post. Then goes comments
(author is the same user). Last tags is saved. Actions mentioned are executed
inside a transaction. Extension takes care about all these.