Раздельный способ изменения форм в Symfony 3/4

Я хотел бы иметь возможность изменять форму с помощью диспетчера событий Symfony. Документация Symfony говорит о динамической модификации формы, однако во всех примерах событие слушатель или подписчик создается в классе формы. Я хотел бы, чтобы эта логика была отделена от моего класса формы.

Как я могу изменить форму Symfony, не указывая, какие прослушиватели событий будут вызываться в классе формы?


person Xatoo    schedule 08.01.2018    source источник


Ответы (1)


Вероятно, вам нужно расширение типа формы, оно позволит вам изменить любой существующие типы форм во всей системе. Там вы можете добавить слушателей событий/подписчиков или то, что вы хотите, к любому конкретному или общему типу формы.

Однако эта задача может стать утомительной, если это очень частый случай. Таким образом, выполнение чего-то подобного может обеспечить вам идеальную посадку:

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();
    }
    
  • Наконец, в новый проход компилятора вводит в службу расширения все зарегистрированные 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) и зарегистрирован подписчик событий как служба.

person yceruto    schedule 08.01.2018
comment
Благодарю вас! Это помогло мне в пути. Теперь я создал расширение типа формы, в которое вводится диспетчер событий. Оттуда я отправляю пользовательский BuildFormEvent. - person Xatoo; 09.01.2018
comment
Просто вернулся более чем через год, чтобы повторить, что это отличное решение. Я даже считаю, что это должно быть частью самой Symfony, и я не могу поверить, что это не получило больше голосов. - person Xatoo; 21.03.2019