События формы

Компонент Формы предоставляет структурированный процесс, чтобы позволить вам настроить ваши формы, используя компонент EventDispatcher. Используя события формы, вы можете изменять информацию или поля на разных этапах рабочего процесса: от наполнения формы до отправи данных из запроса.

Регистрирование слушателя событий очень простое при использовании компонента Формы.

Например, если вы хотите зарегистрировать функцию в событии FormEvents::PRE_SUBMIT, следующий код позволяет вам добавить поле, в зависимости от значений запроса:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// ...

use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;

$listener = function (FormEvent $event) {
    // ...
};

$form = $formFactory->createBuilder()
    // ... добавить поля формы
    ->addEventListener(FormEvents::PRE_SUBMIT, $listener);

// ...

Рабочий процесс формы

Рабочий процесс отправки формы

../_images/general_flow.png

1) Предварительное заполнение формы (FormEvents::PRE_SET_DATA и FormEvents::POST_SET_DATA)

../_images/set_data_flow.png

Во время предварительного заполнения формы, развёртываются два события, когда вызывается метод Form::setData(): FormEvents::PRE_SET_DATA и FormEvents::POST_SET_DATA.

A) Событие FormEvents::PRE_SET_DATA

Событие FormEvents::PRE_SET_DATA развёртывается в начале метода Form::setData(). Оно может быть использовано для:

  • Изменения данных, предоставленных во время предварительного заполнения;
  • Изменения формы, в зависимости от данных предварительного заполнения (динамическое добавление или удаление полей).
Тип данных Значение
Модельные данные null
Нормализованные данные null
Данные просмотра null
Увидеть все события форм одномоментно вы можете в Информационной таблице о событиях форм.

Caution

Во время FormEvents::PRE_SET_DATA, Form::setData() заблокирован и будет выдавать исключение при использовании. Если вы хотите измеить данные, вместо этого вам стоит использовать FormEvent::setData().

Тип формы Symfony\Component\Form\Extension\Core\Type\CollectionType полагается на подписчика Symfony\Component\Form\Extension\Core\EventListener\ResizeFormListener, слушающего событие FormEvents::PRE_SET_DATA для того, чтобы переупорядочить поля формы, в зависимости от данных из объекта редварительного заполнения, путём удаления или добавления всех строк формы.

B) Событие FormEvents::POST_SET_DATA

Событие FormEvents::POST_SET_DATA развёртывается в конце метода Form::setData(). Это событие в основном предназначено для чтения данных после предварительного заполнения формы.

Тип данных Значение
Модельные данные Внедрённые модельные данные
Нормализованные данные Модельные данные, преобразованные с помощью модельного преобразователя
Данные просмотра Нормализованные данные, преобразованные с помощью преобразователя просмотра
Увидеть все события форм одномоментно вы можете в Информационной таблице о событиях форм.

Класс Symfony\Component\Form\Extension\DataCollector\EventListener\DataCollectorListener подписан для прослушивания события FormEvents::POST_SET_DATA, чтобы собирать информацию о формах из денормализированных модельных данных и данных просмотра.

2) Отправка формы (FormEvents::PRE_SUBMIT, FormEvents::SUBMIT и FormEvents::POST_SUBMIT)

../_images/submission_flow.png

При вызове метода Form::handleRequest() или Form::submit(), развёртываются три события: FormEvents::PRE_SUBMIT, FormEvents::SUBMIT, FormEvents::POST_SUBMIT.

A) Событие FormEvents::PRE_SUBMIT

Событие FormEvents::PRE_SUBMIT развёртывается в начале метода Form::submit().

Оно может быть использовано для:

  • Изменения данных из запроса, до отправки данных в форму;
  • Добавления или удаления полей формы,до отправки данных в форму.
Тип данных Значение
Модельные данные Такое же, как в FormEvents::POST_SET_DATA
Нормализованные данные Такое же, как в FormEvents::POST_SET_DATA
Данные просмотра Такое же, как в FormEvents::POST_SET_DATA
Увидеть все события форм одномоментно вы можете в Информационной таблице о событиях форм.

Подписчик Symfony\Component\Form\Extension\Core\EventListener\TrimListener подписывается на событие FormEvents::PRE_SUBMIT, чтобы обрезать данные запроса (для значений строк). Подписчик Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener подписывается на событие FormEvents::PRE_SUBMIT, чтобы валидировать CSRF-токен.

B) Событие FormEvents::SUBMIT

Событие FormEvents::SUBMIT развёртывается прямо перед тем, как метод Form::submit() преобразует нормализированные данные в модельные данные и данные просмотра.

Оно может быть использовано для изменения данных из нормализированного представления данных.

Тип данных Значение
Модельные данные Такое же, как в FormEvents::POST_SET_DATA
Нормализованные данные Данные из запроса обратно преобразованные из запроса с использованием преобразователя
Данные просмотра Такое же, как в FormEvents::POST_SET_DATA
Увидеть все события форм одномоментно вы можете в Информационной таблице о событиях форм.

