Yii2: How to set default attribute values in ActiveRecord?

mae picture mae · Sep 4, 2016 · Viewed 34k times · Source

This may seem like a trivial question, however all of the obvious solutions that I can think of have their own flaws.

What we want is to be able to set any default ActiveRecord attribute value for new records only, in a way that makes it readable before and during validation and does not interfere with derived classes used for search.

The default values need to be set and ready as soon as we instantiate the class, so that (new MyModel)->attr returns the default attr value.

Here are some of the possibilities and the problems they have:

  • A) In MyModel override the init() method and assign default value when isNewRecord is true like so:

    public function init() {
        if ($this->isNewRecord) {
            $this->attr = 'defaultValue';
        }
        parent::init();
    }
    

    Problem: Search. Unless we explicitly unset our default attribute in MySearchModel (very error-prone because it is too easy to forget), this will also set the value before calling search() in the derived MySearchModel class and interfere with searching (the attr attribute will already be set so search will be returning incorrect results). In Yii1.1 this was resolved by calling unsetAttributes() before calling search(), however no such method exists in Yii2.

  • B) In MyModel override the beforeSave() method like so:

    public function beforeSave($insert) {
        if ($insert) {
            $this->attr = 'defaultValue';
        }
        return parent::beforeSave();
    }
    

    Problem: Attribute is not set in unsaved records. (new MyModel)->attr is null. Worse yet, even other validation rules that rely on this value will not be able to access it, because beforeSave() is called after validation.

  • C) To ensure the value is available during validation we can instead override the beforeValidate() method and set the default values there like so:

    public function beforeValidate() {
        if ($this->isNewRecord) {
            $this->attr = 'defaultValue';
        }
        return parent::beforeValidate();
    }
    

    Problem: Attribute is still not set in unsaved (unvalidated) records. We need to at least call $model->validate() if we want to get the default value.

  • D) Use DefaultValidator in rules() to set a default attribute value during validation like so:

    public function rules() {
        return [
            [
                'attr', 'default',
                'value' => 'defaultValue',
                'on' => 'insert', // instantiate model with this scenario
            ],
            // ...
        ];
    }
    

    Problem: Same as B) and C). Value is not set until we actually save or validate the record.

So what is the right way to set default attribute values? Is there any other way without the outlined problems?

Answer

Coz picture Coz · Jan 18, 2017

There's two ways to do this.

$model => new Model();

Now $model has all the default attributes from the database table.

Or in your rules you can use:

[['field_name'], 'default', 'value'=> $defaultValue],

Now $model will always be created with the default values you specified.

You can see a full list of core validators here http://www.yiiframework.com/doc-2.0/guide-tutorial-core-validators.html