Yii 1.1: xrelationbehavior

Enables automatic saving and deletion of related model records
4 followers

This is another take at handling related database records using their configured relations().

First of all I apologize to all users who downloaded until June, 17 2011. I fixed the missing methods by running the extension on a fresh installation without the custom functions and methods which caused PHP errors. It should work now.

Introduction

This behavior attaches event handlers for beforeSave(), beforeDelete(), etc.

It will save and delete related records automatically upon model creation or deletion. It auto-sets foreign keys on related HAS_MANY records.

The extension is an alpha, but it does work with HAS_MANY and MANY_MANY relations.

At this point the extension does NOT work with composite primary/foreign keys.

Changelog

r5

  • now attaching records also makes them available through standard relation attribute (e.g. $model->tags)
  • better validation on MM_attachRecords
  • commmented out some logging lines which caused PHP errors because model is missing the logID method
  • better MANY_MANY parsing, now ignores whitespace between key names

Installation

unpack the zip file in your extensions folder

Requirements

tested with Yii 1.1.7 dev

Usage

The behavior config is an aray whose keys are relation names and corresponding values are config arrays.

The config is intended to allow customisable behavior, such as bypassing child validation or preventing child records deletion.

Right now configuration is NOT implemented, thus the behavior is to delete HAS_MANY and MANY_MANY related children on ActiveRecord beforeDelete().

You still need to pass the empty config array to behavior so it will know which relations it should handle

Model configuration

class XCmsPage extends XRichContent {
    public function relations() {
        return
        array('categories' = > array(self:: :: MANY_MANY, 'XCmsCategory',       
              'xcms_items_categories(item_id, cat_id)', 'together' = > true),
              'tags' => array(self::HAS_MANY, 'XCmsTag', 'parent_id')
            );
        }
 
        public function behaviors() {
            return array('XR' = > array('class' = >
           'application.extensions.XRelationBehavior', 
           'config' = > array(
                    'categories' = > array()
                    'images' = > array()
                       )
)
);
        }
...
    }

Controller configuration: attaching related records

public function actionCreate() {
$model = new XCmsPage();
//an array of related category primary keys, e.g. aray(5,3,4,10)
 $c = $_POST['categories'];
//generate array of related tag model ActiveRecord objects
foreach($_POST['tags'] as $tag){
$tag_model = new XCmsTag;
$tag_model->setAttribute('title',$tag);
$new_tags[]=$tag_model;
}
 $model->attachRelatedRecords(array(
                    'categories' => $c,
                    'tags' => $new_tags,
                ));
$model->save();
...
}

In this example the behavior will set the correct parent_id on child records by using the afterSave() event on XCmsPage. So after theXcmsPage is saved and it gets assigned a primary key(because it was a new record), the behavior will iterate through child records and will set their foreign key.

So you may attach related records regardless of the parent record being newly created or existing.

MANY_MANY is handled by selectively deleting bridgle table (xcms_items_categories) records NOT in $c,(see the above example) and adding those which are present in $c and not present in the bridge table. This minimizes the number of SQL queries. In other words, after saving the XcmsPage record, it will ONLY have the related records passed in $c.

By contrast, HAS_MANY is implemented in such way that it will always save children, it does not care if the parent record has previously saved children.

The coding approach is to trigger a particulary named method depending on relation type. For example, when saving the XCmsPage in the above example, the behavior will look for

  • MM_BeforeSave
  • MM_AfterSave
  • .. and so on (upon processing related categories, which relate as MANY_MANY)

if these methods exist, it will trigger them when the master record does a beforeSave(), AfterSave(), etc.

Note the prefix, which depends on relation type, and the capitalization on 'BeforeSave' and 'AfterSave' Here are the configured prefixes:

CActiveRecord::HAS_MANY => 'HM_',
CActiveRecord::HAS_ONE => 'HO_',
CActiveRecord::MANY_MANY => 'MM_',

So extending the code is mostly a matter of designing methods after the above rules. The behavior will trigger them appropriately.

I posted hoping that someone will takeover and implement transactional saving and deletion, as well as composite keys and per-relation configuration. Please let me know by commenting or contacting me through forum id "tudorilisoi"

Please let me know your thoughts and look through the code as the most important reference.

Be the first person to leave a comment

Please to leave your comment.

Create extension