Формы в Symfony 7: короткое руководство для новичка
Соберём форму обратной связи на Symfony 7: класс формы, рендер в шаблоне Twig, обработка отправки и валидация. Займёт около получаса, если проект уже поднят.

За полчаса вы сделаете рабочую форму обратной связи на Symfony 7.x: класс формы, вывод в шаблоне, обработку отправки в контроллере и проверку введённых данных. В конце нажатие на кнопку будет сохранять сообщение или показывать ошибки прямо под полями, а не в белом экране с исключением.
Что нужно до начала
Форма — не изолированный кусок кода, ей нужен собранный проект и пара пакетов. Проверьте, что у вас есть:
- PHP 8.2 или новее — Symfony 7.x ниже не запустится;
- Composer и уже созданный проект Symfony 7.x (
symfony new my-app --webappставит сразу всё нужное); - пакет форм и валидатор. Если ставили проект флагом
--webapp, они уже на месте. Если нет — доставьте вручную; - шаблонизатор Twig (в
--webappтоже входит).
Доставить форму и валидатор в минимальную сборку:
composer require symfony/form symfony/validatorПроверка: команда php bin/console debug:container form.factory должна показать зарегистрированный сервис, а не ошибку про отсутствие определения.
Шаг 1. Создаём класс формы
В Symfony форма — это отдельный класс, а не разметка в шаблоне. Так одну и ту же форму можно выводить в нескольких местах и тестировать без браузера. Сгенерируйте заготовку командой:
php bin/console make:form ContactTypeКонсоль спросит имя класса данных — для простой формы обратной связи нажмите Enter, оставив поле пустым: данные придут просто массивом. Откройте созданный файл src/Form/ContactType.php и опишите три поля:
<?php
// src/Form/ContactType.php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
class ContactType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
// Имя отправителя, обязательное поле
->add('name', TextType::class, [
'label' => 'Ваше имя',
'constraints' => [
new NotBlank(message: 'Укажите имя'),
new Length(min: 2, max: 100),
],
])
->add('email', EmailType::class, [
'label' => 'E-mail для ответа',
'constraints' => [
new NotBlank(message: 'Укажите e-mail'),
new Email(message: 'Похоже, e-mail введён с ошибкой'),
],
])
->add('message', TextareaType::class, [
'label' => 'Сообщение',
'constraints' => [
new NotBlank(message: 'Напишите текст сообщения'),
new Length(min: 10, max: 2000),
],
])
->add('send', SubmitType::class, [
'label' => 'Отправить',
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
// Данные придут массивом; CSRF-защита включена по умолчанию
$resolver->setDefaults([
'data_class' => null,
]);
}
}Проверка: файл сохранился без синтаксических ошибок, php bin/console lint:container не ругается.
Про ограничения прямо в форме
Здесь валидацию мы задали через опцию constraints у каждого поля. Это допустимо для форм без класса-сущности. Если бы за формой стоял объект (например, Doctrine-сущность), ограничения обычно вешают атрибутами на свойства класса, а в поле формы их не дублируют.
Шаг 2. Контроллер: показать и обработать
Один и тот же метод и рисует форму, и принимает отправку — Symfony различает случаи по методу запроса. Создайте контроллер:
php bin/console make:controller ContactControllerПриведите метод к такому виду:
<?php
// src/Controller/ContactController.php
namespace App\Controller;
use App\Form\ContactType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ContactController extends AbstractController
{
#[Route('/contact', name: 'app_contact')]
public function index(Request $request): Response
{
$form = $this->createForm(ContactType::class);
// Считываем данные запроса в форму
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
// Здесь: сохранить, отправить письмо и т.д.
// Пока просто покажем сообщение об успехе
$this->addFlash('success', 'Сообщение отправлено');
// Редирект после POST, чтобы форма не отправилась повторно
return $this->redirectToRoute('app_contact');
}
return $this->render('contact/index.html.twig', [
'form' => $form,
]);
}
}Обратите внимание на редирект после успешной отправки. Без него обновление страницы браузером повторит POST и отправит форму заново. Это шаблон PRG (Post/Redirect/Get), и в Symfony его делают именно так.
Проверка: откройте /contact — страница загружается без исключения (шаблон мы создадим на следующем шаге, пока может быть ошибка про отсутствие файла — это нормально).
Шаг 3. Шаблон Twig
Symfony умеет отрисовать всю форму одной функцией. Откройте templates/contact/index.html.twig и замените содержимое:
{% extends 'base.html.twig' %}
{% block body %}
<h1>Обратная связь</h1>
{% for message in app.flashes('success') %}
<div class="flash-success">{{ message }}</div>
{% endfor %}
{{ form(form) }}
{% endblock %}Функция form(form) выводит поля, метки, ошибки валидации и скрытое поле CSRF-токена. Если нужно управлять версткой каждого поля отдельно, используют form_start, form_row и form_end — но для первой формы хватит и одной строки.
Проверка: обновите /contact. Появятся три поля и кнопка «Отправить». Отправьте пустую форму — под полями возникнут сообщения «Укажите имя» и остальные. Заполните корректно — увидите зелёную плашку об успехе.
Форма в Symfony — это класс, а не разметка. Как только вы приняли эту идею, валидация, CSRF и повторный вывод перестают быть вашей заботой: они уже внутри.
Если не получилось
- «Unable to find template contact/index.html.twig» — не создан или неправильно назван файл шаблона. Проверьте путь:
templates/contact/index.html.twig, регистр важен. - «The CSRF token is invalid» при отправке — обычно из-за того, что форму вывели вручную и забыли токен, либо сессия не работает. При выводе через
{{ form(form) }}токен добавляется сам; не удаляйте скрытое поле_token. - Форма отображается, но ошибки валидации не появляются — не установлен
symfony/validatorлибо ограничения заданы, но не подключены. Проверьтеcomposer require symfony/validatorи что constraints стоят в поле формы. - «Neither the property ... nor one of the methods ... exist» — вы указали
data_classс сущностью, у которой нет свойства с именем поля. Для формы без класса оставьте'data_class' => null. - Данные приходят пустыми в
getData()— забыли вызвать$form->handleRequest($request)до проверкиisSubmitted().
Что делать дальше
Рабочая форма есть. Следующие шаги, когда понадобятся:
- привяжите форму к Doctrine-сущности через
data_classи переносите ограничения валидации на свойства класса атрибутами; - подключите отправку письма через компонент Symfony Mailer вместо флеш-сообщения;
- если делаете кастомную вёрстку — почитайте раздел документации про form theming и функции
form_row,form_widget,form_errors; - для повторяющихся наборов полей смотрите вложенные формы и
CollectionType— это отдельная тема, здесь мы её не разбирали.
Репетитор 1—11 класс и ВУЗ: Математика и физика методом наведения на мысль, помощь с ДЗ.
Спросить за 8 ₽Источники: Symfony Forms — официальная документация, Symfony Validation — официальная документация
Частые вопросы
У меня Symfony 6.4, код подойдёт?
Обязательно ли создавать отдельный класс формы?
Почему после успешной отправки нужен редирект?
Валидация не срабатывает, хотя constraints прописаны — в чём дело?
Можно ли выводить поля по отдельности, а не одной строкой form(form)?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.