Дата обновления перевода: 2021-06-01

Формы

Screencast

Предпочитаете видео-уроки? Посмотрите Symfony Forms screencast series.

Создание и обработка HTML форм - это сложно и монотонно. Вам нужно разбираться с отображением полей HTML формы, валидацией отправленных данных, отображением данных формы в объектах и многим другим. Symfony содержит мощную функциб форм, которая предоставляет все эти, и многие другие, возможности для действительно сложных сценариев.

Установка

В приложениях, использующих Symfony Flex, запустите эту команду, чтобы установить функцию формы перед её использованием:

1
$ composer require symfony/form

Note

Компонент Symfony для работы с формами - это независимая библиотека, которая может быть использована вне проектов Symfony. Подробности ищите по ссылке Компонент по работе с формами на GitHub.

Использование

Рекомендованный рабочий процесс при работе с формами Symfony, следующий:

  1. Создайте форму в контроллере Symfony или используя cпециальный класс формы;
  2. Отобразите форму в шаблоне, чтобы пользователь мог редактировать и отправлять ее;
  3. Обработайте форму, чтобы валидировать отправленные данные, преобразовать их в PHP-данные и сделать что-то с ними (например, сохранить в базе данных).

Каждый из этих шагов подробно разъясняется в следующих разделах. Чтобы сделать примеры более простыми для понимания, все из них предполагают, что вы создаете небольшое приложение списка дел, отображающее “задачи”.

Пользователи создают и редактируют задачи используя формы Symfony. Каждая задача - это экземпляр следующего класса Task:

// src/Entity/Task.php
namespace App\Entity;

class Task
{
    protected $task;
    protected $dueDate;

    public function getTask(): string
    {
        return $this->task;
    }

    public function setTask(string $task): void
    {
        $this->task = $task;
    }

    public function getDueDate(): ?\DateTime
    {
        return $this->dueDate;
    }

    public function setDueDate(?\DateTime $dueDate): void
    {
        $this->dueDate = $dueDate;
    }
}

Этот класс представляет собой обычный PHP-объект, так как на данный момент он не имеет ничего общего с Symfony или какой-либо другой библиотекой. Это обычный PHP-объект, который выполняет задачу непосредственно внутри вашего приложения (т.е. необходимость представления задачи в вашем приложении). Но вы можете редактировать сущности Doctrine таким же образом.

Типы формы

Прежде чем создавать свою первую форму Symfony, важно понимать концепцию “типа формы”. В других проектах часто различают “формы” и “поля формы”. В Symfony же все это - “типы формы”:

  • одно поле формы <input type="text"> - это “тип формы” (например, TextType);
  • группа нескольких HTML-полей, используемая для ввода почтового адреса - это “тип формы” (например, PostalAddressType);
  • целая <form> с множеством полей для редактирования профиля пользователя - это “тип формы” (например, UserProfileType).

Вначале это может показаться запутанным, но вскоре станет естественным. Кроме того, это упрощает код и делает “компоновку” и “встраивание” полей формы намного проще в реализации.

Существуют десятки типов формы, предоставленных Symfony и вы также можете создавать собственные типы формы.

Создание формы

Symfony предоставляет объект “конструктора формы”, который позволяет вам описывать поля формы, используя свободный интерфейс. Позже, этот конструктор создает реальный объект формы, используемый для отображения и обработки содержания.

Создание формы в контроллере

Если ваш контроллер расширяется из AbstractController, используйте помощник createFormBuilder():

// src/Controller/TaskController.php
namespace App\Controller;

use App\Entity\Task;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class TaskController extends AbstractController
{
    public function new(Request $request): Response
    {
        // создает объект задачи и инициализирует некоторые данные для этого примера
        $task = new Task();
        $task->setTask('Write a blog post');
        $task->setDueDate(new \DateTime('tomorrow'));

        $form = $this->createFormBuilder($task)
            ->add('task', TextType::class)
            ->add('dueDate', DateType::class)
            ->add('save', SubmitType::class, ['label' => 'Create Task'])
            ->getForm();

        // ...
    }
}

