Symfony2 service container: Inject array of services parameter as an argument to another service using XML

I have a parameter that should represent an array of services in my services.xml file:

 <parameters> <parameter key="decorators.all" type="collection"> <parameter type="service" id="decorator1" /> <parameter type="service" id="decorator2" /> <parameter type="service" id="decorator3" /> </parameter> </parameters> <services> <service id="decorator1" class="\FirstDecorator" /> <service id="decorator2" class="\SecondDecorator" /> <service id="decorator3" class="\ThirdDecorator" /> </services> 

Now I want to add this collection to another service as an array of services:

 <services> <service id="notifications_decorator" class="\NotificationsDecorator"> <argument>%decorators.all%</argument> </service> </services> 

But that will not work. I can’t understand why. What am I missing?

+4
source share
3 answers

So you add an array of parameters without an array of services. You can enter the service through the service:

 <services> <service id="notifications_decorator" class="\NotificationsDecorator"> <argument type="service" id="decorator1"/> <argument type="service" id="decorator2"/> <argument type="service" id="decorator3"/> </service> </services> 

Or (in my opinion, the best way) tag decorators services and insert their notifications_decorator at compile time.

UPDATE: working with Tagged Services

In your case, you need to change your services as follows:

 <services> <service id="decorator1" class="\FirstDecorator"> <tag name="acme_decorator" /> </service> <service id="decorator2" class="\SecondDecorator"> <tag name="acme_decorator" /> </service> <service id="decorator3" class="\ThirdDecorator"> <tag name="acme_decorator" /> </service> </services> 

Additionally, you must remove the decorators.all parameter from the <parameters> section. Then you need to add the sth function as addDectorator for \NotificationsDecorator :

 class NotificationsDecorator { private $decorators = array(); public function addDecorator($decorator) { $this->decorators[] = $decorator; } // more code } 

It would be great if you wrote some interface for decorator and added it as a function type of $decorator for addDecorator .

Then you need to write your own compiler pass and ask them about the marked services and add these services to another (similar to doc):

 use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Reference; class DecoratorCompilerPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { if (!$container->hasDefinition('notifications_decorator')) { return; } $definition = $container->getDefinition('notifications_decorator'); $taggedServices = $container->findTaggedServiceIds('acme_decorator'); foreach ($taggedServices as $id => $attributes) { $definition->addMethodCall( 'addDecorator', array(new Reference($id)) ); } } } 

Finally, you should add your DecoratorCompilerPass to Compiler in your package class, for example:

 class AcmeDemoBundle extends Bundle { public function build(ContainerBuilder $container) { parent::build($container); $container->addCompilerPass(new DecoratorCompilerPass()); } } 

Good luck

+5
source

A slightly different approach with labeled services (or whatever you need) and CompilerPassInterface using an array of services instead of method calls. Here are the differences from @NHG's answer:

 <!-- Service definition (factory in my case) --> <service id="example.factory" class="My\Example\SelectorFactory"> <argument type="collection" /> <!-- list of services to be inserted by compiler pass --> </service> 

CompilerPass:

 /* * Used to build up factory with array of tagged services definition */ class ExampleCompilerPass implements CompilerPassInterface { const SELECTOR_TAG = 'tagged_service'; public function process(ContainerBuilder $container) { $selectorFactory = $container->getDefinition('example.factory'); $selectors = []; foreach ($container->findTaggedServiceIds(self::SELECTOR_TAG) as $selectorId => $tags) { $selectors[] = $container->getDefinition($selectorId); } $selectorFactory->replaceArgument(0, $selectors); } } 
+2
source

in yaml you can do:

 app.example_conditions: class: AppBundle\Example\Conditions arguments: [[ "@app.example_condition_1", "@app.example_condition_2", "@app.example_condition_3", "@app.example_condition_4" ]] 

and in AppBundle\Example\Conditions you get an array ...

0
source

Source: https://habr.com/ru/post/1500659/


All Articles