1

我希望能够使用 Symfony Event Dispatcher 更改表单。Symfony 文档讨论了动态表单修改,然而,在所有示例中,事件侦听器或订阅者都是在表单类中创建的。我想让这个逻辑与我的表单类分离。

如何修改 Symfony 表单,而不必指定表单类中将调用哪些事件侦听器?

4

1 回答 1

3

可能您需要的是一个表单类型扩展,它允许您修改整个系统中的任何现有表单类型。在那里,您可以添加事件侦听器/订阅者或您想要的任何特定或通用表单类型。

但是,如果这是一个非常频繁的情况,这项任务往往会变得乏味。所以做这样的事情可以为你提供完美的契合:

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) 并将事件订阅者注册为服务。

于 2018-01-08T20:31:09.740 回答