Если ваш контроллер не расширяется из AbstractController, вам нужно будет получить сервисы в вашем контроллере и использовать метод createBuilder() сервиса form.factory.

В этом примере вы добавили к вашей форме 2 поля - task и dueDate - соответствующих свойствам task и dueDate класса Task. Вы уже каждому тип формы (например, TextType и DateType), представленных их полным именем класса. Наконец, вы добавили кнопку отправки с пользовательским ярлыкм, для отправи формы серверу.

Создание классов формы

Symfony рекомендует размещать минимально возможное количество логики в контроллерах. Вот почему лучше перемещать сложные формы в посвященные им классы, вместо того, чтобы определять их в действиях контроллера. Кроме того, формы, определенные в классах, могут быть использованы повторно в нескольких действиях и сервисах.

Классы формы - это типы формы, которые реализуют FormTypeInterface. Однако, лучше расширять из AbstractType, который уже реализует интерфейс и предоставляет некоторые утилиты:

// src/Form/Type/TaskType.php
namespace App\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;

class TaskType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('task', TextType::class)
            ->add('dueDate', DateType::class)
            ->add('save', SubmitType::class)
        ;
    }
}

Tip

Установите в своем проекте MakerBundle, чтобы генерировать классы формы используя команды make:form и make:registration-form.

Класс формы содержит все необходимые инструкции для создания формы задач. В контроллерах, расширяющихся из AbstractController, используйте помощник createForm() (в других случаях используйте create(), метод сервиса form.factory):

// src/Controller/TaskController.php
namespace App\Controller;

use App\Form\Type\TaskType;
// ...

class TaskController extends AbstractController
{
    public function new(): Response
    {
        // создает объект задачи и инициализирует некоторые данные для этого примера
        $task = new Task();
        $task->setTask('Write a blog post');
        $task->setDueDate(new \DateTime('tomorrow'));

        $form = $this->createForm(TaskType::class, $task);

        // ...
    }
}

Каждая форма должна знать имя класса, содержащего основополагающие данные (например, App\Entity\Task). Обычно, это угадывается на основе объекта, переданного второму аргементу к createForm() (т.е. $task). Позже, когда вы начнете встраивать формы, этого уже не будет достаточно.

Поэтому, хотя это и не всегда необходимо, обычно хорошей идеей будет яdно указать опцию data_class, добавив следующее к вашему классу типа формы:

// src/Form/Type/TaskType.php
namespace App\Form\Type;

use App\Entity\Task;
use Symfony\Component\OptionsResolver\OptionsResolver;
// ...

class TaskType extends AbstractType
{
    // ...

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Task::class,
        ]);
    }
}

Отображение формы

Теперь, когда форма была создана, следующий шаг - отобразить ее. Вместо того, чтобы передавать весь объект формы шаблону, используйте метод createView(), чтобы построить другей объект с визуальным представлением формы:

// src/Controller/TaskController.php
namespace App\Controller;

use App\Entity\Task;
use App\Form\Type\TaskType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class TaskController extends AbstractController
{
    public function new(Request $request): Response
    {
        $task = new Task();
        // ...

        $form = $this->createForm(TaskType::class, $task);

        return $this->render('task/new.html.twig', [
            'form' => $form->createView(),
        ]);
    }
}

Затем, используйте какие-то функции помощников формы, чтобы отобразить содержание формы:

