Формы в Drupal: как собрать Form API и не потерять данные
Form API в Drupal отвечает не только за поля на странице: он связывает построение формы, проверку введённых данных и обработчик отправки. Показываем этот цикл на примере формы обратной связи и разбираем ошибки, из-за которых данные не доходят до обработчика.

В Drupal форма может выглядеть готовой, но не отправлять данные так, как вы ожидаете: поле не прошло проверку, обработчик не вызвался или после отправки пользователь увидел пустую страницу. В Drupal 10 и 11 Form API связывает три этапа — построение формы, валидацию и отправку. Разберём их на небольшой форме обратной связи и покажем, где искать причину сбоя.
Как устроен цикл Form API
Форму обычно описывают классом на основе FormBase. Drupal вызывает его методы в нужном порядке: buildForm() формирует массив элементов, методы проверки отсеивают некорректные значения, а submitForm() обрабатывает успешно отправленную форму. Вы не собираете HTML вручную и не должны самостоятельно разбирать обычный POST-запрос.
В Form API массив формы — не готовый HTML. Это описание полей и поведения, которое Drupal превращает в интерфейс и обрабатывает при отправке.
Для примера используем модуль с машинным именем feedback. Класс формы положите в web/modules/custom/feedback/src/Form/FeedbackForm.php, а маршрут объявите в web/modules/custom/feedback/feedback.routing.yml. Если корневая папка сайта у вас называется иначе, путь к модулю будет другим.
Минимальная форма обратной связи
Класс ниже совместим с Drupal 10 и 11. Он показывает обязательное текстовое поле, проверку длины сообщения и обработчик отправки. В рабочем проекте вместо вывода данных пользователю подключите нужное хранилище или очередь; сохранение здесь намеренно не реализовано.
<?php
namespace Drupal\feedback\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
final class FeedbackForm extends FormBase {
public function getFormId(): string {
return 'feedback_form';
}
public function buildForm(array $form, FormStateInterface $form_state): array {
$form['email'] = [
'#type' => 'email',
'#title' => $this->t('Электронная почта'),
'#required' => TRUE,
];
$form['message'] = [
'#type' => 'textarea',
'#title' => $this->t('Сообщение'),
'#required' => TRUE,
'#rows' => 5,
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Отправить'),
];
return $form;
}
public function validateForm(array &$form, FormStateInterface $form_state): void {
$message = trim($form_state->getValue('message'));
if (mb_strlen($message) < 10) {
$form_state->setErrorByName('message', $this->t('Введите сообщение длиной не менее 10 символов.'));
}
}
public function submitForm(array &$form, FormStateInterface $form_state): void {
$this->messenger()->addStatus($this->t('Форма отправлена.'));
}
}
getFormId() возвращает уникальный машинный идентификатор формы. Поля из buildForm() — элементы render array: ключ вроде #type задаёт тип элемента, а #required помечает его обязательным. validateForm() добавляет ошибку к конкретному полю; при ошибке Drupal снова показывает форму, а отправка не проходит обычный путь успешного submit. В submitForm() значения читают через объект состояния формы, а не из глобального массива запроса.
Подключаем форму маршрутом
Создайте файл feedback.routing.yml в корне модуля:
feedback.form:
path: '/feedback'
defaults:
_form: '\Drupal\feedback\Form\FeedbackForm'
_title: 'Обратная связь'
requirements:
_permission: 'access content'
После включения модуля и очистки кеша маршрут будет доступен по адресу /feedback пользователям с правом просмотра опубликованного содержимого. В примере ограничение задано явно: для формы с конфиденциальными данными выберите подходящее разрешение или создайте отдельное. Сама доступность страницы не должна автоматически означать право на просмотр сохранённых обращений.
Где чаще всего ломается форма
| Симптом | Что проверить |
|---|---|
| Страница отдаёт 404 | Имя файла и синтаксис маршрута, включён ли модуль; после изменения маршрута очистите кеш. |
| Drupal не находит класс формы | Совпадают ли пространство имён, имя класса и путь src/Form/FeedbackForm.php. |
| Не вызывается успешная отправка | Не показывает ли форма ошибку валидации; совпадают ли имена элементов и ключи, передаваемые в методы состояния. |
| Пользователь не может открыть страницу | Проверьте требование в маршруте и права текущей роли. Это может быть отказ доступа, а не ошибка формы. |
Не подменяйте проверку данных настройкой #required. Она помогает интерфейсу и стандартной обработке, но бизнес-правила — например, допустимый диапазон значения — задавайте явно и проверяйте на сервере. И не выводите пользовательский ввод напрямую как HTML: сохраняйте его через подходящий механизм Drupal и экранируйте при показе.
Что добавить, когда форма разрастается
Если форме нужны сторонние сервисы, не создавайте их вручную внутри обработчика. Для зависимости от сервиса используют внедрение зависимостей; способ создания класса в таком случае нужно согласовать с механизмом формирования форм Drupal. Если же вы меняете уже существующую форму другого модуля, обычно рассматривают form alter — это другой сценарий, не способ заменить собственный класс формы.
При работе с динамическими элементами и AJAX важны повторные построения формы: состояние и значения должны корректно переживать запросы, а обработчики — не выполнять побочные действия во время одного лишь построения. AJAX-обновление и хранение пользовательских данных здесь не рассматриваются — для них нужны отдельные решения и проверки.
Что сделать дальше
Сначала включите модуль, откройте маршрут и проверьте оба сценария: корректное сообщение проходит, короткое возвращает ошибку у поля. Затем решите, где и с какими правами хранить обращения. Перед выпуском проверьте форму под ролями, которые будут ей пользоваться, и отдельно протестируйте обработку повторной отправки. Для следующего шага откройте документацию Form API и сверяйте детали с версией Drupal вашего проекта.
Render array — массив, которым Drupal описывает элементы страницы и их свойства перед генерацией HTML. В форме его используют для полей, кнопок и настроек отображения.
Проверка рекламы на риски: Что в макете вызовет вопросы ФАС: превосходные степени, сравнения, обещания результата, маркировка.
Спросить за 40 ₽Источники: Drupal Form API, FormBase, Drupal API 11.x
Частые вопросы
Зачем форме нужен getFormId()?
Почему submitForm() не срабатывает?
Достаточно ли поставить #required для обязательного поля?
Можно ли сохранять данные формы прямо в submitForm()?
Будет ли этот пример работать в другой версии Drupal?
Что проверять, если маршрут возвращает 404?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.