Caution

На этом этапе вы не можете добавлять или удалять поля из формы.

Symfony\Component\Form\Extension\Core\EventListener\FixUrlProtocolListener подписывается на событие FormEvents::SUBMIT, чтобы добавить протокол по умолчаниюк полям URL, которые были отправлены без протокола.

C) Событие FormEvents::POST_SUBMIT

Событие FormEvents::POST_SUBMIT развёртывается после Form::submit(), когда были денормализированы модельные данные и данные просмотра.

Оно может быть использовано, чтобы извлекать данные после денормализации.

Тип данных Значение
Модельные данные Нормализованные данные обратно преобразованные с помощью преобразователя модели
Нормализованные данные Такое же, как в FormEvents::SUBMIT
Данные просмотра Нормализованные данные преобразованные с помощью преобразователя просмотра
Увидеть все события форм одномоментно вы можете в Информационной таблице о событиях форм.

Caution

На этом этапе вы не можете добавлять или удалять поля из формы.

Symfony\Component\Form\Extension\DataCollector\EventListener\DataCollectorListener подписывается на событие FormEvents::POST_SUBMIT, чтобы собирать информацию о формах. Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener подписывается на событие FormEvents::POST_SUBMIT, чтобы автоматически валидировать денормализованный объект.

Регистрация слушателей или подписчиков событий

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

Имя каждого события "формы" определяется как константа в классе FormEvents. Дополнительно, каждому обратному вызову события (метод слушателя или подписчика) передаётся один аргумент, который является экземпляром класса FormEvent. Объект события содержит ссылку на текущее состояние формы и текущие данные, которые обрабатываются.

Имя Константа FormEvents Данные события
form.pre_set_data FormEvents::PRE_SET_DATA Модельные данные
form.post_set_data FormEvents::POST_SET_DATA Модельные данные
form.pre_bind FormEvents::PRE_SUBMIT Данные запроса
form.bind FormEvents::SUBMIT Нормализованные данные
form.post_bind FormEvents::POST_SUBMIT Данные просмотра

Слушатели событий

Слушатель событий может быть любым типом валидного вызываемого.

Создание и привязка слушателя собыйти - это очень просто:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// ...

use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;

$form = $formFactory->createBuilder()
    ->add('username', TextType::class)
    ->add('show_email', CheckboxType::class)
    ->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
        $user = $event->getData();
        $form = $event->getForm();

        if (!$user) {
            return;
        }

        // Проверяет, выбрал ли пользователь отображать его электронную почту.
        // Если данные были отправлены ранее, дополнительное значение,
        // включённое в переменных запроса, должно быть удалено.
        if (true === $user['show_email']) {
            $form->add('email', EmailType::class);
        } else {
            unset($user['email']);
            $event->setData($user);
        }
    })
    ->getForm();

// ...

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// src/Form/SubscriptionType.php
namespace App\Form;

use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;

// ...
class SubscriptionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('username', TextType::class)
            ->add('show_email', CheckboxType::class)
            ->addEventListener(
                FormEvents::PRE_SET_DATA,
                array($this, 'onPreSetData')
            )
        ;
    }

    public function onPreSetData(FormEvent $event)
    {
        // ...
    }
}

Подписчики событий

Подписчики событий имеют разные применения:

  • Улучшение читаемости;
  • Прослушивание нескольких событий;
  • Регруппировка нескольких слушателей внутри одного класса.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// src/Form/EventListener/AddEmailFieldListener.php
namespace App\Form\EventListener;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Extension\Core\Type\EmailType;

class AddEmailFieldListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            FormEvents::PRE_SET_DATA => 'onPreSetData',
            FormEvents::PRE_SUBMIT   => 'onPreSubmit',
        );
    }

    public function onPreSetData(FormEvent $event)
    {
        $user = $event->getData();
        $form = $event->getForm();

        // Проверяет, выбрал ли пользователь из первоначальных данных
        // отображать свою электронную почту
        if (true === $user->isShowEmail()) {
            $form->add('email', EmailType::class);
        }
    }

    public function onPreSubmit(FormEvent $event)
    {
        $user = $event->getData();
        $form = $event->getForm();

        if (!$user) {
            return;
        }

        // Проверяет, выбрал ли пользователь отображать свою электронную почту.
        // Если данные были отправлены ранее, дополнительное значение,
        // включенное в переменных запроса, должно быть удалено.
        if (true === $user['show_email']) {
            $form->add('email', EmailType::class);
        } else {
            unset($user['email']);
            $event->setData($user);
        }
    }
}

Для того, чтобы зарегистрировать подписчика событий, используйте метод addEventSubscriber():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use App\Form\EventListener\AddEmailFieldListener;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;

// ...

$form = $formFactory->createBuilder()
    ->add('username', TextType::class)
    ->add('show_email', CheckboxType::class)
    ->addEventSubscriber(new AddEmailFieldListener())
    ->getForm();

// ...

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