1
2
{# templates/task/new.html.twig #}
{{ form(form) }}

Вот и все! Функция form() отображает все поля и стартовые и конечные теги``<form>``. По умолчанию, метод формы - POST и целевой URL такой же, который отображал форму, но вы можете изменить оба этих пункта.

Заметьте, как отображенное поле ввода task имеет значение свойства task из объекта $task (т.е. “Написать пост в блоге”). Это первая задача формы: брать данные из объекта и переводить их в формат, подходящий для отображения в HTML-форме.

Tip

Система форм достаточно умна, чтобы получить доступ к значению защищенного свойства task через методы getTask() и setTask() класса Task. Кром случаев, когда свойство публично, оно должно иметь методы “getter” и “setter”, чтобы Symfony могла получать и размещать данные в свойстве. Для булевого свойства, вы можете использовать метод “isser” или “hasser” (например, isPublished() или hasReminder()) вместо геттера (например, getPublished() или getReminder()).

Несмотря на то, что это отображение короткое, оно не очень гибкое. Обычно вам нужно будет иметь больше контроля над тем, как выглядит вся форма или некоторые ее поля. Например, благодаря интеграции Bootstrap 4 с формами Symfony, вы можете установить эту опцию, чтобы сгенерировать формы, совместимые с CSS-фреймворком Bootstrap 4:

  • YAML
    1
    2
    3
    # config/packages/twig.yaml
    twig:
        form_themes: ['bootstrap_4_layout.html.twig']
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    <!-- config/packages/twig.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:twig="http://symfony.com/schema/dic/twig"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            https://symfony.com/schema/dic/services/services-1.0.xsd
            http://symfony.com/schema/dic/twig
            https://symfony.com/schema/dic/twig/twig-1.0.xsd">
    
        <twig:config>
            <twig:form-theme>bootstrap_4_layout.html.twig</twig:form-theme>
            <!-- ... -->
        </twig:config>
    </container>
    
  • PHP
    1
    2
    3
    4
    5
    6
    7
    8
    // config/packages/twig.php
    use Symfony\Config\TwigConfig;
    
    return static function (TwigConfig $twig) {
        $twig->formThemes(['bootstrap_4_layout.html.twig']);
    
        // ...
    };
    

Встроенные темы формы Symfony включают в себя Bootstrap 3 и 4, а также Foundation 5 и 6. Вы также можете создать собственную тему формы Symfony.

В дополнение к темам формы, Symfony позволяет вам настраивать то, как отображаются поля со множеством функций для отображения каждой части поля отдельно (виджеты, ярлыки, ошибки, сообщения помощи, и т.д.)

Обработка формы

Рекомендованный способ обработки формы - это использование действия как для отображения формы, так и для обработки ее отправки. Вы можете использовать отдельные действия, но использование одного все упрощает и в то же время делает код компактным и легко изменяемым.

Обработка формы означает перевод данных, отправленных пользователем, в свойства объекта. Чтобы это произошло, отправленные данные пользователя должны быть записаны в объект формы:

// src/Controller/TaskController.php

// ...
use Symfony\Component\HttpFoundation\Request;

class TaskController extends AbstractController
{
    public function new(Request $request): Response
    {
        // просто настройте свежий объект $task (удалите данные примера)
        $task = new Task();

        $form = $this->createForm(TaskType::class, $task);

        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            // $form->getData() holds the submitted values
            // но изначальная переменная `$task` также была обновлена
            $task = $form->getData();

            // ... выполните какое-то действие, например сохраните задачу в базу данных
            // for example, if Task is a Doctrine entity, save it!
            // $entityManager = $this->getDoctrine()->getManager();
            // $entityManager->persist($task);
            // $entityManager->flush();

            return $this->redirectToRoute('task_success');
        }

        return $this->render('task/new.html.twig', [
            'form' => $form->createView(),
        ]);
    }
}

