我有一个主模型,它有一个字段规则:
class User extends yiidbActiveRecord
{
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'users';
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['username', 'email'], 'required'],
];
}
}
我必须马上说,我不能对这个模型进行更改,我只能在从这个继承的下一个模型中进行所有更改:
class UserForm extends User
{
public function rules(): array
{
return array_merge(
parent::rules(),
[
['username', 'safe', 'skipOnEmpty' => true],
]
);
}
}
最重要的是,在第一个模型中,用户名字段是必需的,但是现在我需要使它在填写表单时为空,并且在保存时将电子邮件字段的值复制到用户名字段。
对于复制,我使用以下函数:public function beforeSave($insert)
{
if (empty($this->username)) {
$this->username = $this->email;
}
return parent::beforeSave($insert);
}
但是由于用户名字段是必需的,所以没有为我保存任何内容,我在子模型中所做的工作不起作用。如何在不编辑第一个模型的情况下使字段可选?
不能通过添加新规则来替换该规则。如果您这样定义规则,结果是required
和safe
规则都适用于username
字段。
您必须在规则定义数组中找到required
规则,并从该规则应用的属性中删除username
。如果您知道规则定义总是在特定的位置上,这就很容易了。例如,如果它总是父模型的第一条规则:
public function rules()
{
$rules = parent::rules();
// the array diff is used to remove only 'username' field from list of attributes
$rules[0][0] = array_diff($rules[0][0], ['username']);
return $rules;
}
如果你不知道required
规则的位置,你需要使用一些循环来找到它。例如:
foreach ($rules as $rule) {
if (
$rule[1] == 'required'
&& (
(is_array($rule[0]) && in_array('username', $rule[0]))
|| (is_string($rule[0]) && $rule[0] == 'username')
)
) {
// rule found
}
}
但是还有一个可能的解决方案解决你的问题。而不是复制beforeSave()
回调email
字段的值,你可以复制它在beforeValidate()
回调。这样,当username
字段被验证时,它将已经有email字段的值,并且它将通过验证。
您可以通过在规则中添加where子句来绕过所需的验证:
[['username', 'email'], 'required', 'when' => function() { return false; }],
由于函数总是返回false,因此永远不会应用验证器。