在 Symfony 3/4 中修改表单的分离方式
Decoupled way of modifying forms in Symfony 3/4
我希望能够使用 Symfony Event Dispatcher 更改表单。 Symfony 文档 talks about 动态形式修改,但是,在所有示例中,事件侦听器或订阅者都是以 class 形式创建的。我想让这个逻辑与我的表单分离 class.
如何修改 Symfony 表单而不必在表单中指定要调用的事件侦听器 class?
可能您需要的是 form type extension,它允许您修改整个系统中任何现有的表单类型。在那里,您可以添加事件 listeners/subscribers 或您想要的任何特定或通用表单类型。
但是,如果这是一个非常频繁的情况,这个任务往往会变得乏味。所以做这样的事情可以为你提供一个完美的契合:
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 实现的功能。在这里,我留给你一个实现它的方法:
首先,create a new form type extension根据配置将订阅者添加到表单生成器:
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;
}
}
新建接口配置表单class监听:
interface FormEventSubscriberInterface
{
public static function getFormClass();
}
最后,进入一个new compiler 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
) 并将事件订阅者注册为服务。
我希望能够使用 Symfony Event Dispatcher 更改表单。 Symfony 文档 talks about 动态形式修改,但是,在所有示例中,事件侦听器或订阅者都是以 class 形式创建的。我想让这个逻辑与我的表单分离 class.
如何修改 Symfony 表单而不必在表单中指定要调用的事件侦听器 class?
可能您需要的是 form type extension,它允许您修改整个系统中任何现有的表单类型。在那里,您可以添加事件 listeners/subscribers 或您想要的任何特定或通用表单类型。
但是,如果这是一个非常频繁的情况,这个任务往往会变得乏味。所以做这样的事情可以为你提供一个完美的契合:
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 实现的功能。在这里,我留给你一个实现它的方法:
首先,create a new form type extension根据配置将订阅者添加到表单生成器:
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; } }
新建接口配置表单class监听:
interface FormEventSubscriberInterface { public static function getFormClass(); }
最后,进入一个new compiler 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
) 并将事件订阅者注册为服务。