Контроллер следует общему паттерну для обработки форм, и имеет три возможных пути:

  1. При изначальной загрузке страницы в браузере, форма еще не была отправлена, и $form->isSubmitted() возвращает false. Таким образом, форма создается и отображается;
  2. Когда пользователь отправляет форму, handleRequest() распознает это и немедленно пишет отправленные данные обратно в свойства task и dueDate объекта $task. Затем этот объект валидируется (валидация разъясняется в следующем разделе). Если он не валиден, isValid() возвращает false и форма отображается снова, но теперь с ошибками валидации;
  3. Когда пользователь отправляет форму с валидыми данными, отправленные данные снова записываются в форму, но в этот раз isValid() возвращает true. Теперь у вас есть возможность выполнить какие-то действия, используя объект $task (например, сохранение в базу данных), прежде чем перенаправлять пользователя на какую-то другую страницу (например, страницу “спасибо” или “успешно”);

Note

Перенаправление пользователя после успешной отправки формы - лучшая практика, которая предотвращает пользователя от нажатия кнопки “Обновить” в их браузере и повторной отправки данных.

Caution

Метод createView() должен быть вызван после вызова handleRequest(). В других случаях, при использовании событий формы, изменения, сделанные в событиях *_SUBMIT, не будут применены к просмотру (вроде ошибок валидации).

See also

Если вам нужно больше контроля над тем, как ваша форма отпавляется, или какие данные передаются ей, вы можете использовать метод submit() для обработки отправки формы.

Tip

Если вам нужно отобразить и обработать одну и ту же форму в разных шаболнах, используйте функцию render(), чтобы встроить контроллер, который обрабатывает форму:

1
{{ render(controller('App\\Controller\\TaskController::new')) }}

Валидация формы

В предыдущем разделе вы узнали, как форма может быть отправлена с валидными или не валидными данными. В Symfony валидация применяется к объекту, лежащему в основе формы (например, Task). Другими словами, вопрос не в том, валидна ли «форма», а валиден ли объект $task, после того как форма передала ему отправленные данные. Вызов метода $form->isValid() – это сокращение, которое спрашивает объект $task валидны ваши данные или нет.

До использования валидации, добавьте её поддержку в ваше приложение:

1
$ composer require symfony/validator

Валидация проводится путем добавления к классу набора правил, под названием (валидация) ограничения. Вы можете добавить их либо к классу сущности, либо к классу формы.

Чтобы увидеть первый подход - добавление ограничений к сущности - в действии, добавьте ограничения валидации, так, чтобы поля task и dueDate не могли быть пустыми, и последнее было валидным объектом DateTime.

  • Annotations
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // src/Entity/Task.php
    namespace App\Entity;
    
    use Symfony\Component\Validator\Constraints as Assert;
    
    class Task
    {
        /**
         * @Assert\NotBlank
         */
        public $task;
    
        /**
         * @Assert\NotBlank
         * @Assert\Type("\DateTime")
         */
        protected $dueDate;
    }
    
  • Attributes
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    // src/Entity/Task.php
    namespace App\Entity;
    
    use Symfony\Component\Validator\Constraints as Assert;
    
    class Task
    {
        #[Assert\NotBlank]
        public $task;
    
        #[Assert\NotBlank]
        #[Assert\Type(\DateTime::class)]
        protected $dueDate;
    }
    
  • YAML
    1
    2
    3
    4
    5
    6
    7
    8
    # config/validator/validation.yaml
    App\Entity\Task:
        properties:
            task:
                - NotBlank: ~
            dueDate:
                - NotBlank: ~
                - Type: \DateTime
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    <!-- config/validator/validation.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping
            https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
    
        <class name="App\Entity\Task">
            <property name="task">
                <constraint name="NotBlank"/>
            </property>
            <property name="dueDate">
                <constraint name="NotBlank"/>
                <constraint name="Type">\DateTime</constraint>
            </property>
        </class>
    </constraint-mapping>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // src/Entity/Task.php
    namespace App\Entity;
    
    use Symfony\Component\Validator\Constraints\NotBlank;
    use Symfony\Component\Validator\Constraints\Type;
    use Symfony\Component\Validator\Mapping\ClassMetadata;
    
    class Task
    {
        // ...
    
        public static function loadValidatorMetadata(ClassMetadata $metadata): void
        {
            $metadata->addPropertyConstraint('task', new NotBlank());
    
            $metadata->addPropertyConstraint('dueDate', new NotBlank());
            $metadata->addPropertyConstraint(
                'dueDate',
                new Type(\DateTime::class)
            );
        }
    }
    

