Yii2:如何在 ActiveRecord 中设置默认属性值?
Yii2: How to set default attribute values in ActiveRecord?
这似乎是一个微不足道的问题,但我能想到的所有显而易见的解决方案都有其自身的缺陷。
我们想要的是能够仅为新记录设置任何默认的 ActiveRecord 属性值,以使其在验证之前和期间可读并且不干扰用于搜索的派生 classes 的方式.
一旦实例化 class,就需要设置默认值并准备好,以便 (new MyModel)->attr
returns 默认 attr
值。
以下是一些可能性及其存在的问题:
A) 在 MyModel
中覆盖 init()
方法并在 isNewRecord
为真时分配默认值,就像这样:
public function init() {
if ($this->isNewRecord) {
$this->attr = 'defaultValue';
}
parent::init();
}
问题:搜索。除非我们在 MySearchModel
中显式取消设置我们的默认属性(非常容易出错,因为它太容易忘记),这也会在派生的 MySearchModel
[=] 中调用 search()
之前设置值94=] 并干扰搜索(attr
属性已设置,因此搜索将返回不正确的结果)。在 Yii1.1 中,这是通过在调用 search()
之前调用 unsetAttributes()
来解决的,但是在 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) 在 rules()
中使用 DefaultValidator
在验证期间设置默认属性值,如下所示:
public function rules() {
return [
[
'attr', 'default',
'value' => 'defaultValue',
'on' => 'insert', // instantiate model with this scenario
],
// ...
];
}
问题:与 B) 和 C) 相同。在我们实际保存或验证记录之前不会设置值。
那么设置默认属性值的正确方法是什么?有没有其他方法没有概述的问题?
你的问题我看了好几遍,觉得有些矛盾。
您希望默认值在验证之前和期间可读,然后您尝试 init()
或 beforeSave()
。因此,假设您只想在模型中设置默认值,以便它们可以在生命周期的一部分中尽可能长时间地存在并且不干扰派生 类,只需在初始化对象后设置它们。
您可以准备单独的方法,其中设置了所有默认值并显式调用它。
$model = new Model;
$model->setDefaultValues();
或者您可以创建静态方法来创建模型并设置所有默认值和return它的实例。
$model = Model::createNew();
或者您可以将默认值传递给构造函数。
$model = new Model([
'attribute1' => 'value1',
'attribute2' => 'value2',
]);
这与直接设置属性没有太大区别。
$model = new Model;
$model->attribute1 = 'value1';
$model->attribute2 = 'value2';
一切都取决于您希望模型对控制器的透明程度。
这种方式是为整个生命周期设置属性,直接初始化除外,它不会干扰派生搜索模型。
我知道有人回答了,但我会添加我的方法。
我有 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
具有数据库 table 中的所有默认属性。
或者在您的规则中您可以使用:
[['field_name'], 'default', 'value'=> $defaultValue],
现在 $model
将始终使用您指定的默认值创建。
您可以在此处查看核心验证器的完整列表 http://www.yiiframework.com/doc-2.0/guide-tutorial-core-validators.html
您可以准备单独的方法,其中设置了所有默认值并显式调用它。
$model = new Model;
if($model->isNewRecord())
$model->setDefaultValues();
只需像这样重写模型中的 __construct()
方法:
class MyModel extends \yii\db\ActiveRecord {
function __construct(array $config = [])
{
parent::__construct($config);
$this->attr = 'defaultValue';
}
...
}
这是 Yii 臃肿的多用途 ActiveRecords 的挂断
依我拙见,表单模型、活动记录和搜索模型最好拆分成单独的 classes/subclasses
为什么不拆分搜索模型和表单模型?
abstract class Creature extends ActiveRecord {
...
}
class CreatureForm extends Creature {
public function init() {
parent::init();
if ($this->isNewRecord) {
$this->number_of_legs = 4;
}
}
}
class CreatureSearch extends Creature {
public function search() {
...
}
}
这种方法的好处是
- 您可以轻松满足不同的验证、设置和显示情况,而无需求助于一堆 ifs 和开关
- 您仍然可以在父级中保留通用代码 class 以避免重复
- 您可以对每个子项进行更改class,而不必担心它会如何影响另一个子项
- 个人 class 不需要知道他们任何 siblings/children 的存在就可以正常工作
事实上,在我们最近的项目中,我们使用的搜索模型根本没有从相关的 ActiveRecord 扩展
如果你想从数据库加载默认值,你可以把这段代码放在你的模型中
public function init()
{
parent::init();
if(!method_exists($this,'search')) //for checking this code is on model search or not
{
$this->loadDefaultValues();
}
}
这似乎是一个微不足道的问题,但我能想到的所有显而易见的解决方案都有其自身的缺陷。
我们想要的是能够仅为新记录设置任何默认的 ActiveRecord 属性值,以使其在验证之前和期间可读并且不干扰用于搜索的派生 classes 的方式.
一旦实例化 class,就需要设置默认值并准备好,以便 (new MyModel)->attr
returns 默认 attr
值。
以下是一些可能性及其存在的问题:
A) 在
MyModel
中覆盖init()
方法并在isNewRecord
为真时分配默认值,就像这样:public function init() { if ($this->isNewRecord) { $this->attr = 'defaultValue'; } parent::init(); }
问题:搜索。除非我们在
MySearchModel
中显式取消设置我们的默认属性(非常容易出错,因为它太容易忘记),这也会在派生的MySearchModel
[=] 中调用search()
之前设置值94=] 并干扰搜索(attr
属性已设置,因此搜索将返回不正确的结果)。在 Yii1.1 中,这是通过在调用search()
之前调用unsetAttributes()
来解决的,但是在 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) 在
rules()
中使用DefaultValidator
在验证期间设置默认属性值,如下所示:public function rules() { return [ [ 'attr', 'default', 'value' => 'defaultValue', 'on' => 'insert', // instantiate model with this scenario ], // ... ]; }
问题:与 B) 和 C) 相同。在我们实际保存或验证记录之前不会设置值。
那么设置默认属性值的正确方法是什么?有没有其他方法没有概述的问题?
你的问题我看了好几遍,觉得有些矛盾。
您希望默认值在验证之前和期间可读,然后您尝试 init()
或 beforeSave()
。因此,假设您只想在模型中设置默认值,以便它们可以在生命周期的一部分中尽可能长时间地存在并且不干扰派生 类,只需在初始化对象后设置它们。
您可以准备单独的方法,其中设置了所有默认值并显式调用它。
$model = new Model;
$model->setDefaultValues();
或者您可以创建静态方法来创建模型并设置所有默认值和return它的实例。
$model = Model::createNew();
或者您可以将默认值传递给构造函数。
$model = new Model([
'attribute1' => 'value1',
'attribute2' => 'value2',
]);
这与直接设置属性没有太大区别。
$model = new Model;
$model->attribute1 = 'value1';
$model->attribute2 = 'value2';
一切都取决于您希望模型对控制器的透明程度。
这种方式是为整个生命周期设置属性,直接初始化除外,它不会干扰派生搜索模型。
我知道有人回答了,但我会添加我的方法。 我有 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
具有数据库 table 中的所有默认属性。
或者在您的规则中您可以使用:
[['field_name'], 'default', 'value'=> $defaultValue],
现在 $model
将始终使用您指定的默认值创建。
您可以在此处查看核心验证器的完整列表 http://www.yiiframework.com/doc-2.0/guide-tutorial-core-validators.html
您可以准备单独的方法,其中设置了所有默认值并显式调用它。
$model = new Model;
if($model->isNewRecord())
$model->setDefaultValues();
只需像这样重写模型中的 __construct()
方法:
class MyModel extends \yii\db\ActiveRecord {
function __construct(array $config = [])
{
parent::__construct($config);
$this->attr = 'defaultValue';
}
...
}
这是 Yii 臃肿的多用途 ActiveRecords 的挂断
依我拙见,表单模型、活动记录和搜索模型最好拆分成单独的 classes/subclasses
为什么不拆分搜索模型和表单模型?
abstract class Creature extends ActiveRecord {
...
}
class CreatureForm extends Creature {
public function init() {
parent::init();
if ($this->isNewRecord) {
$this->number_of_legs = 4;
}
}
}
class CreatureSearch extends Creature {
public function search() {
...
}
}
这种方法的好处是
- 您可以轻松满足不同的验证、设置和显示情况,而无需求助于一堆 ifs 和开关
- 您仍然可以在父级中保留通用代码 class 以避免重复
- 您可以对每个子项进行更改class,而不必担心它会如何影响另一个子项
- 个人 class 不需要知道他们任何 siblings/children 的存在就可以正常工作
事实上,在我们最近的项目中,我们使用的搜索模型根本没有从相关的 ActiveRecord 扩展
如果你想从数据库加载默认值,你可以把这段代码放在你的模型中
public function init()
{
parent::init();
if(!method_exists($this,'search')) //for checking this code is on model search or not
{
$this->loadDefaultValues();
}
}