Формы и валидация в Yii 2: частые ошибки новичков
Форма отправляется, а данные в базу не попадают. Или валидация молчит там, где должна ругаться. Разбираем, почему модель Yii 2 ведёт себя не так, как ждёт новичок, и как настроить формы правильно.

Вы сделали форму на Yii 2, нажали «Сохранить», страница перезагрузилась — и ничего. Ни ошибки, ни записи в базе. Или наоборот: форма принимает пустые поля, которые вы «точно пометили обязательными». В девяти случаях из десяти дело не в багах фреймворка, а в том, что модель Yii 2 работает по правилам, о которых новичок не знает: правила валидации привязаны к сценариям, а массовое присвоение атрибутов возможно только для тех полей, что явно объявлены безопасными.
Разберём типичные грабли на связке ActiveForm + Model + контроллер. Версия — Yii 2.0.x (актуальна ветка 2.0.49 и выше). Код рабочий, его можно повторить на любом свежем шаблоне basic или advanced.
Что должно быть до начала
- Установленный Yii 2.0.x (проверьте
Yii::getVersion()или файлcomposer.json). - Модель, унаследованная от
yii\base\Modelилиyii\db\ActiveRecord. - Форма во вьюхе через виджет
ActiveForm, а не голый HTML.
Дальше идут ошибки в порядке того, как часто на них наступают.
Ошибка 1. Атрибут не проходит, потому что не объявлен в rules()
Самое частое. Метод load() копирует данные из POST в модель, но только те атрибуты, которые считаются безопасными1. Безопасным атрибут становится, если для него есть хотя бы одно правило валидации в rules(). Нет правила — load() тихо пропускает поле, и в базу летит пустое значение.
public function rules()
{
return [
// без этой строки атрибуты email и name
// не загрузятся из формы
[['name', 'email'], 'required'],
['email', 'email'],
// поле, которое не проверяется, но должно грузиться:
['comment', 'safe'],
];
}Правило safe — это способ сказать «валидировать не надо, но грузить разрешаю». Если забыли и его, и любое другое правило для атрибута — он будет игнорироваться при массовом присвоении.
Если после $model->load(Yii::$app->request->post()) какой-то атрибут пустой, а в POST он точно есть — первым делом смотрите rules(). Скорее всего, для него нет ни одного правила.Ошибка 2. Забыли вызвать validate()
Метод load() только присваивает данные. Он не запускает проверки. Валидация — это отдельный вызов validate() либо неявно внутри save() у ActiveRecord.
public function actionCreate()
{
$model = new ContactForm();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
// сюда попадём только если данные валидны
$model->sendEmail();
return $this->redirect(['thanks']);
}
// рендерим форму заново — ошибки уже в $model
return $this->render('create', ['model' => $model]);
}Порядок в условии важен: сначала load(), потом validate(). Если написать наоборот, валидатор проверит пустую модель. У ActiveRecord метод save() по умолчанию сам вызывает validate(); отключить это можно, передав save(false) — но тогда невалидные данные уйдут в базу, и это делают осознанно, а не по невнимательности.
Ошибка 3. Сценарии: правило есть, но не срабатывает
Правило в rules() можно привязать к сценарию через ключ on или except. Если правило висит на сценарии update, а модель работает в сценарии по умолчанию — правило молчит.
public function rules()
{
return [
[['username'], 'required'],
// пароль обязателен только при регистрации
['password', 'required', 'on' => 'register'],
];
}Чтобы правило register заработало, сценарий надо установить до валидации:
$model = new User();
$model->scenario = 'register';
$model->load(Yii::$app->request->post());
$model->validate();Отдельный подвох: если вы переопределяете scenarios() вручную, то перечень безопасных атрибутов для сценария определяется уже этим методом, а не только rules(). Атрибут, не попавший в список сценария, снова не загрузится через load().
Ошибка 4. Имена полей в форме не совпадают с моделью
ActiveForm генерирует поля с именами вида ContactForm[email]. Метод load() ищет данные именно по имени класса модели. Если вы вручную написали <input name="email"> вместо $form->field($model, 'email'), load() ничего не найдёт в POST и вернёт false.
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'name')->textInput() ?>
<?= $form->field($model, 'email')->textInput() ?>
<?= Html::submitButton('Отправить') ?>
<?php ActiveForm::end(); ?>Если поле нужно загрузить под другим именем формы (например, при загрузке чужого массива), второй аргумент load($data, $formName) задаёт имя вручную. Пустая строка load($data, '') грузит атрибуты по плоским ключам без обёртки именем класса.
Ошибка 5. Клиентская валидация «съедает» отправку, а серверная не проверяет
По умолчанию ActiveForm включает JS-валидацию. Новичок радуется, что подсказки появляются без перезагрузки, и забывает, что клиентскую проверку можно обойти через консоль или отключённый JS. Серверная валидация в контроллере обязательна всегда — клиентская лишь удобство.
Обратная ситуация: JS-валидация блокирует отправку, а вы ждёте перезагрузки страницы и не понимаете, почему её нет. Проверьте вкладку с ошибками в браузере — поле подсвечено красным. Отключить клиентскую проверку для конкретного поля можно так:
<?= $form->field($model, 'email', [
'enableClientValidation' => false,
])->textInput() ?>Сравнение способов вывести ошибки
| Способ | Что показывает | Когда применять |
|---|---|---|
$form->field() | ошибку под конкретным полем | стандартная форма, ошибки рядом с полями |
$form->errorSummary($model) | общий список всех ошибок вверху | длинные формы, где поля далеко от кнопки |
$model->getErrors() | массив ошибок в коде | API, AJAX-ответы, логирование |
$model->getFirstError('email') | первую ошибку по атрибуту | когда нужен один текст, а не массив |
Если не получилось
- Форма отправляется, но
load()возвращаетfalse. Данных в POST нет под ожидаемым именем формы. ПроверьтеYii::$app->request->post()черезvar_dumpи сравните ключ с именем класса модели. - Атрибут остаётся пустым после
load(). Нет правила вrules()или атрибут не в списке безопасных для текущего сценария. Добавьте правило илиsafe. - Валидация проходит всегда, даже с пустыми полями. Забыли вызвать
validate()либо сохраняете черезsave(false). - Ошибка
Setting unknown property. В форме есть поле, которого нет в модели как публичного свойства. УActiveRecordатрибуты берутся из колонок таблицы; уModelих надо объявить какpublic $field;. - CSRF-ошибка 400 Bad Request. Форму собрали вручную без скрытого CSRF-токена. Виджет
ActiveFormдобавляет его сам — используйте виджет, а не голый HTML.
Что настроить дальше
Когда базовые формы работают, следующий шаг — вынести валидацию за пределы стандартных правил. Смотрите в сторону:
- метода
beforeValidate()для нормализации данных перед проверкой (обрезка пробелов, приведение регистра); - собственных валидаторов через inline-метод (
['field', 'validateSomething']) или отдельный класс-наследникyii\validators\Validator; - AJAX-валидации через
enableAjaxValidation— она проверяет данные серверными правилами без полной отправки формы.
Инвалидация связанных моделей и валидация вложенных форм — отдельная тема, здесь мы разобрали только одиночную модель. Начните с того, чтобы после каждого load() мысленно отвечать на два вопроса: есть ли правило для этого атрибута и вызван ли validate(). Эти две проверки закрывают большинство «форма не работает».
1 Безопасный атрибут — в терминологии Yii 2 атрибут, для которого разрешено массовое присвоение через load() и setAttributes(). Механизм защищает от подмены полей, которых нет в форме, но которые есть в модели (например, роль пользователя).
Тест и проверочная: Быстрые ответы с объяснением, почему остальные варианты неверны
Спросить за 10 ₽Источники: Yii 2 Guide — Validating Input, Yii 2 Guide — Creating Forms (ActiveForm)
Частые вопросы
У меня Yii 2.0.15, а примеры для более свежей версии — сработают?
Почему load() возвращает false, хотя форму я точно отправил?
Обязательно ли писать правило safe для полей, которые не нужно валидировать?
Можно ли доверять только клиентской валидации ActiveForm?
Что будет, если сохранить модель через save(false) с невалидными данными?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.