Это всё! Если вы повторно отправите форму с не валидными данными, вы увидите, что соответствующие ошибки будут отображены в форме.

Чтобы увидеть второй подход - добавление ограничений к форме - и чтобы узнать больше об ограничениях валидации, пожалуйста, обратитесь к документации валидации Symfony.

Сообщения валидации формы

New in version 5.2: Опция legacy_error_messages была представлена в Symfony 5.2

Типы формы имеют сообщения ошибок по умолчанию, которые более чистые и дружелюбные по отношению к пользователю, чем те, что предоставлены ограничениями валидации. Чтобы включит эти новые сообщения, установите опуцию legacy_error_messages как false:

  • YAML
    1
    2
    3
    4
    # config/packages/framework.yaml
    framework:
        form:
            legacy_error_messages: false
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    <!-- config/packages/framework.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:framework="http://symfony.com/schema/dic/symfony"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            https://symfony.com/schema/dic/services/services-1.0.xsd
            http://symfony.com/schema/dic/symfony
            https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
    
        <framework:config>
            <framework:form legacy-error-messages="false"/>
        </framework:config>
    </container>
    
  • PHP
    1
    2
    3
    4
    5
    6
    // config/packages/framework.php
    use Symfony\Config\FrameworkConfig;
    
    return static function (FrameworkConfig $framework) {
        $framework->form()->legacyErrorMessages(false);
    };
    

Другие распространенные функции формы

Передача опций форме

Если вы создаете формы в классах, при создании формы в контроллере, вы можете передать ей пользовательсткие опции в качестве третьего необязательного аргумента createForm():

// src/Controller/TaskController.php
namespace App\Controller;

use App\Form\Type\TaskType;
// ...

class TaskController extends AbstractController
{
    public function new(): Response
    {
        $task = new Task();
        // используйте некоторую PHP-логику, чтобы решить, обязательно ли это поле формы
        $dueDateIsRequired = ...;

        $form = $this->createForm(TaskType::class, $task, [
            'require_due_date' => $dueDateIsRequired,
        ]);

        // ...
    }
}

Если вы попробуете использовать форму сейчас, вы увидите сообщение об ошибке: Опция “require_due_date” не существует. Это потому что формы должны заявлять обо всех опциях, которые они принимают, используя метод configureOptions():

// src/Form/Type/TaskType.php
namespace App\Form\Type;

use Symfony\Component\OptionsResolver\OptionsResolver;
// ...

class TaskType extends AbstractType
{
    // ...

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            // ...,
            'require_due_date' => false,
        ]);

        // вы также можете определить позволенные типы, значения и
        // любые другие функции, поддерживающиеся компонентом OptionsResolver
        $resolver->setAllowedTypes('require_due_date', 'bool');
    }
}

Теперь вы можете использовать эту новую опцию формы внутри метода buildForm():

// src/Form/Type/TaskType.php
namespace App\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;

class TaskType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            // ...
            ->add('dueDate', DateType::class, [
                'required' => $options['require_due_date'],
            ])
        ;
    }

    // ...
}

Опции типа формы

Каждый тип формы имеет ряд опций для его конфигурации, как объясняется в справочнике типов формы Symfony. Две наиболее ипользуемые опции - это required и label.

Опция required

Наиболее распространенной опцией является required, которая может прменяться к любому полю. По умолчанию, эта опция установлена как true, что означает, что готовые к HTML5 браузеры будут требовать заполнения всех полей перед отправкой формы.

Если вы не хотите такого поведения, то либо отключите валидацию клиентской стороны для всей формы, либо установите опцию required как false в одном или более полях:

->add('dueDate', DateType::class, [
    'required' => false,
])

Опция required не выполняет валидацию серверской стороны. Если пользователь отправляет пустое значение поля (либо в старом браузере, либо веб-сервисе, к примеру), оно будет принято как валидное значение, разве что вы также не используете ограничения валидации Symfony NotBlank или NotNull.

Опция label

По умолчанию, ярлык полей формы - это очеловеченная версия имени свойства (user -> User; postalAddress -> Postal Address). Установите опцию label в полях, чтобы явно определить их ярлыки:

->add('dueDate', DateType::class, [
    // установите как FALSE, чтобы не отображать ярлык для этого поля
    'label' => 'To Be Completed Before',
])

Tip

По умолчанию, теги <label> обязательных полей отображаются с CSS-классом required, поэтому вы можете отобразить звездочку, применив CSS-стиль:

1
2
3
label.required:before {
    content: "*";
}

Изменение действия и HTTP-метода

По умолчанию, форма будет отправлена через запрос HTTP POST по тому же URL, под которым отображалась форма. При создании формы в контроллере, используйте методы setAction() и setMethod(), чтобы изменить это:

// src/Controller/TaskController.php
namespace App\Controller;

// ...
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;

class TaskController extends AbstractController
{
    public function new(): Response
    {
        // ...

        $form = $this->createFormBuilder($task)
            ->setAction($this->generateUrl('target_route'))
            ->setMethod('GET')
            // ...
            ->getForm();

        // ...
    }
}

При создании формы в классе, передайте действие и метод в виде опций формы:

// src/Controller/TaskController.php
namespace App\Controller;

use App\Form\TaskType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
// ...

class TaskController extends AbstractController
{
    public function new(): Response
    {
        // ...

        $form = $this->createForm(TaskType::class, $task, [
            'action' => $this->generateUrl('target_route'),
            'method' => 'GET',
        ]);

        // ...
    }
}

Наконец, вы можете пеоепределить действие в шаблоне, передав их функциям помощников form() или form_start():

1
2
{# templates/task/new.html.twig #}
{{ form_start(form, {'action': path('target_route'), 'method': 'GET'}) }}

Note

Если метод формы не GET или POST, а PUT, PATCH или DELETE, Symfony внедрит скрытое поле с именем _method, которое хранит этот метод. Форма будет отправлена в нормальнои запросе POST, но маршрутизация Symfony способна обнаружить параметр _method и будет интерпретировать его как запрос PUT, PATCH или DELETE. Опция конфигурации-framework-http_method_override должна быть включена, чтобы это работало.

Изменение имени формы

Если вы исследуете HTML-содержание отображенной формы, вы увидите, что имя <form> и имена полей генерируются из имени класса типа (например, <form name="task" ...> и <select name="task[dueDate][date][month]" ...>).

Если вы хотите изменить это, используйте метод createNamed():

// src/Controller/TaskController.php
namespace App\Controller;

use App\Form\TaskType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
// ...

class TaskController extends AbstractController
{
    public function new(): Response
    {
        $task = ...;
        $form = $this->get('form.factory')->createNamed('my_name', TaskType::class, $task);

        // ...
    }
}

Вы даже можете полностью избавиться от имени, установив пустую строку.

HTML-валидация клиентской стороны

Благодаря HTML5, многие браузеры могут нативно устанавливать определенные ограничения валидации клиентской стороны. Наиболее распространенная валидация активируется путем добавления атрибута required в обязательные поля. Для браузеров, поддерживающих HTML5, это приведет к отображению родного сообщения браузера, если пользователь попробует отправить форму с таким пустым полем.

Сгенерированные формы вовсю пользуются этой новой фнукцией, добавляя правильные HTML-атрибуты, которые триггерят валидацию. Валидация клиентской стороны, однако, может быть отключена, путем добавления атрибута novalidate к тегу <form> или formnovalidate к тегу отправки. Это особенно полезно, если вы хотите протестировать ограничения валидации клиентское стороны, но ваш браузер не позволяет вам, к примеру, отправить пустые поля.

1
2
3
4
{# templates/task/new.html.twig #}
{{ form_start(form, {'attr': {'novalidate': 'novalidate'}}) }}
    {{ form_widget(form) }}
{{ form_end(form) }}

Угадывание типа формы

Если объект, обрабатываемый формой, имеет ограничения валидации, Symfony может исследовать мтеоды, чтобы угадать типа вашего поля и настроить его для вас. В примере выше, Symfony может угадать по правилам валидации, что поле task - это нормальное поле TextType, а поле dueDate - это поле DateType.

При создании формы, опустите второй аргумент метода add(), или передайте ему null, чтобы включить “механизм угадывания” Symfony:

// src/Form/Type/TaskType.php
namespace App\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;

class TaskType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            // если вы не определите опции поля, вы можете опустить второй аргумент
            ->add('task')
            // если вы определите опции поля, передайте NULL в качестве второго аргумента
            ->add('dueDate', null, ['required' => false])
            ->add('save', SubmitType::class)
        ;
    }
}

