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

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

Компонент Формы предоставляет структурированный процесс, чтобы позволить вам настроить ваши формы, используя компонент 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().

Тип формы collection полагается на подписчика 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
// src/AppBundle/Form/SubscriptionType.php
namespace AppBundle\Form;

use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
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/AppBundle/Form/EventListener/AddEmailFieldListener.php
namespace AppBundle\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 Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
    use AppBundle\Form\EventListener\AddEmailFieldListener;

// ...

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

// ...

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