Yii 1.1: eadvancedarbehavior

The EAdvancedArBehavior extension removes the complexities involved when saving objects with relations
21 followers

The EAdvancedArBehavior extension takes away some of the complexities involved when saving objects, for example, with MANY_MANY and BELONGS_TO relations.

Requirements

Built on Yii 1.1.6

Usage

The EAdvancedArBehavior extension adds up some functionality to the default possibilites of yii´s ActiveRecord implementation.

To use this extension, just copy this file to your extensions/ directory, add 'import' => 'application.extensions.EAdvancedArBehavior', [...] to your config/main.php and add this behavior to each model you would like to inherit the new possibilities:

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

Depending on the functionallities you want to enable, you need to set some configuration parameters. This is described below.

Better support for MANY_MANY relations:

Suppose you have a User and Project model having a MANY_MANY relation, for example:

User.php:

public function relations() {
     return array( .....
         'projects'        => array(self::MANY_MANY, 'Project', 'tbl_project_user(user_id, project_id)') );
}

Project.php:

public function relations() {
     return array( .....
         'users'        => array(self::MANY_MANY, 'User', 'tbl_project_user(project_id, user_id)') );
}

Now you can use it like

$user = User::model()->findBPk(12) ;
$projects = Project::model()->findAll( $criteria ) ;
$user->projects = $projects ;
$user->save() ;

Remove relations like

$user->projects = array() ; // cleans up the relation table
// or
$user->delete() ; 

Better support for BELONGS_TO relations:

With this extension you can connect two related objects as follows:

$user = new User();
$user->company = Company::model()->find($conditions, $params);
$post->save();

Without this extension, you would have to do this as follows:

$user = new User();
$company = Company::model()->find($conditions, $params) ;
$user->company_id = $company->id ;
$post->save();

HAS_ONE and HAS_MANY

Suppose a User HAS_ONE Address and HAS_MANY Emails, and Address/Email BELONGS_TO User (both have the foreignkey column user_id). So you can do now:

$user = new User() ;
$user->emails = array( $email1, $email2, ... ) ;
$user->address = $address ;
$user->save() ;

Set uninitialized column fields to NULL

See: http://code.google.com/p/yiiext/downloads/detail?name=ensureNull_1.0.1.zip

Use it like

return array(
 'EAdvancedArBehavior' => array(
        'class' => 'application.extensions.EAdvancedArBehavior',
        'useOnUpdate' => false, // set empty fields to NULL only on inserts not updates
    ));

MySQL ONLY: Multiple timespans with CURRENT_TIMESTAMP

Although the title of this section suggests something which cannot be done with MySQL, there is a nice workaround available. This extension provides the means to have a table with one column for the insert time and an other for the last modification time. First, add the following two columns to your table definition

  update_time timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
  created_time timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',

Next, configure the following parameters

return array(
        'EAdvancedArBehavior' => array(
                'class' => 'application.extensions.EAdvancedArBehavior',
                'useNullOnTimestamp' => TRUE,         // set timestamps with a default value of 0000-00-00 00:00 to NULL (for inserts)
                'useOnUpdate' => true,                // required
                'onUpdateTimestamp'=>'update_time',   // column name of the update time
                ),
          );

ignoreRelationsExcept($array=null)

To reduce unnecessary database queries, define the relations which are modified before save():

$model->ignoreRelationsExcept( array('company', 'address') ) ;
$model->save() ;

All relations are ignored except company and address

TODO

add more functionalities (any AR suggestions are welcome!)

Total 14 comments

#9025 report it
zealotous at 2012/07/13 06:36pm
problem with php 5.4 build in server

I have got error when running my application on php 5.4.4 buildin server

Declaration of EAdvancedArbehavior::beforeValidate() should be compatible with CModelBehavior::beforeValidate($event)

Any idea how to fix this?

I use EAdvancedArBehavior 2.3

Update: Replace beforeSave function definition