Caution

При использовании определенной группы валидации формы, угадыватель типа поля начнет рассматривать все ограничения валидации при угадывании типов ваших полей (включая ограничения, которые не являются частью используемых(ой) групп(ы) валидации).

Опции угадывания типа формы

Когда для какого-то поля включены механизмы угадывания (т.е. вы опускаете или передаете null в качестве второго аргумента add()), в дополнение к типу формы, могут быть угаданы следующие опции:

required
Опция required может быть угадана на основании правил валидации (т.е. является ли поле NotBlank или NotNull) или метаданных Doctrine (т.е. является ли поле nullable). Это очень полезно, так как валидация вашей клиентской стороны будет автоматически соответствовать вашим правилам валидации.
maxlength
Если поле является некоторым видом текстового поля, то может быть угадан атрибут опции maxlength из ограничений валидации (используется ли Length или Range) или из метаданных Doctrine (через длину поля).

Если вы хотите изменить одно из угаданных значений, переопределите его, передав опцию в массиве опций поля:

->add('task', null, ['attr' => ['maxlength' => 4]])

See also

Кроме угадывания типа формы, Symfony также угадывает ограничения валидации если вы используете сущность Doctrine. Прочтите руководство Валидация объектов, чтобы узнать больше информации.

Неотображенные поля

При редактировании объекта через форму, все поля формы считаются свойствами объекта. Любые поля формы, не существующие в объекте, будут вызывать исключения.

Если вам нужны дополнительные поля в форме, которые не будут храниться в объекте (например, чтобы добавить флажок “Я согласен с этими условями” checkbox), установите опцию как false в этих полях:

// ...
use Symfony\Component\Form\FormBuilderInterface;

class TaskType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('task')
            ->add('dueDate')
            ->add('agreeTerms', CheckboxType::class, ['mapped' => false])
            ->add('save', SubmitType::class)
        ;
    }
}

Эти “неотображенные поля” могут быть установлены и доступны в контроллере с помощью:

$form->get('agreeTerms')->getData();
$form->get('agreeTerms')->setData(true);

Кроме того, если в форме есть поля, которые не добавлены в отправленные данные, эти поля будут явно установлены как null.

Узнать больше

При создании форм, помните, что первоочередной целью формы является перевод данных из объекта (Task) в HTML-форму, чтобы пользователь мог изменять эти данные. Вторая цель формы - взять данные, отправленные пользователем, и повторно применить их к объекту.

В формах Symfony существует еще очень много всего, что можно узнать, и множество мощных фокусов:

Справочник:

Продвинутые функции:

Темы формы и настройка:

События:

Валидация:

Разное:

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.