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
],
// ...
];
}
Задача : то же, что б) и в). Значение не устанавливается до тех пор, пока мы не сохраним или не проверим запись.
Так как же правильно установить значения атрибутов по умолчанию? Есть ли другой путь без обозначенных проблем?
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