Дата оновлення перекладу 2022-05-17

Форми

Screencast

Віддаєте перевагу відео-урокам? Подивіться Symfony Forms screencast series.

Створення та обробка HTML форм - це складно та монотонно. Вам необхідно розібратися з відображенням полів HTML форми, валідацією відправлених даних, відображенням даних форми в об’єктах та багато чим іншим. Symfony містить потужну функцію форм, яка надає всі ці, та багато інших, можливості для дійсно складних сценаріїв.

Установка

У додатках, що використовують Symfony Flex, запустіть цю команду, щоб встановити функцію форми перед її використанням:

1
$ composer require symfony/form

Note

Компонент Symfony для роботи з формами - це незалежна бібліотека, яка може бути використана поза проектів Symfony. Подробиці шукайте за посиланням Компонент по роботі з формами на GitHub.

Використання

Рекомендований робочий процес при роботі з формами Symfony, наступний:

  1. Створіть форму в контролері Symfony або використовуючи спеціальний клас форми;
  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), що представлені їхнім повним іменем класу. Нарешті, ви додали кнопку відправки з користувацьким ярликом, для відправлення форми серверку.

Cтворення класів форми

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). Пізніше, коли ви почнете вбудовувати форми, цього вже не буде достатньо.

Тому, хоча це і не завжди необхідно, зазвичай гарною ідеєю буде чітко вказати опцію 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(),
        ]);
    }
}

У версіях Symfony до 5.3, контролери використовували метод $this->render('...', ['form' => $form->createView()]) для відображення форми. Метод renderForm() здобуває цю логіку, а також автоматично встановлює у відповіді HTTP статус-код 422, якщо відправлена форма не є валідною.

New in version 5.3: Метод renderForm() було представлено в Symfony 5.3.

Потім: використайте якісь функції помічників форми, щоб відобразити зміст форми:

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() містить відправлені значення
            // але початкова змінна `$task` також була оновлена
            $task = $form->getData();

            // ... виконайте якусь дію, наприклад, збережіть завдання в базу даних
            // наприклад, якщо Завдання - сутність Doctrine, збережіть її!
            // $entityManager = $this->getDoctrine()->getManager();
            // $entityManager->persist($task);
            // $entityManager->flush();

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

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

Контролер слідує загальному патерну для обробки форм, і має три можливих шляхи:

  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. Опція configuration-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;
use Symfony\Component\Form\FormFactoryInterface;
// ...

class TaskController extends AbstractController
{
    public function new(FormFactoryInterface $formFactory): Response
    {
        $task = ...;
        $form = $formFactory->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. Прочитайте керівництво Validating Objects, щоб дізнатися більше інформації.

Невідображені поля

При редагуванні об’єкту через форму, всі поля форми вважаються властивостями об’єкту. Будь-які поля форми, що не існують в об’єкті, будуть викликати виключення.

Якщо вам потрібні додаткові поля в формі, які не будуть зберігатися в об’єкті (наприклад, щоб додати прапорець чекбоксу “Я згодний з цими умовами”), встановіть опцію як 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.