Yii2:如何在ActiveRecord中设置默认属性值?

mae*_*mae 12 activerecord yii2 yii2-model yii2-validation

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

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

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

以下是一些可能性和问题:

  • A)MyModel超控的init()方法和分配默认值时isNewRecord,像这样是正确的:

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

    问题:搜索.除非我们明确地取消设置我们的默认属性MySearchModel(非常容易出错,因为它太容易忘记),这也会在调用search()派生MySearchModel类之前设置值并干扰搜索(该attr属性已经设置,因此搜索将返回结果不正确).在Yii1.1中,这是通过在调用unsetAttributes()之前调用来解决的search(),但是在Yii2中不存在这样的方法.

  • B)MyModel超控的beforeSave(),像这样的方法:

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

    问题:未在未保存的记录中设置属性.(new MyModel)->attrnull.更糟糕的是,即使是依赖于此值的其他验证规则也无法访问它,因为验证之后beforeSave()会调用它.

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

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

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

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

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

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

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

Coz*_*Coz 17

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

$model => new Model();
Run Code Online (Sandbox Code Playgroud)

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

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

[['field_name'], 'default', 'value'=> $defaultValue],
Run Code Online (Sandbox Code Playgroud)

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

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

  • 正如我在选项** D **中指出的那样,该解决方案是不够的,因为在执行`$ model-> validate()`之前,该值实际上没有设置。 (2认同)

Ste*_*goo 6

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

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

        parent::init();
    }
Run Code Online (Sandbox Code Playgroud)

同样如下面的@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();
    }
Run Code Online (Sandbox Code Playgroud)

  • 抱歉,我真的不喜欢这种方法..您正在引入对父类的子类的依赖。子级应该能够随意更改,而不影响父级的功能,并且特定于子级的任何功能都应该在子级中可见。检查名为“search”的方法更糟糕! (2认同)

Art*_*rth 5

这是与Yii肿的多功能ActiveRecord的挂断

以我的拙见,最好将表单模型,活动记录和搜索模型拆分为单独的类/子类

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

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() {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

这种方法的好处是

  • 您可以轻松地满足不同的验证,设置和展示案例的需要,而无需借助大量的ifs和switch
  • 您仍然可以将通用代码保留在父类中,以避免重复
  • 您可以对每个子类进行更改,而不必担心它会如何影响另一个
  • 各个班级无需了解其兄弟姐妹/子女的存在即可正常运行

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

  • 这是一个老问题。从那以后,这几乎就是我一直在做的事情。应该这样记录在Yii中。 (2认同)