-   public function beforeSave() {
+   public function beforeSave($event) {

Solution found here...

#6940 report it
Bravoman at 2012/02/15 02:52am
Casing typo in the class name

The class you build is awesome by the way. But it's always referenced as "EAdvancedArBehavior". While the class name in the file is spelled with a lower b in "behavior". This is a problem in Unix environments as the filename does have a capital B in it. The problem doesn't occur unless you are using the class as a behavior for an object that needs to (serialized and) unserialized, which leads to an autoload error.

#5698 report it
Athos at 2011/11/03 12:29pm
Error with alphanumeric keys

To avoid error with alphanumeric keys, line 217 should be:

return sprintf("%s, ('%s', '%s')", $sql, $this->owner->{$this->owner->tableSchema->primaryKey}, $rel )  ;
#4762 report it
pastia at 2011/08/12 07:46am
Update HAS_ONE relations

Hi, I have noticed that this extension only work when creating an object that has a 'HAS_ONE' relation, but not also when updating.

I want to do something like this: $client->attributes=$_POST['Client']; $client->clientSetup->attributes = $_POST['ClientSetup']; $client->save();

The problem is on line 67 of your code, because you only save if the foreignkey field is empty or different.

Could you fix this ?

#3475 report it
jeanluca at 2011/04/14 11:54am
Bug Issue

thanks for pointing that out!!

I've uploaded a new version (v0.8) which solves this issue for HAS_ONE and HAS_MANY

Or did you experience it with MANY_MANY ?

cheers

#3471 report it
R.K. at 2011/04/14 09:55am
Bug Issue

Note: Do not use this extension on all your active records because on update it will update all items. I wanted to update one of my table element post.view count. it updated all my tables.

#3342 report it
jeanluca at 2011/04/05 04:22am
New Behaviors

Hello

I've uploaded a new version which implements both suggested behaviors concerning deleting relations from the relation table.

Furthermore, the suggested 'transaction' behavior, I actually like how Yii does it (its easy to use) so I can't think of a better way to implement this!

Thanks a lot for the help!

Cheers

#3334 report it
R.K. at 2011/04/04 07:11am
Remove all relations with table

I think this will be also good feather to this extension. delete all relation if array will be empty. Ex:

$post = Post::model()->findByPk(1);
$post->tags = array();
$post->save();

enchantment of class function.

public function writeManyManyTables()
    {
        ...
elseif (is_array($this->owner->$key) && $this->owner->$key === array())
                    {
                        $this->executeManyManyEntry($this->makeManyManyDeleteCommand(
                            $relation[2],
                            $this->owner->{$this->owner->tableSchema->primaryKey}));
                    }
...
    }
#3140 report it
zealotous at 2011/03/20 11:24am
save-relations-ar-behavior

this extention better then save-relations-ar-behavior component, for me.

#3110 report it
Athos at 2011/03/16 05:16pm
Delete MANY_MANY relations

Hi again.

I introduced a new behavior:

public function beforeDelete($event)
{
    Yii::trace('deleting MANY_MANY data for '.get_class($this->owner),'system.db.ar.CActiveRecord');
 
    foreach($this->owner->relations() as $key => $relation)
    {
        if($relation['0'] == CActiveRecord::MANY_MANY) // ['0'] equals relationType
        {
                            $this->executeManyManyEntry($this->makeManyManyDeleteCommand(
                                    $relation[2],
                                    $this->owner->{$this->owner->tableSchema->primaryKey}));
 
                    }
            }
    }
#3109 report it
jeanluca at 2011/03/16 03:37pm
Transaction

very interesting, that would be useful! I have to perform a little bit of research first, but if I have figured something out I'll post my ideas here!

thnx for tip

#3101 report it
Athos at 2011/03/16 07:59am
Transaction

Thank you for your attention.

Perhaps you might want to now implement the transaction.

#3098 report it
jeanluca at 2011/03/16 05:05am
Returns error if the primary key is other than "id"

thanks! I have it implemented in the current version (v0.3)

#3093 report it
Athos at 2011/03/15 03:12pm
Returns error if the primary key is other than "id"

I suggest changing line 108 from: $this->owner->$relation[2] = $this->owner->{$key}->id;

To: $this->owner->$relation[2] = $this->owner->{$key}->primaryKey;

Leave a comment

Please to leave your comment.

Create extension