it-swarm.com.ru

Yii2: Как установить значения атрибутов по умолчанию в ActiveRecord?

Это может показаться тривиальным вопросом, однако все очевидные решения, о которых я могу подумать, имеют свои недостатки.

Мы хотим установить любое значение атрибута ActiveRecord по умолчанию только для новых записей таким образом, чтобы сделать его читаемым до и во время проверки и не мешать производным классам, используемым для поиска.

Значения по умолчанию должны быть установлены и готовы, как только мы создадим экземпляр класса, чтобы (new MyModel)->attr возвращало значение по умолчанию attr.

Вот некоторые из возможностей и проблем, которые у них есть:

  • A) В MyModel переопределите метод init() и присвойте значение по умолчанию, когда isNewRecord имеет значение true, примерно так:

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

    Проблема: Поиск. Если мы явно не удалим наш атрибут по умолчанию в MySearchModel (очень подверженный ошибкам, потому что его слишком легко забыть), это также установит значение перед вызовом search() в производном классе MySearchModel и будет мешать поиску (атрибут attr уже будет установлен так поиск будет давать неверные результаты). В Yii1.1 это было решено путем вызова unsetAttributes() перед вызовом search(), однако в Yii2 такого метода не существует.

  • B) В MyModel переопределите метод beforeSave() следующим образом:

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

    Проблема: атрибут не установлен в несохраненных записях. (new MyModel)->attr является null. Хуже того, даже другие правила проверки, основанные на этом значении, не смогут получить к нему доступ, потому что beforeSave() называется after validation. 

  • C) Чтобы гарантировать, что значение доступно во время проверки, мы можем вместо этого переопределить метод beforeValidate() / и установить значения по умолчанию там следующим образом:

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

    Проблема: Атрибут все еще не установлен в несохраненных (не проверенных) записях. Нам нужно по крайней мере вызвать $model->validate(), если мы хотим получить значение по умолчанию.

  • D) Используйте DefaultValidator в rules(), чтобы установить значение атрибута по умолчанию во время проверки следующим образом:

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

    Проблема: То же, что B) и C). Значение не устанавливается, пока мы не сохраним или не подтвердим запись.

Так что is правильный способ установить значения атрибутов по умолчанию? Есть ли другой способ без обозначенных проблем?

10
mae

Я знаю, что на него дан ответ, но я добавлю свой подход .... У меня есть модели Application и ApplicationSearch. В модели приложения я добавляю init с проверкой текущего экземпляра. Если его ApplicationSearch, я пропускаю инициализации.

    public function init()
    { 
        if(!$this instanceof ApplicationSearch)  
        {
            $this->id = hash('sha256',  123);
        }

        parent::init();
    }

также, как @mae прокомментировал ниже, вы можете проверить существование метода поиска в текущем экземпляре, предполагая, что вы не добавили метод с поиском по имени в базовую модель без поиска, поэтому код становится:

    public function init()
    { 
        // no search method is available in Gii generated Non search class
        if(!method_exists($this,'search'))  
        {
            $this->id = hash('sha256',  123);
        }

        parent::init();
    }
5
Stefano Mtangoo

Есть два способа сделать это.

$model => new Model();

Теперь $model имеет все атрибуты по умолчанию из таблицы базы данных.

Или в ваших правилах вы можете использовать:

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

Теперь $model будет всегда создаваться с заданными вами значениями по умолчанию.

Вы можете увидеть полный список основных валидаторов здесь http://www.yiiframework.com/doc-2.0/guide-tutorial-core-validators.html

15
Coz

Я прочитал ваш вопрос несколько раз и думаю, что есть некоторые противоречия.
Вы хотите, чтобы значения по умолчанию были читаемыми до и во время проверки, а затем вы пытаетесь init() или beforeSave(). Итак, предполагая, что вы просто хотите установить значения по умолчанию в модели, чтобы они могли присутствовать как можно дольше в течение части жизненного цикла и не мешать производным классам, просто установите их после инициализации объекта.

Вы можете подготовить отдельный метод, в котором установлены все значения по умолчанию, и вызвать его явно.

$model = new Model;
$model->setDefaultValues();

Или вы можете создать статический метод для создания модели со всеми установленными значениями по умолчанию и возврата ее экземпляра.

$model = Model::createNew();

Или вы можете передать значения по умолчанию в конструктор.

$model = new Model([
    'attribute1' => 'value1',
    'attribute2' => 'value2',
]);

Это не сильно отличается от установки атрибутов напрямую.

$model = new Model;
$model->attribute1 = 'value1';
$model->attribute2 = 'value2';

Все зависит от того, насколько прозрачна ваша модель для вашего контроллера.

Таким образом, атрибуты устанавливаются для всего жизненного цикла, кроме прямой инициализации, и это не влияет на производную модель поиска.

1
Bizley

Просто переопределите метод __construct() в вашей модели следующим образом:

class MyModel extends \yii\db\ActiveRecord {

    function __construct(array $config = [])
       {
           parent::__construct($config);
           $this->attr = 'defaultValue';
       }
    ...
}
0
Игорь Чугусов