У меня есть основная модель, и у нее есть правила поля:
class User extends \yii\db\ActiveRecord
{
/**
* {@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],
]
);
}
}
Суть в том, что в первой модели поле username обязательно, а теперь мне нужно сделать так, чтобы при заполнении формы оно было пустым, а при сохранении значение поля email копировалось в поле username.
Для копирования я использую функцию:
public function beforeSave($insert)
{
if (empty($this->username)) {
$this->username = $this->email;
}
return parent::beforeSave($insert);
}
Но из-за того, что поле username обязательно, у меня ничего не сохраняется, то, что я делал в дочерней модели, не работает. Как сделать поле необязательным, не редактируя первую модель?
Вы можете обойти требуемую проверку, добавив предложение where в свое правило:
[['username', 'email'], 'required', 'when' => function() { return false; }],
Поскольку функция всегда возвращает false, валидатор никогда не применяется.
Вы пытались опустить array_merge
и написать все правила самостоятельно? Или вы можете использовать array_replace_recursive
вместо array_merge
.
Вы не можете заменить правило, добавив другое. Если вы определяете такие правила, в результате оба правила, 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
}
}
Но есть и другое возможное решение вашей проблемы. Вместо того, чтобы копировать значение поля email
в обратном вызове beforeSave()
, вы можете скопировать его в обратном вызове beforeValidate(). Таким образом, когда поле username
будет проверено, оно уже будет иметь значение поля электронной почты и пройдет проверку.
не помогает, я не могу редактировать модель User, а если это добавить в UserForm, то ничего не меняется