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



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



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



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



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



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



    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() называется после проверки.




  • 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
    ],
    // ...
    ];
    }


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




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

1171   5  

5 ответов:

Я знаю, что это ответ, но я добавлю свой подход. У меня есть модели 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();
    }

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

$model => new Model();

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

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

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

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

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

Я читал ваш вопрос несколько раз и думаю, что здесь есть некоторые противоречия.
Вы хотите, чтобы значения по умолчанию считывались до и во время проверки, а затем вы пытаетесь 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';

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

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

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

class MyModel extends \yii\db\ActiveRecord {

    function __construct(array $config = [])
       {
           parent::__construct($config);
           $this->attr = 'defaultValue';
       }
    ...
}

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

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

Comments

    Ничего не найдено.