4

I'm trying to do data transformation on an attribute by using the setter method setUsr_firstname for an attribute that is named usr_firstname.

class User extends ActiveRecord implements IdentityInterface {

    public function rules() {
        return [
            [['usr_firstname', 'usr_lastname', ... ], 'required'],
            ...
        ];
    }

    ...

    public function setUsr_firstname($value) {
        $this->usr_firstname = fix_wrong_title_case($value);
    }
}

Then I do the following:

$model = new User(['scenario' => User::SCENARIO_REGISTER]);
$model->usr_firstname = 'John';

But the setter method is never called! I have tried naming the method all kinds of things - eg. setUsrFirstname - but nothing works. How do I get this to work?

UPDATE

Figured out it doesn't work for any ActiveRecord attribute, with or without an underscore in the attribute name.

TheStoryCoder
  • 2,874
  • 4
  • 24
  • 54
  • So are you saying that with method `public function setUsrFirstname($value) { $this->usr_firstname = fix_wrong_title_case($value); }` and calling it like `$model->setUsrFirstname('John');` it doesn't work? – Bizley Oct 20 '16 at 16:22
  • @Bizley No, I'm not calling the setter method directly - that is the whole point with Yii's data transformation. Any time the attribute `usr_firstname` is modified anywhere in the system, Yii should automatically invoke the setter method `setUsr_firstname` and run the value through that before setting it in the model/object. – TheStoryCoder Oct 20 '16 at 16:26
  • 1
    This does not work in case of properties declared directly or Active Record attributes (no matter if it's with the underscore or not). – Bizley Oct 20 '16 at 16:44
  • You should handle this with a validation rule – soju Oct 20 '16 at 21:09
  • @Bizley It is an ActiveRecord attribute but why won't it work with that? And what do I do instead? – TheStoryCoder Oct 21 '16 at 06:32
  • @TheStoryCoder I was about to ask for the code you claimed is working but since you edited the comment you probably know now. The reason is [here](https://github.com/yiisoft/yii2/blob/2.0.10/framework/db/BaseActiveRecord.php#L301) - AR's magic `__set` method doesn't allow it. You can use [FilterValidator](http://www.yiiframework.com/doc-2.0/guide-tutorial-core-validators.html#filter) to get the same thing during validation or override models `afterFind()` and/or `beforeSave()` methods with that attribute. – Bizley Oct 21 '16 at 06:42
  • @Bizley I see. Too bad there isn't a way to do it before the model is saved... But I guess you could call `$model->validate(); $model->clearErrors();` to actually achieve it! If you want to write your comment as an answer I can accept it... – TheStoryCoder Oct 21 '16 at 09:09

1 Answers1

8

Setter method is not called in case of properties declared directly or Active Record attributes (no matter if it's with the underscore or not).

Active Record's magic __set method assigns value directly to the mapped attribute.

You can achieve something similar with new virtual attribute:

public function setUsrFirstname($value)
{
    $this->usr_firstname = doSomethingWith($value);
}

public function getUsrFirstname()
{
    return $this->usr_firstname;
}

So now you can use it like:

$this->usrFirstname = 'John';
echo $this->usrFirstname;

If you want to modify the attribute in the life cycle of AR you can:

  • use FilterValidator to change it during validation,
  • override model's afterFind() method to change it after DB fetch,
  • override model's beforeSave() method to change it before saving to DB,
  • prepare behavior that can do all the things above,
  • prepare method to be called directly that will change it (you can use the first setter method for this; no need to call validate() and clearErrors()).
Bizley
  • 15,937
  • 5
  • 43
  • 53