Удалить необходимое из поля в дочерней модели yii2

У меня есть основная модель, и у нее есть правила поля:

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 обязательно, у меня ничего не сохраняется, то, что я делал в дочерней модели, не работает. Как сделать поле необязательным, не редактируя первую модель?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
2
0
58
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете обойти требуемую проверку, добавив предложение where в свое правило:

[['username', 'email'], 'required', 'when' => function() { return false; }],

Поскольку функция всегда возвращает false, валидатор никогда не применяется.

не помогает, я не могу редактировать модель User, а если это добавить в UserForm, то ничего не меняется

Hector 20.01.2023 14:01

Вы пытались опустить array_merge и написать все правила самостоятельно? Или вы можете использовать array_replace_recursive вместо array_merge.

schmauch 20.01.2023 14:57
Ответ принят как подходящий

Вы не можете заменить правило, добавив другое. Если вы определяете такие правила, в результате оба правила, 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 будет проверено, оно уже будет иметь значение поля электронной почты и пройдет проверку.

Другие вопросы по теме