跳到内容

表单事件

编辑此页

表单组件提供了一个结构化的流程,让您可以通过使用 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()
    // ... add form fields
    ->addEventListener(FormEvents::PRE_SUBMIT, $listener);

// ...

表单工作流程

在表单的生命周期中,有两个时刻可以更新表单数据

  1. 预填充期间 (setData()),构建表单时;
  2. 当处理表单提交 (handleRequest()) 以根据用户输入的值更新表单数据时。

1) 预填充表单 (FormEvents::PRE_SET_DATAFormEvents::POST_SET_DATA)

当调用 Form::setData() 时,会在表单预填充期间分发两个事件:FormEvents::PRE_SET_DATAFormEvents::POST_SET_DATA

A) FormEvents::PRE_SET_DATA 事件

FormEvents::PRE_SET_DATA 事件在 Form::setData() 方法的开始处分发。它用于使用 FormEvent::setData() 修改预填充期间给定的数据。由于事件是从 Form::setData() 方法中分发的,因此 Form::setData() 方法会被锁定,如果从监听器调用它,则会抛出异常。

数据类型
事件数据 注入到 setData() 中的模型数据
表单模型数据 null
表单规范化数据 null
表单视图数据 null

另请参阅

表单事件信息表 中一览所有表单事件。

代替。

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() 中的模型数据
表单规范化数据 使用模型转换器转换的模型数据
表单视图数据 使用视图转换器转换的规范化数据

另请参阅

表单事件信息表 中一览所有表单事件。

Symfony\Component\Form\Extension\DataCollector\EventListener\DataCollectorListener 类被订阅以监听 FormEvents::POST_SET_DATA 事件,以便从反规范化的模型和视图数据中收集有关表单的信息。

2) 提交表单 (FormEvents::PRE_SUBMIT, FormEvents::SUBMITFormEvents::POST_SUBMIT)

当调用 Form::handleRequest()Form::submit() 时,会分发三个事件:FormEvents::PRE_SUBMITFormEvents::SUBMITFormEvents::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 中相同
表单视图数据 FormEvents::POST_SET_DATA 中相同

另请参阅

表单事件信息表 中一览所有表单事件。

警告

此时,您无法向表单添加或删除字段。

Symfony\Component\Form\Extension\Core\EventListener\FixUrlProtocolListener 订阅 FormEvents::SUBMIT 事件,以便为提交时没有协议的 URL 字段添加默认协议。

C) FormEvents::POST_SUBMIT 事件

FormEvents::POST_SUBMIT 事件在 Form::submit() 方法之后,一旦模型和视图数据被反规范化,就会分发。

它可以用于在反规范化后获取数据。

数据类型
事件数据 使用视图转换器转换的规范化数据
表单模型数据 使用模型转换器反向转换的规范化数据
表单规范化数据 使用视图转换器从请求中反向转换的请求数据
表单视图数据 使用视图转换器转换的规范化数据

另请参阅

表单事件信息表 中一览所有表单事件。

警告

此时,您无法向当前表单及其子表单添加或删除字段。

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_submit FormEvents::PRE_SUBMIT 请求数据
form.submit FormEvents::SUBMIT 规范化数据
form.post_submit FormEvents::POST_SUBMIT 视图数据

事件监听器

事件监听器可以是任何类型的有效可调用对象。例如,您可以在 FormFactoryaddEventListener 方法中直接内联定义一个事件监听器函数

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;
        }

        // checks whether the user has chosen to display their email or not.
        // If the data was submitted previously, the additional value that is
        // included in the request variables needs to be removed.
        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
    {
        // ...
    }
}

事件订阅器

事件订阅器有不同的用途

  • 提高可读性;
  • 监听多个事件;
  • 将多个监听器 regroup 到单个类中。

考虑以下表单事件订阅器的示例

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();

        // checks whether the user from the initial data has chosen to
        // display their email or not.
        if (true === $user->isShowEmail()) {
            $form->add('email', EmailType::class);
        }
    }

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

        if (!$user) {
            return;
        }

        // checks whether the user has chosen to display their email or not.
        // If the data was submitted previously, the additional value that
        // is included in the request variables needs to be removed.
        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();

// ...
本作品,包括代码示例,根据 Creative Commons BY-SA 3.0 许可获得许可。
TOC
    版本