20

这似乎是一个微不足道的问题,但是我能想到的所有明显的解决方案都有自己的缺陷。

我们想要的是能够为新记录设置任何默认的 ActiveRecord 属性值,以使其在验证之前和期间可读并且不干扰用于搜索的派生类的方式。

一旦我们实例化类,就需要设置默认值并准备好,以便(new MyModel)->attr返回默认attr值。

以下是一些可能性和他们遇到的问题:

  • A)MyModel覆盖该方法并在为 trueinit()时分配默认值,如下所示: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)->attrnull。更糟糕的是,即使是其他依赖此值的验证规则也无法访问它,因为验证后beforeSave()调用。

  • C)为了确保值在验证期间可用,我们可以重写该beforeValidate()方法并在那里设置默认值,如下所示:

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

    问题:在未保存(未验证)的记录中仍未设置属性。$model->validate()如果我们想获得默认值,我们至少需要调用。

  • D)在验证期间使用DefaultValidatorinrules()设置默认属性值,如下所示:

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

    问题:与 B) 和 C) 相同。在我们实际保存或验证记录之前,不会设置值。

那么设置默认属性值的正确方法是什么?有没有其他方法没有概述的问题?

4

7 回答 7

26

有两种方法可以做到这一点。

$model => new Model();

现在$model具有数据库表中的所有默认属性。

或者在您的规则中,您可以使用:

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

现在$model将始终使用您指定的默认值创建。

您可以在此处查看完整的核心验证器列表http://www.yiiframework.com/doc-2.0/guide-tutorial-core-validators.html

于 2017-01-18T03:45:30.303 回答
17

这是 Yii 臃肿的多功能 ActiveRecords 的挂机

在我看来,表单模型、活动记录和搜索模型最好分成单独的类/子类

为什么不拆分搜索模型和表单模型?

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() {
        ...
    }
}

这种方法的好处是

  • 您可以轻松满足不同的验证、设置和展示案例,而无需求助于一堆 if 和 switch
  • 您仍然可以在父类中保留公共代码以避免重复
  • 您可以对每个子类进行更改,而不必担心它会如何影响其他子类
  • 各个班级不需要知道他们的任何兄弟姐妹/孩子的存在即可正常运行

事实上,在我们最近的项目中,我们使用的搜索模型根本不从相关的 ActiveRecord 扩展而来

于 2019-03-13T17:02:48.433 回答
6

我知道它已经回答了,但我会添加我的方法。我有 Application 和 ApplicationSearch 模型。在应用程序模型中,我添加了 init 并检查当前实例。如果它的 ApplicationSearch 我跳过初始化。

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

        parent::init();
    }

同样正如@mae 在下面评论的那样,您可以检查当前实例中是否存在搜索方法,假设您没有将任何名称为 search 的方法添加到非搜索基础模型中,因此代码变为:

    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();
    }
于 2016-12-19T10:14:01.193 回答
3

我已经多次阅读您的问题,我认为存在一些矛盾。
您希望在验证之前和验证期间默认值是可读的,然后您尝试使用init()or 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';

一切都取决于您希望模型对控制器的透明程度。

这种方式属性是为整个生命周期设置的,除了直接初始化,它不会干扰派生的搜索模型。

于 2016-09-04T08:00:57.737 回答
0

只需像这样覆盖__construct()模型中的方法:

class MyModel extends \yii\db\ActiveRecord {

    function __construct(array $config = [])
       {
           parent::__construct($config);
           $this->attr = 'defaultValue';
       }
    ...
}
于 2018-05-12T21:57:03.497 回答
0

如果要从数据库加载默认值,可以将此代码放入模型中

 public function init()
 {
    parent::init(); 
    if(!method_exists($this,'search')) //for checking this code is on model search or not
    {
        $this->loadDefaultValues();
    }
 }
于 2021-06-11T02:34:06.143 回答
-1

您可以准备单独的方法,其中设置了所有默认值并显式调用它。

$model = new Model;
if($model->isNewRecord())
    $model->setDefaultValues();
于 2017-02-08T06:25:52.047 回答