这似乎是一个微不足道的问题,但是我能想到的所有明显的解决方案都有其自身的缺陷。
我们想要的是能够为新记录设置任何默认的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中,这是通过在调用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)相同。在我们实际保存或验证记录之前,不会设置值。
那么是设置默认属性值的正确方法?没有概述的问题还有其他方法吗?
答案 0 :(得分:16)
有两种方法可以做到这一点。
$model => new Model();
现在$model
具有数据库表中的所有默认属性。
或者在您的规则中,您可以使用:
[['field_name'], 'default', 'value'=> $defaultValue],
现在将始终使用您指定的默认值创建$model
。
您可以在此处查看核心验证器的完整列表http://www.yiiframework.com/doc-2.0/guide-tutorial-core-validators.html
答案 1 :(得分:6)
我知道答案已经回答,但我会添加我的方法。 我有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();
}
答案 2 :(得分: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() {
...
}
}
这种方法的好处是
实际上,在我们最近的项目中,我们使用的搜索模型根本没有从相关的ActiveRecord扩展
答案 3 :(得分:2)
我已多次阅读你的问题,我认为存在一些矛盾
您希望在验证之前和验证期间可以读取默认值,然后尝试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';
一切都取决于您希望模型对您的控制器有多透明。
这种方式为整个生命周期设置属性,但直接初始化除外,它不会干扰派生搜索模型。
答案 4 :(得分:0)
只需在模型中覆盖__construct()
方法,如下所示:
class MyModel extends \yii\db\ActiveRecord {
function __construct(array $config = [])
{
parent::__construct($config);
$this->attr = 'defaultValue';
}
...
}
答案 5 :(得分:0)
如果你想从数据库加载默认值,你可以把这段代码放在你的模型中
public function init()
{
parent::init();
if(!method_exists($this,'search')) //for checking this code is on model search or not
{
$this->loadDefaultValues();
}
}
答案 6 :(得分:-1)
您可以准备单独的方法,其中设置了所有默认值并明确调用它。
$model = new Model;
if($model->isNewRecord())
$model->setDefaultValues();