表单事件
表单组件提供了一个结构化的流程,让您可以通过使用 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);
// ...
表单工作流程
在表单的生命周期中,有两个时刻可以更新表单数据
- 在预填充期间 (
setData()
),构建表单时; - 当处理表单提交 (
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() 方法中分发的,因此 Form::setData() 方法会被锁定,如果从监听器调用它,则会抛出异常。
数据类型 | 值 |
---|---|
事件数据 | 注入到 setData() 中的模型数据 |
表单模型数据 | null |
表单规范化数据 | null |
表单视图数据 | null |
B) FormEvents::POST_SET_DATA
事件
FormEvents::POST_SET_DATA
事件在 Form::setData() 方法的末尾分发。此事件可用于根据填充的数据修改表单(动态添加或删除字段)。
数据类型 | 值 |
---|---|
事件数据 | 注入到 setData() 中的模型数据 |
表单模型数据 | 注入到 setData() 中的模型数据 |
表单规范化数据 | 使用模型转换器转换的模型数据 |
表单视图数据 | 使用视图转换器转换的规范化数据 |
另请参阅
在 表单事件信息表 中一览所有表单事件。
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 中相同 |
另请参阅
在 表单事件信息表 中一览所有表单事件。
B) FormEvents::SUBMIT
事件
FormEvents::SUBMIT
事件在 Form::submit() 方法将规范化数据转换回模型和视图数据之前分发。
它可以用于更改来自数据的规范化表示的数据。
数据类型 | 值 |
---|---|
事件数据 | 使用视图转换器从请求中反向转换的请求数据 |
表单模型数据 | 与 FormEvents::POST_SET_DATA 中相同 |
表单规范化数据 | 与 FormEvents::POST_SET_DATA 中相同 |
表单视图数据 | 与 FormEvents::POST_SET_DATA 中相同 |
另请参阅
在 表单事件信息表 中一览所有表单事件。
警告
此时,您无法向表单添加或删除字段。
C) FormEvents::POST_SUBMIT
事件
FormEvents::POST_SUBMIT
事件在 Form::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 |
视图数据 |
事件监听器
事件监听器可以是任何类型的有效可调用对象。例如,您可以在 FormFactory
的 addEventListener
方法中直接内联定义一个事件监听器函数
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();
// ...