Yii2: How to set default attribute values in ActiveRecord?
<p>This may seem like a trivial question, however all of the obvious solutions that I can think of have their own flaws.</p>

<p>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.</p>

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

<p>Here are some of the possibilities and the problems they have:</p>

<ul>
<li><p><strong>A)</strong> In <code>MyModel</code> override the <code>init()</code> method and assign default value when <code>isNewRecord</code> is true like so:</p>

<pre><code>public function init() {
    if ($this-&gt;isNewRecord) {
        $this-&gt;attr = 'defaultValue';
    }
    parent::init();
}
</code></pre>

<p><strong>Problem</strong>: Search. Unless we explicitly unset our default attribute in <code>MySearchModel</code> (very error-prone because it is too easy to forget), this will also set the value before calling <code>search()</code> in the derived <code>MySearchModel</code> class and interfere with searching (the <code>attr</code> attribute will already be set so search will be returning incorrect results). In Yii1.1 this was resolved by calling <a href="http://www.yiiframework.com/doc/api/1.1/CModel#unsetAttributes-detail" rel="noreferrer"><code>unsetAttributes()</code></a> before calling <code>search()</code>, however no such method exists in Yii2.</p></li>
<li><p><strong>B)</strong> In <code>MyModel</code> override the <a href="http://www.yiiframework.com/doc-2.0/yii-db-baseactiverecord.html#beforeSave()-detail" rel="noreferrer"><code>beforeSave()</code></a> method like so:</p>

<pre><code>public function beforeSave($insert) {
    if ($insert) {
        $this-&gt;attr = 'defaultValue';
    }
    return parent::beforeSave();
}
</code></pre>

<p><strong>Problem</strong>: Attribute is not set in unsaved records. <code>(new MyModel)-&gt;attr</code> is <code>null</code>. Worse yet, even other validation rules that rely on this value will not be able to access it, because <code>beforeSave()</code> is called <em>after</em> validation. </p></li>
<li><p><strong>C)</strong> To ensure the value is available during validation we can instead override the <a href="http://www.yiiframework.com/doc-2.0/yii-base-model.html#beforeValidate()-detail" rel="noreferrer"><code>beforeValidate()</code></a> method and set the default values there like so:</p>

<pre><code>public function beforeValidate() {
    if ($this-&gt;isNewRecord) {
        $this-&gt;attr = 'defaultValue';
    }
    return parent::beforeValidate();
}
</code></pre>

<p><strong>Problem</strong>: Attribute is still not set in unsaved (unvalidated) records. We need to at least call <code>$model-&gt;validate()</code> if we want to get the default value.</p></li>
<li><p><strong>D)</strong> Use <a href="http://www.yiiframework.com/doc-2.0/yii-validators-defaultvaluevalidator.html" rel="noreferrer"><code>DefaultValidator</code></a> in <code>rules()</code> to set a default attribute value during validation like so:</p>

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

<p><strong>Problem</strong>: Same as B) and C). Value is not set until we actually save or validate the record.</p></li>
</ul>

<p>So what <em>is</em> the right way to set default attribute values? Is there any other way without the outlined problems?</p>