13

As per title, can a Symfony2 form event listener access the service container?

This is an example event listener (for post bind event):

class CustomerTypeSubscriber implements EventSubscriberInterface
{

    public static function getSubscribedEvents()
    {
        return array(FormEvents::POST_BIND => 'onPostBind');
    }

    public function onPostBind(DataEvent $event)
    {
        // Get the entity (Customer type)
        $data = $event->getData();

        // Get current user
        $user = null;    

        // Remove country if address is not provided
        if(!$data->getAddress()) :
            $data->setCountry(null);
        endif;
    }

}
j0k
  • 22,600
  • 28
  • 79
  • 90
gremo
  • 47,186
  • 75
  • 257
  • 421

2 Answers2

29

What do you need to access to service container for?

You can inject any services into your listener using dependency injection (as you define your listener as a service, right?).

You should be able to do something like:

    service.listener:
    class: Path\To\Your\Class
    calls:
      - [ setSomeService, [@someService] ]
    tags:
      - { [Whatever your tags are] }

And in your listener:

private $someService;

public function setSomeService($service) {
    $this->someService = $someService;
}

Where someService is the ID of whatever service you want to inject.

If you want, you can inject the service container with @service_container, but it's probably better to inject only what you need 'cause I think having everything container aware makes you a bit lazy.

Chris McKinnel
  • 14,694
  • 6
  • 64
  • 67
  • Well i didn't know that listeners could be defined as as service. Right now i'm defining only form types and classes (as services). Good to know! Thanks. – gremo May 18 '12 at 17:29
  • Right, so I guess that answers your question? Anything defined as a service can have access to other services (including the service container) using dependency injection. – Chris McKinnel May 20 '12 at 00:02
  • How to get it in form then, inject it to form through DI as well? – umpirsky Jun 29 '12 at 11:19
  • It doesn't work for me somewhy. Could you guess what's the reason could be? – Serhii Smirnov Oct 10 '14 at 13:14
5

I think the way a Form Subscriber works is a little different to a regular Event Listener.

In your controller, you are instantiating your form type, i.e.

$form = $this->createForm(new CustomerType(), $customer);

Since the container is available in your controller, you could pass it directly to your form type, i.e.

$form = $this->createForm(new CustomerType($this->container), $customer);

In my case I needed the security context, so my implementation (similar but slightly different to your original q):

In my controller:

$form = $this->createForm(new CustomerType($this->get('security.context')), $customer));

In my Form class:

use Symfony\Component\Security\Core\SecurityContext;
class CustomerType extends AbstractType
{
    protected $securityContext;

    public function __construct(SecurityContext $securityContext)
    {
        $this->securityContext = $securityContext;
    }

    public function buildForm(FormBuilder $builder, array $options)
    {
        // Delegate responsibility for creating a particular field to EventSubscriber
        $subscriber = new CustomerAddSpecialFieldSubscriber($builder->getFormFactory(), $this->securityContext);
        $builder->addEventSubscriber($subscriber);
        $builder->add( ... the rest of my fields ... );
    }

    // other standard Form functions
}

And in my Form Subscriber

use Symfony\Component\Security\Core\SecurityContext;
CustomerAddSpecialFieldSubscriber
{
    private $factory;

    protected $securityContext;

    public function __construct(FormFactoryInterface $factory, SecurityContext $securityContext)
    {
        $this->factory = $factory;
        $this->securityContext = $securityContext;
    }

    public static function getSubscribedEvents()
    {
        return array(FormEvents::PRE_SET_DATA => 'preSetData');
    }

    public function preSetData(DataEvent $event)
    {
        if (true === $this->securityContext->isGranted('ROLE_ADMIN')) {
            // etc
        }
    }

}

Hope it helps.

Chris Gordon
  • 394
  • 4
  • 5