Yii 1.1: save-relations-ar-behavior

CSaveRelationsBehavior: Save HasMany and ManyMany relational active records along with the main model
22 followers

The CSaveRelationsBehavior enables ActiveRecord model to save HasMany and ManyMany relational active records along with the main model

Version 1.0.3 update:

  • Correct the way in which the related records are found for HAS_MANY relations in the setRelationRecords method
  • Updated demonstration application

Demonstration applications are available in the download section.

Resources

Any bug report, feedback or enhancements requests will be highly appreciated.

Documentation

Requirements

  • Yii Framework 1.0.4 or later

Installation

  • Extract the release file under protected/components

Usage

  • Add the following code to the models you wish to add this behavior to:
public function behaviors(){
        return array('CSaveRelationsBehavior' => array('class' => 'application.components.CSaveRelationsBehavior'));
}

In your controler, to save the relations data, simply call:

$model->setRelationRecords('relationName',$data);
$model->save();
  • For ManyMany relations, $data is either an array of foreign key values (ie. array(2,5,43)) or an array of associative arrays giving the composite foreign keys values of the related model (ie. array(array('pk1'=>2,'pk2'=>'fr'),array('pk1'=>5,'pk2'=>'en'))). You will typically get this data from some checkboxes values listing the ids of the related model.
  • For HasMany relations, $data should be set as an array of associative arrays giving the attributes values of the related model (ie. array(array('id'=>123, 'name'=>'someValue', 'visible'=>true), array('id'=>456, 'name'=>'someOtherValue', 'visible'=>false))); You can get this data by using the tabular input technique within the form of the main model

In both cases, the foreign keys related to the main model will automatically be populated with its primary key(s) value(s). Most of the time, you will call the setRelationRecords that way:

$model->setRelationRecords('relationName',is_array(@$_POST['ModelName']) ? $_POST['ModelName'] : array());

By default, the behavior will handle the save operation in a transactional way so that if anything goes wrong during the save of some related data, your relational integrity will not be affected. If you prefer to handle this yourself, you can set the 'transactional' property of the behavior to false. Also, if any error occurs during the save process, the 'hasError' property will be set to true.

Delete of related records is automaticaly managed. If you dont want the behavior to take care of it, just set the deleteRelatedRecords property to false:

public function behaviors(){
        return array('CSaveRelationsBehavior' => array('class' => 'application.components.CSaveRelationsBehavior', 'deleteRelatedRecords'=>false));
}

Additional features:

  • $model->addSaveRelation('relationName'[,'customErrorMessage']) You can use this method to force the save of some relation. You can also set the error message of the relation by using the second parameter (see setSaveRelationMessage below)
  • $model->removeSaveRelation('relationName') Simply do the oposite
  • $model->setSaveRelationMessage('relationName','customErrorMessage') Set the message to be shown in the error summary of the main model
  • $model->setRelationScenario('relationName','scenario') Set the validation scenario to be applied to each related record

Demonstration

You can try this behavior by using the provided demo application in the download section. Unzip the content to your web server and take a look to the README file to know how to setup this application.

Bugs, feedback, enhancements:

Any bug report, feedback or enhancements requests will be highly appreciated.

Change Log

June 19, 2010

  • v1.0.3 release
    • Correct the way in which the related records are found for HAS_MANY relations in the setRelationRecords method

June 12, 2010

  • v1.0.2 release
    • Enh: Added validation scenario support for HAS_MANY relations
    • Enh: Relation initialisation optimisation code
    • Enh: Added delete related records support

May 16, 2010

  • v1.0.1 release
    • Bug #1: 'save' attribut of the relations property may not be set correctly
    • Enh: Better validation handling; it is now performed in the beforeValidation event

May 15, 2010

  • Demonstration applications for Yii Framework 1.0 and 1.1

May 13, 2010

  • v1.0 Initial release.

Total 6 comments

#5062 report it
horizons at 2011/09/12 03:56am
Problem with realtions with condition

Hi i have a problem with this extension I have 2 realtions to the same table with different conditions on the relations. Saving isn't a problem the related values would be saved correctly (if i set the other columns). But sadly the "other" entries will get deleted every time. Because the conditions wouldn't be used when $relatedRecord->deleteAll($criteria); is called.

It would be also nice if the fields (which are condition in the relation) would be set by default on adding new entries to the relation.

edit: if you add this before the delete call in afterSave should fix this issue.

if($activeRelation->condition)
{
  $activeRelation->mergeWith($criteria);
  $criteria = new CDbCriteria(array(
      'condition'=>$activeRelation->condition,
      'params'=>$activeRelation->params,
      'having'=>$activeRelation->having,
      'join'=>$activeRelation->join,
      'with'=>$activeRelation->with,
  ));
}
#2702 report it
juban_ at 2011/01/31 02:30am
Re: Little correction

"If deleteRelatedRecords if false, the transaction should still be committed or nothing happens."

Squatrem, you are absolutely right :) Thanks for the feedback. I will quickly put the fix in the repository.

Regards.

Alban.

#2440 report it
Squatrem at 2011/01/02 04:02pm
Little correction

If deleteRelatedRecords if false, the transaction should still be committed or nothing happens.

public function afterDelete($event) {
    if($this->deleteRelatedRecords) {
        ... 
    } else
        if($this->transactional && $this->transaction) $this->transaction->commit();
}

Thanks for a great time saving extension !

#2333 report it
Albert at 2010/12/14 04:38am
MySQL only

This extension works only in MySQL.

there is no "INSERT IGNORE" in PostgreSQL.

It is a very bad idea to cap all exceptions and just write "An error occured during the save of ..."

#501 report it
juban_ at 2010/05/13 10:24am
Thank you

Hi thyseus, and thank you for your nice feedback.

You are absolutely right. CSaveRelationsBehavior shares some functional parts of the CAdvancedArBehavior, but the implementation is quite different I think. Specially in the way that the behavior retrieve the foreign keys informations (base on the core Yii implementation for some parts). Also, using CAdvancedArBehavior, I missed a simple way to save HasMany relations in a tabular way.

About the onDelete event, I think it could be useful for cases where the foreign keys constraints are not set or the DBM doesn't support it (MyISAM for instance). It could be some sort of garbage collector cleaning up after the main model record have been deleted by deleting every records sharing the same foreign keys values.

Anyway, thank you for you support.

#502 report it
thyseus at 2010/05/13 10:03am
Nice one!

Looks like a brother of the CAdvancedArBehavior that does the same. But i like the feature to set up a error message in the Controller if something went wrong.

I saw the afterDelete() is still todo. This will be difficult to implement cause you have to define what should happen when the parent objects get deleted somewhere. InnoDB supports this (ON DELETE ...), but i think this should be set in the Model somewhere.

This whole functionality definitely should be implemented in the Core Active Record Model, combining the best of CAdvancedArBehavior and save-relations-ar-behavior! :)

keep up the good work

Leave a comment

Please to leave your comment.

Create extension