esaverelatedbehavior ESaveRelatedBehavior enables ActiveRecord models to save HAS_MANY relational active records and MANY_MANY relations.

  1. Requirements
  2. Usage
  3. MANY_MANY relations
  4. HAS_MANY relations
  5. Advanced usage
  6. Update

The ESaveRelatedBehavior enables ActiveRecord models to save HAS_MANY relational active records and MANY_MANY relations along with the main model.

It provides two new methods:

saveWithRelated()
Saves the model and all specified related models
returns false if any model has not been saved

saveRelated()
Saves the specified related models only and
returns false if any model has not been saved

Features:

  • handles many_many and has_many relations
  • fills has_many relations with validated objects
  • allows selection of scenario for has_many relations
  • processes everything within a transaction
  • relations can be set with data arrays or object arrays
  • only specified relations are saved
  • massive assignment works, since data is set on relation directly
  • adds 2 methods to activeRecord, no use of beforeSave/afterSave,
    therefore the behavior can be added to all activeRecord classes,
    it will only do its work when the new methods are called
  • uses standard SQL
  • NO HANDLING OF COMPOSITE KEYS yet

Requirements

Yii Framework 1.1 or above PHP version 5.3 or above

Usage

Extract the release file under 'protected/components'
Add the following code to your models:

public function behaviors(){
		return array('ESaveRelatedBehavior' => array(
         'class' => 'application.components.ESaveRelatedBehavior')
     );
}

MANY_MANY relations

When a MANY_MANY relation is defined in the relations() function,
instances of the foreign model can be related while saving the model.

Examples:

post model defines many_many relation with categories:

'categories'=>array(
    self::MANY_MANY, 'category', 'post_category(post_id, category_id)'
)

To add new rows to the m:n table (post_category) do:

$post = new post();
$post->categories = category::model()->findAll();
$post->saveWithRelated('categories');

This saves the new post and updates the m:n table with every category.

The typical usage to assign data send from a checkBoxList would be:

$post->categories = array(1, 2, 3);
$post->saveWithRelated('categories');

So you can either pass an array of objects or an array of the primary keys of the foreign table.

You can also pass a single object or a single primary key:

$post->categories = category::model()->findByPk(1);
$post->categories = 1;
$post->saveWithRelated('categories');

If you are saving several relations you would do:

$post->saveWithRelated( array('relation1', 'relation2') );

If saving was not successful, then $post->categories will return the data that was set.
If saving was successful, then the relation will return the related models.

When updating a model then all entries in the m:n table for this model are first deleted.
(Howto prevent deletion see section "advanced usage")

HAS_MANY relations

When a HAS_MANY relation is defined in the relations() function, instances of the foreign model can be added while saving the model.

Examples:

author model defines has_many relation with posts:

'posts'=>array(
    self::HAS_MANY, 'post', 'author_post(author_id, post_id)'
)

To save a new author along with 2 posts you can now do:

$author = new author();
$author->posts = array(new post(), new post());
$author->saveWithRelated('posts');

This saves the new author together with two (empty) posts.

The typical usage though would be to assign data send from a tabular form. Each array entry represents the data for one post:

$author->posts = array(
   array(
       'title' => 'My first post',
       'content' => 'This is my first post.'
   ),
   array(
       'title' => 'My second post',
       'content' => 'This is my second post.'
   )
);
$author->saveWithRelated('posts');

So similar to many_many relations you can either pass an array of objects or an array of data that represents the related objects.

You can also pass a single object:

$author->posts = new post();
$author->saveWithRelated('posts');

If you are saving several relations you would do:

$author->saveWithRelated( array('relation1', 'relation2') );

If saving was not successful, then $author->posts will return an array of validated post models which can be used to display the tabular input form along with validation messages.
If saving was successful, then the relation will return the related models.

When updating a model then all records related to this model are first deleted.
(You can prevent deletion: see section "advanced usage")

You can also specify the scenario to be used for the insertion of the related models:

$model->saveWithRelated( array('relationName1' => array('scenario' => 'special')));

You can specify the scenario to be used for the insertion of the last related model:

$model->saveWithRelated( array('relationName1' => array('lastScenario' => 'special')));

This can be useful if you have to validate aggregated values.
Say you have a field A in the model and field B in the related models.
Now you would like to check if the sum of values in field B equals the value in field A.
You can do this with an SQL statement in a validator which is active for the last scenario.

Advanced usage

To save the related data only do:

$model->saveRelated( array('relationName1','relationName2') );

If you do not want to delete existing related records before insertion do

$model->saveWithRelated( array('relationName1' => array('append' => true)));

You will have to make sure that the new records are not dublicates of existing records, the behavior does not handle that.

Update

v1.6
Fixed bug that occurred when saving MANY_MANY relations when parent object had errors

v1.5
Added parameter 'lastScenario'

v1.4
Small code change to make it compatible to PHP versions < 5.3.
This has not been checked on all versions though, so I left requirements at PHP >= 5.3

v1.3
Corrected error: When using saveWithRelated for new models they can contain validation errors and hence will not be saved, so their id is not known. Therefore no models can be related yet. MANY_MANY-relations will now not be saved in this case. For HAS_MANY relations the models will only be validated (no saving attempt,because the foreign key would be missing).

v1.2
Corrected error: when checking for a model class for a mn-table the autoloader caused an exception when the class file did not exist

v1.1

  • added support for scenario selection when adding has_many relations
  • changed the way options are specified when saving relations
    (now an array is used so that 'append' and 'scenario' can both be specified

v1.0

  • first version