Search code examples
symfonysymfony-3.3

Getting a list of tagged services in my controller


What i want is to add services to the service container that i want to use later in my controller or service.

So i created two services with my custom tag fbeen.admin

here they are:

services:
    app.test:
        class: AppBundle\Admin\TestAdmin
        tags:
            - { name: fbeen.admin }

    fbeen.admin.test:
        class: Fbeen\AdminBundle\Admin\TestAdmin
        tags:
            - { name: fbeen.admin }

Now i want to use all the services with the tag fbeen.admin in my controller but i dont know how.

I followed the How to work with service tags tutorial but i get stuck on this rule:

$definition->addMethodCall('addTransport', array(new Reference($id)));

On some way the addTransport method of the TransportChain class should be called but it seems that it isn't been called.

And even if it would be called then i still do not have a list of services with the fbeen.admin tag into my controller.

I am sure that i am missing something but who can explain me what it is?

p.s. I know that compilerPass runs at buildtime but for example sonata admin knows all admin classes and twig knows all twig extensions. How do they know?

Thank you for reading this :-)


Solution

  • Symfony 3.3

    Container gets compiled once (in debug more often, but in production only once). What you manage with addMethodCall... is that once you request your service from container, which you are storing in $definition (that in this case is controller). Then container will call method addMethodCall('method'.. during initialising your service.

    What it will look in container:

    // This is pseudo content of compiled container
    $service = new MyController();
    // This is what compiler pass addMethodCall will add, now its your 
    // responsibility to implement method addAdmin to store admins in for 
    // example class variable. This is as well way which sonata is using
    $service->addAdmin(new AppBundle\Admin\TestAdmin());
    $service->addAdmin(new AppBundle\Admin\TestAdmin());
    
    return $service; // So you get fully initialized service
    

    Symfony 3.4+

    What you can do is this:

    // Your services.yaml
    services:
        App/MyController/WantToInjectSerivcesController:
            arguments:
                $admins: !tagged fbeen.admin
    
    // Your controller
    class WantToInjectSerivcesController {
        public function __construct(iterable $admins) {
            foreach ($admins as $admin) {
                // you hot your services here
            }
        }
    }
    

    Bonus autotagging of your services. Lets say all your controllers implements interface AdminInterface.

    // In your extension where you building container or your kernel build method
    $container->registerForAutoconfiguration(AdminInterface::class)->addTag('fbeen.admin');
    

    This will tag automatically all services which implement your interface with tag. So you don't need to set tag explicitly.