Формы
Форму в Drupal описывают массивом, а не разметкой. По этому описанию система сама выводит поля, проверяет обязательные значения, защищает от подделки запроса и вызывает ваши методы проверки и обработки. Сделаем форму подписки на новые статьи блога.
Где лежит форма
Каждая форма — отдельный класс в папке src/Form модуля. Создайте папку и файл вручную.
Класс формы
// web/modules/custom/blog/src/Form/SubscribeForm.php
namespace Drupal\blog\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
class SubscribeForm extends FormBase
{
public function getFormId(): string
{
return 'blog_subscribe_form';
}
public function buildForm(array $form, FormStateInterface $form_state): array
{
$form['email'] = [
'#type' => 'email',
'#title' => $this->t('Почта'),
'#required' => TRUE,
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Подписаться'),
];
return $form;
}
public function submitForm(array &$form, FormStateInterface $form_state): void
{
$email = $form_state->getValue('email');
$this->messenger()->addStatus($this->t('Адрес @email подписан.', ['@email' => $email]));
}
}| Метод | Что делает |
|---|---|
getFormId() | Возвращает уникальное имя формы |
buildForm() | Описывает поля и кнопки |
submitForm() | Выполняется после успешной отправки |
Поля — те же массивы вывода со свойством #type. Значения отправленной формы хранит объект $form_state.
Маршрут формы
Форме не нужен контроллер: в маршруте вместо _controller указывают _form:
# web/modules/custom/blog/blog.routing.yml
blog.subscribe:
path: '/blog/subscribe'
defaults:
_form: '\Drupal\blog\Form\SubscribeForm'
_title: 'Подписка'
requirements:
_permission: 'access content'После drush cr откройте /blog/subscribe: появится поле и кнопка. Пустую форму браузер не отправит, а после отправки с адресом вверху страницы появится зелёное сообщение.
Проверка
Свои правила проверки пишут в методе validateForm():
// web/modules/custom/blog/src/Form/SubscribeForm.php
public function validateForm(array &$form, FormStateInterface $form_state): void
{
if (str_ends_with($form_state->getValue('email'), '@example.com')) {
$form_state->setErrorByName('email', $this->t('Укажите настоящий адрес.'));
}
}setErrorByName() отмечает поле с ошибкой: форма вернётся к посетителю с красным сообщением, а submitForm() не выполнится. Формат адреса и обязательность Drupal проверил сам — по #type и #required.
Элементы формы
| Тип | Поле |
|---|---|
textfield | Строка |
textarea | Многострочный текст |
email, tel, url, number | Поля с проверкой формата |
select | Список: варианты в #options |
checkbox, checkboxes, radios | Флажки и переключатели |
date | Дата |
submit | Кнопка отправки |
Общие свойства: #title, #description, #default_value, #required.
После отправки
// web/modules/custom/blog/src/Form/SubscribeForm.php
$this->messenger()->addStatus($this->t('Готово.'));
$this->messenger()->addError($this->t('Не удалось сохранить.'));
$form_state->setRedirect('blog.list');Сообщение показывается один раз — на следующей странице. setRedirect() отправляет посетителя на маршрут по имени; без него он останется на странице формы. Сохранять адрес в базу форма начнёт в уроке о базе данных.
Форма внутри страницы
Форму можно вывести как часть массива вывода в контроллере:
// web/modules/custom/blog/src/Controller/BlogController.php
$build['subscribe'] = $this->formBuilder()->getForm(\Drupal\blog\Form\SubscribeForm::class);Частая ошибка
Данные нельзя читать из $_POST: там значения до проверки и без защиты от подделки. Используйте $form_state->getValue('email') — в нём только поля, описанные в форме и прошедшие проверку. И не выводите форму обычными тегами HTML: без Form API у неё не будет токена защиты.