Alberto Alberto - 6 months ago 74
PHP Question

Symfony2: instantiating form event subscribers in service container

I am trying to associate a custom Event Subscriber to all the forms of my application.

I started by creating the Event Subscriber class

<?php

namespace AppBundle\EventListener;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;

/**
* Custom form listener.
*/
class FormListener implements EventSubscriberInterface
{

public static function getSubscribedEvents()
{
return array(
FormEvents::PRE_SUBMIT => 'onPreSubmit',
FormEvents::SUBMIT => 'onSubmit',
FormEvents::POST_SUBMIT => 'onPostSubmit',
FormEvents::PRE_SET_DATA => 'onPreSetData',
FormEvents::POST_SET_DATA => 'onPostSetData',
);
}

public function onPreSubmit(FormEvent $event)
{
// code here
}

public function onSubmit(FormEvent $event)
{
// code here
}

public function onPostSubmit(FormEvent $event)
{
// code here
}

public function onPreSetData(FormEvent $event)
{
// code here
}

public function onPostSetData(FormEvent $event)
{
// code here
}

}


I know I can associate it to a form inside its
buildForm
function

public function buildForm(FormBuilderInterface $builder, array $options)
{
// code here

->addEventSubscriber(new \AppBundle\EventListener\FormListener());
}


and everything works fine so far.

Now is the question: since I would like to add this event substriber to all the forms of my application (to perform some common checks and provide hooks for form manipulation) I thought not to instantiate my event subscriber inside each form, but inside the service container (in services.yml) like this:

appbundle.form.listener:
class: AppBundle\EventListener\FormListener
tags:
- { name: kernel.event_subscriber }


Needless to say, this second approach is not working. So my question is: am I doing something wrong? Is it possible to listen to form events outside the form? Is there something wrong with my approach?

Answer

You can create form extension for FormType, which is base type for all other.

Form extension should look like this:

namespace AppBundle\Form\Extension;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormBuilderInterface;

class FormTypeExtension extends AbstractTypeExtension
{

    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder->addEventSubscriber(new \AppBundle\EventListener\FormListener());
    }

    public function getExtendedType()
    {
        return FormType::class;
    }
}

Then register this extension in service container like this:

services:
    app.form_type_extension:
        class: AppBundle\Form\Extension\FormTypeExtension
        tags:
            - { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FromType }

Further reference in the cookbook