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

К концу этой инструкции у вас будет форма регистрации: пользователь вводит имя и e-mail, Symfony проверяет данные, а корректная запись уходит в базу через Doctrine. На чистом проекте это занимает около 30–40 минут вместе с установкой зависимостей. Инструкция написана для Symfony 7.x (актуально и для 6.4 LTS — отличия отмечены отдельно).
Что нужно иметь до начала
Проверьте окружение, прежде чем создавать первую форму. На пятом шаге не хочется узнать, что не хватает пакета.
- PHP 8.2 или новее — минимальное требование Symfony 7. Проверьте командой
php -v. - Composer —
composer --versionдолжен что-то вернуть. - Установленный Symfony-проект с работающим маршрутизатором и Twig. Если проекта нет:
composer create-project symfony/skeleton myapp, затем внутри —composer require webapp. - Symfony CLI для локального сервера (необязательно, но удобно): команда
symfony serve. - Настроенное подключение к базе в файле
.env(переменнаяDATABASE_URL), если планируете сохранять данные.
Шаг 1. Установить компонент форм
В skeleton-проекте компонента форм нет по умолчанию. Ставим его вместе с валидатором:
composer require symfony/form symfony/validator
Заодно поставим maker-bundle — он генерирует классы форм за вас (только для разработки):
composer require --dev symfony/maker-bundle
Проверка: команда php bin/console list make покажет список генераторов, среди них будет make:form. Значит, пакеты на месте.
Шаг 2. Создать сущность для данных
Форма должна куда-то складывать данные. Создадим сущность Contact через maker:
php bin/console make:entity Contact
Maker спросит поля. Добавьте два: name (тип string, длина 255) и email (тип string, длина 255). Получится примерно такой класс (сокращённо, геттеры и сеттеры maker сгенерирует сам):
<?php
namespace App\Entity;
use App\Repository\ContactRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: ContactRepository::class)]
class Contact
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
#[Assert\NotBlank(message: 'Укажите имя')]
private ?string $name = null;
#[ORM\Column(length: 255)]
#[Assert\NotBlank(message: 'Укажите e-mail')]
#[Assert\Email(message: 'Некорректный e-mail')]
private ?string $email = null;
// геттеры и сеттеры сгенерированы maker'ом
}
Атрибуты #[Assert\...] я добавил вручную — это правила валидации из компонента symfony/validator. Они сработают, когда форма будет проверять данные.
Создайте и примените миграцию:
php bin/console make:migration
php bin/console doctrine:migrations:migrate
Проверка: в базе появится таблица contact с полями id, name, email.
Шаг 3. Сгенерировать класс формы
В Symfony форма — это отдельный класс, а не HTML в шаблоне. Так поля описываются в PHP и переиспользуются. Генерируем:
php bin/console make:form ContactType Contact
Первый аргумент — имя класса формы, второй — сущность, к которой она привязана. Получится файл src/Form/ContactType.php:
<?php
namespace App\Form;
use App\Entity\Contact;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ContactType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, [
'label' => 'Ваше имя',
])
->add('email', EmailType::class, [
'label' => 'E-mail',
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Contact::class,
]);
}
}
Обратите внимание: типы полей — это классы (TextType::class, EmailType::class), а не строки. Полный список встроенных типов есть в документации компонента форм.
Шаг 4. Обработать форму в контроллере
Создадим контроллер, который отдаёт форму и обрабатывает отправку:
php bin/console make:controller ContactController
Замените содержимое метода на следующее:
<?php
namespace App\Controller;
use App\Entity\Contact;
use App\Form\ContactType;
use Doctrine\ORM\EntityManagerInterface;
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, EntityManagerInterface $em): Response
{
$contact = new Contact();
$form = $this->createForm(ContactType::class, $contact);
// читает данные из запроса и переносит их в объект $contact
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($contact);
$em->flush();
$this->addFlash('success', 'Данные сохранены');
// редирект после POST, чтобы форма не отправилась повторно при F5
return $this->redirectToRoute('app_contact');
}
return $this->render('contact/index.html.twig', [
'form' => $form,
]);
}
}
ПорядокisSubmitted() && isValid()не случаен:isValid()имеет смысл только после того, как форма получила данные из запроса черезhandleRequest(). Проверять валидность неотправленной формы — логическая ошибка.
В Symfony 7 маршруты объявляются атрибутом #[Route] из пространства Symfony\Component\Routing\Attribute\Route. Если вы на 6.4 и код скопирован из старого гайда, там может быть Annotation\Route — проверьте импорт, иначе получите ошибку класса.
Шаг 5. Вывести форму в шаблоне
Откройте templates/contact/index.html.twig и выведите форму. Начните с самого простого варианта:
{% extends 'base.html.twig' %}
{% block body %}
{% for message in app.flashes('success') %}
<div class="flash">{{ message }}</div>
{% endfor %}
{{ form(form) }}
{% endblock %}
Функция form(form) отрисует все поля, метки, поля ошибок и тег <form> целиком. Если нужен контроль над разметкой, разбейте на части:
{{ form_start(form) }}
{{ form_row(form.name) }}
{{ form_row(form.email) }}
<button type="submit">Отправить</button>
{{ form_end(form) }}
Кнопку submit удобнее держать в шаблоне, а не в классе формы, — так одну форму можно выводить с разными кнопками.
Проверка: откройте /contact (при запущенном symfony serve — по адресу вида https://127.0.0.1:8000/contact). В браузере появятся два поля и кнопка. Отправьте пустую форму — под полями покажутся сообщения «Укажите имя» и «Укажите e-mail». Заполните корректно — увидите «Данные сохранены», а в таблице contact появится строка.
Если не получилось
- «Class "Symfony\Component\Form\..." not found» — не установлен компонент. Вернитесь к шагу 1 и выполните
composer require symfony/form. - Форма отображается, но валидация не срабатывает — не установлен
symfony/validatorлибо в атрибутах сущности опечатка в имени констрейнта. Проверьте импортuse Symfony\Component\Validator\Constraints as Assert;. - «The CSRF token is invalid» при отправке — истёк токен или отключены сессии. По умолчанию Symfony добавляет CSRF-защиту автоматически; убедитесь, что форма выводится через
form()/form_end(), а не вручную набранным HTML без скрытого поля токена. - «An exception occurred ... table contact doesn't exist» — не применена миграция. Выполните
php bin/console doctrine:migrations:migrate. - Данные не сохраняются, ошибок нет — забыли
$em->persist()и$em->flush()либо блок не выполняется, потому чтоisValid()вернул false. Загляните в профайлер (панель внизу страницы в dev-режиме, вкладка Forms) — там видно, какое поле не прошло проверку.
Что делать дальше
Форма работает — дальше стоит посмотреть в сторону встроенного профайлера: вкладка Forms показывает переданные значения, применённые констрейнты и результат валидации по каждому полю. Это первое, куда смотреть, когда форма ведёт себя не так, как ожидалось.
Следующие темы, которые пригодятся: типы полей ChoiceType и EntityType для выпадающих списков, вложенные формы через CollectionType, а также кастомизация вывода через form themes. Каждая — отдельный разбор; здесь мы собрали минимально рабочую форму от установки до записи в базу.
Проверка грамотности: Орфография, пунктуация, стиль — с объяснением каждой правки
Спросить за 10 ₽Источники: Symfony Docs — Forms, Symfony Docs — Validation
Частые вопросы
У меня Symfony 6.4, а не 7 — код подойдёт?
Обязательно ли привязывать форму к сущности Doctrine?
Куда добавлять правила валидации — в форму или в сущность?
Форма не сохраняет данные, но и ошибок не показывает — что делать?
Нужно ли самому добавлять защиту от CSRF?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.