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

К концу этого гайда у вас будет работающая форма: пользователь заполняет поля, нажимает «Отправить», данные проходят валидацию и попадают в объект. Всё на Symfony 7.x с PHP 8.2+. Времени уйдёт примерно 30–40 минут, если проект уже развёрнут. Мы не будем разбирать, как устроен компонент Form внутри — только то, что нужно, чтобы получить результат.
Что нужно иметь до начала
Форму нельзя собрать в пустом проекте — компонент Form ставится отдельно, а рендер шаблона требует Twig. Проверьте перед стартом:
- PHP 8.2 или выше — команда
php -vпокажет версию; - установленный Composer;
- развёрнутый проект Symfony 7.x (можно создать через
symfony new my_project --webapp, флаг--webappсразу подтянет Form, Twig и валидатор); - если проект минимальный — доустановите пакеты вручную:
composer require symfony/form
composer require symfony/twig-bundle
composer require symfony/validator
Проверка: после установки в файле composer.json в секции require появятся строки symfony/form, symfony/twig-bundle и symfony/validator.
Форма в Symfony — это не HTML-разметка, а PHP-класс. Разметку генерирует Twig из этого класса. Именно поэтому одну и ту же форму можно отрисовать в трёх разных шаблонах, не трогая логику.
Шаги
-
Создайте класс формы. Symfony хранит формы в каталоге
src/Form. Проще всего сгенерировать заготовку командой мейкера (если установленsymfony/maker-bundle):php bin/console make:form ContactTypeЕсли мейкера нет — создайте файл
src/Form/ContactType.phpвручную:<?php // src/Form/ContactType.php // Symfony 7.x 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; class ContactType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void { $builder // имя поля, тип поля, опции ->add('name', TextType::class, [ 'label' => 'Ваше имя', ]) ->add('email', EmailType::class, [ 'label' => 'Электронная почта', ]) ->add('message', TextareaType::class, [ 'label' => 'Сообщение', ]) ->add('send', SubmitType::class, [ 'label' => 'Отправить', ]); } }Проверка: файл лежит в
src/Form/ContactType.php, класс называетсяContactType, PHP не ругается на синтаксис при запускеphp bin/console cache:clear. -
Постройте форму в контроллере. Контроллер создаёт форму из класса, передаёт запрос и решает, что делать с данными. Файл
src/Controller/ContactController.php:<?php // src/Controller/ContactController.php // Symfony 7.x 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(); // тут была бы отправка письма или запись в базу return $this->redirectToRoute('app_contact'); } return $this->render('contact/index.html.twig', [ 'contactForm' => $form, ]); } }Проверка: откройте
/contactв браузере — страница пока упадёт с ошибкой об отсутствующем шаблоне. Это нормально, шаблон создаём следующим шагом. -
Отрисуйте форму в шаблоне. Создайте файл
templates/contact/index.html.twig:{# templates/contact/index.html.twig #} {% extends 'base.html.twig' %} {% block body %} {{ form(contactForm) }} {% endblock %}Функция
form()рисует всю форму целиком: поля, метки и кнопку. Проверка: обновите/contact— появятся три поля и кнопка «Отправить». -
Добавьте валидацию. Пока форму можно отправить пустой. Ограничения задаются через опцию
constraintsпрямо в классе формы. Добавьте импорты и правила вContactType:use Symfony\Component\Validator\Constraints\Email; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; // внутри buildForm: ->add('name', TextType::class, [ 'label' => 'Ваше имя', 'constraints' => [ new NotBlank(message: 'Укажите имя'), new Length(min: 2, minMessage: 'Слишком короткое имя'), ], ]) ->add('email', EmailType::class, [ 'label' => 'Электронная почта', 'constraints' => [ new NotBlank(message: 'Укажите почту'), new Email(message: 'Некорректный адрес'), ], ])Проверка: отправьте форму с пустым полем имени — под полем появится «Укажите имя», данные в контроллер не попадут, потому что
isValid()вернётfalse.
Куда положить данные формы
В примере выше форма не привязана к объекту — getData() вернёт массив по именам полей. Если у вас есть класс данных (DTO или сущность Doctrine), передайте его вторым аргументом в createForm, и Symfony заполнит объект автоматически:
$contact = new Contact();
$form = $this->createForm(ContactType::class, $contact);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// $contact уже заполнен данными из формы
}
Чтобы связать форму с классом на уровне самой формы, добавьте в ContactType метод configureOptions с ключом data_class. Точное имя опции и её поведение сверяйте в разделе документации Symfony «Forms → Processing Forms».
Если не получилось
- «Unable to find template contact/index.html.twig» — шаблон не создан или лежит не в том каталоге. Twig ищет шаблоны в
templates/, путь вrender()считается от неё. - «Neither the property ... nor one of the methods ... exist» — имя поля в форме не совпадает со свойством класса данных, если вы передали объект в
createForm. Проверьте, что у объекта есть публичное свойство или геттер/сеттер с этим именем. - Валидация не срабатывает, форма проходит с пустыми полями — не установлен
symfony/validatorлибо ограничения заданы, но не импортированы нужные классыConstraints. Проверьте импорты в начале файла формы. - Ошибки не показываются в шаблоне — вы рендерите поля по отдельности через
form_widgetбезform_errors. Функцияform()целиком показывает ошибки сама; при ручном рендере добавляйте блок ошибок явно. - Страница просто перезагружается без реакции — забыли
$form->handleRequest($request)до проверкиisSubmitted(). Без него форма никогда не считается отправленной.
Что делать дальше
Форма работает — дальше по мере роста проекта смотрите в три стороны. Настройте CSRF-защиту (в Symfony она включена по умолчанию для форм, привязанных к сессии, — проверьте, что токен рендерится). Разберитесь с темами оформления форм под Bootstrap или Tailwind: Symfony умеет менять разметку через form_themes без правки классов формы. И вынесите бизнес-логику из контроллера в отдельный сервис, когда обработчик формы перестанет умещаться в один экран. Инвалидация и повторная отправка формы — отдельная тема, здесь мы её не трогали.
Задачи: математика, физика, химия: Разбор задачи по шагам с проверкой и похожей задачей
Спросить за 10 ₽Источники: Symfony Documentation — Forms, Symfony Documentation — Validation Constraints
Частые вопросы
У меня Symfony 6.4, а не 7.x — код подойдёт?
Обязательно ли использовать make:form?
Почему форма отправляется, но данные не сохраняются?
Как рендерить поля по отдельности, а не всю форму сразу?
Валидацию задавать в форме или в классе сущности?
Не появляется CSRF-токен, форма считается недоверенной — что делать?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.