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

Дата обновления перевода 2024-07-19

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

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

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

// ...

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

В жизненном цикле формы есть два момента, когда данные формы могут быть обновлены:

  1. Во время предварительного заполнения (setData()) при создании формы;
  2. При обработке отправки формы (handleRequest()) для обновления данных формы на основе значений, введенных пользователем.

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

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

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

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

??? ?????? ????????
?????? ??????? ????????? ??????, ?????????? ? setData()
????????? ?????? null
??????????????? ?????? null
?????? ????????? null

See also

Увидеть все события форм одномоментно вы можете в Информационной таблице о событиях форм .

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

??? ?????? ????????
?????? ??????? ????????? ??????, ?????????? ? setData()
????????? ?????? ????????? ??????, ?????????? ? setData()
??????????????? ?????? ????????? ??????, ??????????????? ? ??????? ?????????? ???????????????
?????? ????????? ??????????????? ??????, ??????????????? ? ??????? ??????????????? ?????????

See also

Увидеть все события форм одномоментно вы можете в Информационной таблице о событиях форм .

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

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

При вызове метода 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

See also

Увидеть все события форм одномоментно вы можете в Информационной таблице о событиях форм .

Подписчик 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
?????? ????????? ????? ??, ??? ? FormEvents::POST_SET_DATA

See also

Увидеть все события форм одномоментно вы можете в Информационной таблице о событиях форм .

Caution

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

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

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

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

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

??? ?????? ????????
?????? ??????? ??????????????? ??????, ??????????????? ? ??????? ??????????????? ?????????
????????? ?????? ??????????????? ??????, ??????????????? ??????? ? ??????? ??????????????? ??????
??????????????? ?????? ?????? ?? ???????, ??????????????? ??????? ?? ??????? ? ??????? ??????????????? ?????????
?????? ????????? ??????????????? ?????? ??????????????? ? ??????? ??????????????? ?????????

See also

Увидеть все события форм одномоментно вы можете в Информационной таблице о событиях форм .

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\Event\PreSubmitEvent;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormEvents;

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

        if (!$user) {
            return;
        }

        // Проверяет, выбрал ли пользователь отображать его электронную почту.
        // Если данные были отправлены ранее, дополнительное значение,
        // включённое в переменных запроса, должно быть удалено.
        if (isset($user['showEmail']) && $user['showEmail']) {
            $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\Event\PreSetDataEvent;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormEvents;

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

    public function onPreSetData(PreSetDataEvent $event): void
    {
        // ...
    }
}

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

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

  • Улучшение читаемости;
  • Прослушивание нескольких событий;
  • Регруппировка нескольких слушателей внутри одного класса.

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

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
51
// src/Form/EventListener/AddEmailFieldListener.php
namespace App\Form\EventListener;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\Event\PreSetDataEvent;
use Symfony\Component\Form\Event\PreSubmitEvent;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\FormEvents;

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

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

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

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

        if (!$user) {
            return;
        }

        // Проверяет, выбрал ли пользователь отображать свою электронную почту.
        // Если данные были отправлены ранее, дополнительное значение,
        // включенное в переменных запроса, должно быть удалено.
        if (isset($user['showEmail']) && $user['showEmail']) {
            $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\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\TextType;

// ...

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

// ...