【问题标题】:Decoupled way of modifying forms in Symfony 3/4Symfony 3/4 中修改表单的解耦方式
【发布时间】:2018-01-08 17:06:08
【问题描述】:

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

如何修改 Symfony 表单,而不必在表单类中指定要调用的事件侦听器?

【问题讨论】:

    标签: php symfony symfony-forms symfony4 symfony3.x


    【解决方案1】:

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

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

    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;
          }
      }
      
    • 新建一个接口来配置要监听的表单类:

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

    【讨论】:

    • 谢谢!这在我的路上帮助了我。我现在创建了一个表单类型扩展,其中注入了事件调度程序。从那里我发送一个自定义的 BuildFormEvent。
    • 一年多后才回来重申这是一个很棒的解决方案。我什至相信这应该是 Symfony 本身的一部分,我不敢相信这没有得到更多的支持。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-22
    • 2017-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多