我希望能够使用 Symfony Event Dispatcher 更改表单。Symfony 文档讨论了动态表单修改,然而,在所有示例中,事件侦听器或订阅者都是在表单类中创建的。我想让这个逻辑与我的表单类分离。
如何修改 Symfony 表单,而不必指定表单类中将调用哪些事件侦听器?
我希望能够使用 Symfony Event Dispatcher 更改表单。Symfony 文档讨论了动态表单修改,然而,在所有示例中,事件侦听器或订阅者都是在表单类中创建的。我想让这个逻辑与我的表单类分离。
如何修改 Symfony 表单,而不必指定表单类中将调用哪些事件侦听器?
可能您需要的是一个表单类型扩展,它允许您修改整个系统中的任何现有表单类型。在那里,您可以添加事件侦听器/订阅者或您想要的任何特定或通用表单类型。
但是,如果这是一个非常频繁的情况,这项任务往往会变得乏味。所以做这样的事情可以为你提供完美的契合:
class FoobarFormSubscriber implements EventSubscriberInterface, FormEventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(FormEvents::PRE_SET_DATA => 'preSetData');
}
public static function getFormClass()
{
return FoobarType::class;
}
public function preSetData(FormEvent $event)
{
$form = $event->getForm();
$form->add('custom', null, array('mapped' => false));
}
}
但显然这不是 Symfony 实现的功能。在这里,我给你一个实现它的秘诀:
首先,根据配置创建一个新的表单类型扩展,将订阅者添加到表单构建器中:
class FormEventTypeExtension extends AbstractTypeExtension
{
private $subscribers;
public function __construct(array $subscribers = array())
{
$this->subscribers = $subscribers;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$formClass = get_class($builder->getType()->getInnerType());
if (isset($this->subscribers[$formClass])) {
foreach ($this->subscribers[$formClass] as $subscriber) {
$builder->addEventSubscriber($subscriber);
}
}
}
public function getExtendedType()
{
return FormType::class;
}
}
新建一个接口来配置要监听的表单类:
interface FormEventSubscriberInterface
{
public static function getFormClass();
}
最后,在一个新的编译器 pass中,注入所有已注册的扩展服务,这些服务kernel.event_subscriber
实现了先前的接口:
public function process(ContainerBuilder $container)
{
$subscribers = array();
foreach ($container->findTaggedServiceIds('kernel.event_subscriber') as $serviceId => $tags) {
$subscriberClass = $container->getDefinition($serviceId)->getClass();
if (is_subclass_of($subscriberClass, FormEventSubscriberInterface::class, true)) {
$subscribers[$subscriberClass::getFormClass()][] = new Reference($serviceId);
}
}
$extensionDef = $container->getDefinition(FormEventTypeExtension::class);
$extensionDef->setArgument(0, $subscribers);
}
然后,您的自定义订阅者被解耦并准备好按原样工作,只需确保实现两个接口 ( EventSubscriberInterface, FormEventSubscriberInterface
) 并将事件订阅者注册为服务。