表单事件
Form组件提供了一个结构化的处理过程,让你利用EventDispatcher组件,来定制你的表单。使用表单事件,你可以在工作流的不同阶段,修改某些信息或字段:从表单的加载,到利用请求提交数据。
使用表单组件的话,注册一个事件监听(event listener)是非常容易的。
例如,你想要注册一个方法给FormEvents::PRE_SUBMIT
事件,则下面的代码可以让你根据request值的不同,而添加一个字段:
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()
// add form fields 添加字段
->addEventListener(FormEvents::PRE_SUBMIT, $listener);
// ... |
表单的工作流 ¶
表单提交工作流 ¶
1)预加载表单(FormEvents::PRE_SET_DATA和FormEvents::POST_SET_DATA) ¶
当Form::setData()
被调用时,有两个事件在表单的pre-population预加载过程中被派遣:FormEvents::PRE_SET_DATA
和FormEvents::POST_SET_DATA
。
A)FormEvents::PRE_SET_DATA事件 ¶
FormEvents::PRE_SET_DATA
事件,在Form::setData()
方法的最初阶段被派遣。它可以用于:
对预加载过程中给定的数据进行调整;
根据预加载的数据来调整表单(动态添加/删除字段)。
Data Type(数据类型) | Value(值) |
---|---|
Model data | null |
Normalized data | null |
View data | null |
可在表单事件信息表中找到完整信息。
在FormEvents::PRE_SET_DATA
期间,Form::setData()
方法被锁定,如果使用该方法则会抛出异常 。如果你需要修改数据,应该使用FormEvent::setData()
来替代。
B)FormEvents::POST_SET_DATA事件 ¶
FormEvents::POST_SET_DATA
事件在Form::setData()
方法的最末阶段被派遣。这个事件往往被用于“在得到预加载表单之后”来读取数据。
Data Type(数据类型) | Value(取值) |
---|---|
Model data | 注入到setData() 的Model data |
Normalized data | 通过model transformer转换而成的odel data |
View data | 通过view transformer转换而成的ormalized 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()
方法的最初阶段被派遣。
它可以被用于:
改变request中的数据,在它们被提交到表单之前;
添加或删除字段,在数据被提交到表单之前。
Data Type(数据类型) | Value(取值) |
---|---|
Model data | 等同于FormEvents::POST_SET_DATA 中的 |
Normalized data | 等同于FormEvents::POST_SET_DATA 中的 |
View data | 等同于FormEvents::POST_SET_DATA 中的 |
可在表单事件信息表中找到完整信息。
B)FormEvents::SUBMIT事件 ¶
FormEvents::SUBMIT
事件被派遣的时间,刚好是在Form::submit()
方法把normalized data给转换回model data和view data之前。
它可以被用于改变“从normalized data表现层”得到的数据。
Data Type(数据类型) | Value(取值) |
---|---|
Model data | 等同于FormEvents::POST_SET_DATA 中的 |
Normalized data | 通过view transformer转换而来的reqeust,再被反向转换回reqeust时所包含的数据 |
View data | 等同于FormEvents::POST_SET_DATA 中的 |
可在表单事件信息表中找到完整信息。
此时,你无法添加或移除表单字段。
C)FormEvents::POST_SUBMIT事件 ¶
FormEvents::POST_SUBMIT
事件的派遣时机,是在Form::submit()
方法一旦将model data和view data的denormalize化完成之后。
它可以用于在denormalization进程之后取出数据。
Data Type(数据类型) | Value(取值) |
---|---|
Model data | 通过model transformer反向转换而成的Normalized数据 |
Normalized data | 等同于FormEvents::SUBMIT 中的 |
View data | 通过view transformer转换而成的normalized data |
可在表单事件信息表中找到完整信息。
此时,你无法添加或移除表单字段。
注册事件监听和订阅 ¶
为了能使用表单事件,你需要创建一个监听或订阅,并将它们注册到一个事件。
每一个“表单”事件的名称,都被定义成FormEvents
类中的一个常量。而且,每一个事件的callback回调(监听方法/订阅方法)都被当成一个单独的参数传入FormEvent
的实例。事件对象包括一个对表单当前状态的引用,以及被处理过的当前数据。
Name(事件名) |
FormEvents ConstAnt(常量) |
Event's Data(事件数据) |
---|---|---|
form.pre_set_data |
FormEvents::PRE_SET_DATA |
Model data |
form.post_set_data |
FormEvents::POST_SET_DATA |
Model data |
form.pre_bind |
FormEvents::PRE_SUBMIT |
Request data |
form.bind |
FormEvents::SUBMIT |
Normalized data |
form.post_bind |
FormEvents::POST_SUBMIT |
View data |
Event Listeners(表单监听器) ¶
在表单事件中,一个event listener可以是任何类型的有效回调(callable)。
创建并绑定一个监听给表单,是很容易的:
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 |
// ...
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;
}
// Check whether the user has chosen to display his email or not.
// If the data was submitted previously, the additional value that is
// included in the request variables needs to be removed.
// 检查用户是否决定显示其email字段
// 若数据之前被提交过,request对象中所包含的附加值将被移除
if (true === $user['show_email']) {
$form->add('email', EmailType::class);
} else {
unset($user['email']);
$event->setData($user);
}
})
->getForm();
// ... |
当你使用一个form type类时,使用其自有方法来完成用于监听的回调,可以增加程序的可读性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
// ...
class SubscriptionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('username', TextType::class);
$builder->add('show_email', CheckboxType::class);
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
array($this, 'onPreSetData')
);
}
public function onPreSetData(FormEvent $event)
{
// ...
}
} |
Event Subscribers(表单订阅器) ¶
在表单事件中,事件订阅器有不同的作用:
改善代码的可读性;
可以监听多个事件;
在一个类中,重组多个监听(方法)
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 |
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();
// Check whether the user from the initial data has chosen to
// display his email or not.
if (true === $user->isshowEmail()) {
$form->add('email', EmailType::class);
}
}
public function onPreSubmit(FormEvent $event)
{
$user = $event->getData();
$form = $event->getForm();
if (!$user) {
return;
}
// Check whether the user has chosen to display his email or not.
// If the data was submitted previously, the additional value that
// is included in the request variables needs to be removed.
// 检查用户是否决定显示其email字段
// 若数据之前被提交过,request对象中所包含的附加值将被移除
if (true === $user['show_email']) {
$form->add('email', EmailType::class);
} else {
unset($user['email']);
$event->setData($user);
}
}
} |
注册表单订阅,使用form builder中的addEventSubscriber()
方法:
1 2 3 4 5 6 7 8 9 10 11 12 |
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();
// ... |