Yii on update, detect if a specific AR property has been changed on beforeSave()
Asked Answered
C

4

8

I am raising a Yii event on beforeSave of the model, which should only be fired if a specific property of the model is changed.

The only way I can think of how to do this at the moment is by creating a new AR object and querying the DB for the old model using the current PK, but this is not very well optimized.

Here's what I have right now (note that my table doesn't have a PK, that's why I query by all attributes, besides the one I am comparing against - hence the unset function):

public function beforeSave()
{
    if(!$this->isNewRecord){ // only when a record is modified
        $newAttributes = $this->attributes;
        unset($newAttributes['level']);
        $oldModel = self::model()->findByAttributes($newAttributes);

        if($oldModel->level != $this->level)
            // Raising event here
    }
    return parent::beforeSave();
}

Is there a better approach? Maybe storing the old properties in a new local property in afterFind()?

Cao answered 27/8, 2013 at 9:16 Comment(0)
C
17

You need to store the old attributes in a local property in the AR class so that you can compare the current attributes to those old ones at any time.

Step 1. Add a new property to the AR class:

// Stores old attributes on afterFind() so we can compare
// against them before/after save
protected $oldAttributes;

Step 2. Override Yii's afterFind() and store the original attributes immediately after they are retrieved.

public function afterFind(){
    $this->oldAttributes = $this->attributes;
    return parent::afterFind();
}

Step 3. Compare the old and new attributes in beforeSave/afterSave or anywhere else you like inside the AR class. In the example below we are checking if the property called 'level' is changed.

public function beforeSave()
{
    if(isset($this->oldAttributes['level']) && $this->level != $this->oldAttributes['level']){

            // The attribute is changed. Do something here...

    }

    return parent::beforeSave();
}
Cao answered 27/8, 2013 at 10:9 Comment(1)
This is indeed the only "clean" way to do it in Yii. What I did was implement a derived ActiveRecord that has that functionality built in and makes it available to all my models. A lot easier than doing it again every time. I also gave it methods like "getIsChanged()" and "getChangedProperties()" and so on.Tridentine
B
2

Just in one line

$changedArray = array_diff_assoc($this->attributes, $this->oldAttributes);

foreach($changedArray as $key => $value){

  //What ever you want 
  //For attribute use $key
  //For value use $value

}

In your case you want to use if($key=='level') inside of foreach

Brucie answered 10/11, 2016 at 12:50 Comment(0)
R
1

Yii 1.1: mod-active-record at yiiframework.com

or Yii Active Record instance with "ifModified then ..." logic and dependencies clearing at gist.github.com

Rance answered 19/8, 2014 at 19:57 Comment(0)
S
0

You can store old properties with hidden fields inside update form instead of loading model again.

Sajovich answered 27/8, 2013 at 9:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.