Когда вы впервые сталкиваетесь с Yii2, ActiveForm кажется простым инструментом. Но потом приходит задача: форма с динамическими полями, вложенные модели, зависимые выпадающие списки. И тут начинается танцы с бубном. В этой статье я покажу, как не сойти с ума и сделать все красиво.
Почему ActiveForm — это не просто форма
ActiveForm в Yii2 — это не просто генератор HTML. Это мощный инструмент, который умеет работать с моделями, валидацией и AJAX. Но чтобы раскрыть его потенциал, нужно понимать несколько вещей.
Во-первых, каждая форма привязана к модели. Даже если у вас сложная структура, лучше разбить её на несколько моделей. Например, для заказа у вас может быть модель Order и модель OrderItem. ActiveForm позволяет работать с ними через вложенные формы.
Во-вторых, валидация. Yii2 поддерживает клиентскую и серверную валидацию. Но для сложных сценариев (например, проверка уникальности комбинации полей) лучше использовать серверную валидацию с AJAX.
Динамические поля: когда полей много, а заранее неизвестно сколько
Представьте форму, где пользователь может добавлять товары в заказ. Количество товаров не ограничено. Как это сделать?
Самый простой способ — использовать JavaScript для добавления новых полей. Но в Yii2 есть более элегантное решение: использование виджета yii\widgets\ActiveForm в сочетании с yii\base\DynamicModel или вложенными моделями.
Вот пример, как добавить динамические поля для нескольких email-адресов:
// В контроллере
$model = new DynamicModel(['emails' => []]);
$model->addRule(['emails'], 'email', ['allowName' => true]);
if (Yii::$app->request->isPost) {
$model->load(Yii::$app->request->post());
if ($model->validate()) {
// Сохраняем
}
}
return $this->render('form', ['model' => $model]);
В представлении:
<?php $form = ActiveForm::begin(); ?>
<div id="emails-container">
<?php foreach ($model->emails as $index => $email): ?>
<?= $form->field($model, "emails[$index]")->textInput() ?>
<?php endforeach; ?>
</div>
<button type="button" id="add-email">Добавить email</button>
<?php ActiveForm::end(); ?>
И JavaScript для добавления новых полей:
$('#add-email').on('click', function() {
var index = $('#emails-container .form-group').length;
var field = '<div class="form-group">' +
'<label class="control-label" for="dynamicmodel-emails-' + index + '">Email</label>' +
'<input type="text" id="dynamicmodel-emails-' + index + '" class="form-control" name="DynamicModel[emails][' + index + ']">' +
'</div>';
$('#emails-container').append(field);
});
Вложенные формы: работаем с несколькими моделями
Часто форма содержит данные из разных таблиц. Например, форма регистрации пользователя включает данные профиля. В Yii2 это решается через вложенные модели.
Создайте две модели: User и Profile. В контроллере:
$user = new User();
$profile = new Profile();
if (Yii::$app->request->isPost) {
$user->load(Yii::$app->request->post());
$profile->load(Yii::$app->request->post());
if ($user->validate() && $profile->validate()) {
$user->save();
$profile->user_id = $user->id;
$profile->save();
// Редирект
}
}
return $this->render('register', ['user' => $user, 'profile' => $profile]);
В представлении используйте разные префиксы для полей:
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($user, 'username') ?>
<?= $form->field($user, 'password')->passwordInput() ?>
<?= $form->field($profile, 'first_name') ?>
<?= $form->field($profile, 'last_name') ?>
<button type="submit" class="btn btn-primary">Зарегистрироваться</button>
<?php ActiveForm::end(); ?>
Обратите внимание: поля профиля автоматически получат префикс "Profile" благодаря тому, что передается объект модели.
| Компонент | Описание | Пример использования |
|---|---|---|
| ActiveForm | Базовый класс для создания форм | ActiveForm::begin() |
| ActiveField | Отдельное поле формы | $form->field($model, 'attr') |
| DynamicModel | Модель без таблицы, для динамических данных | new DynamicModel(['field1', 'field2']) |
AJAX-валидация: не даем пользователю ошибиться
Для сложных форм важно проверять данные на лету. Yii2 поддерживает AJAX-валидацию из коробки. Нужно только включить её в настройках формы.
В представлении добавьте опцию enableAjaxValidation:
<?php $form = ActiveForm::begin([
'enableAjaxValidation' => true,
]); ?>
В контроллере обработайте AJAX-запрос:
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
Это позволит проверять каждое поле отдельно при потере фокуса. Очень удобно для длинных форм.
Кастомные виджеты: когда стандартных полей мало
Иногда нужно добавить нестандартный элемент. Например, выбор даты или загрузку файлов. В Yii2 есть множество расширений, но можно создать свой виджет.
Пример виджета для выбора цвета:
namespace app\widgets;
use yii\widgets\InputWidget;
class ColorPicker extends InputWidget
{
public function run()
{
return $this->render('color-picker', [
'model' => $this->model,
'attribute' => $this->attribute,
]);
}
}
В представлении виджета (views/widgets/color-picker.php):
<input type="color" id="<?= \yii\helpers\Html::getInputId($model, $attribute) ?>"
name="<?= \yii\helpers\Html::getInputName($model, $attribute) ?>"
value="<?= \yii\helpers\Html::getAttributeValue($model, $attribute) ?>">
Теперь можно использовать виджет в форме:
<?= $form->field($model, 'color')->widget(ColorPicker::class) ?>
Что в итоге
Сложные формы в Yii2 — это не страшно. Главное — разбивать задачу на части: использовать отдельные модели, динамические поля через JavaScript, AJAX-валидацию для удобства пользователя и кастомные виджеты для нестандартных элементов. Помните правило: не пытайтесь сделать все в одном файле. Дробите, структурируйте, и код станет понятным.
Надеюсь, эти примеры помогут вам в работе. Если остались вопросы — пишите в комментариях, я отвечу.
