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

I have a parameter which should represent 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 inject this collection to another service as an array of services:

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

But it doesn't work. Can't understand why. What am I missing?



Solution 1:[1]

Little bit different approach with tagged services (or whatever you need) and CompilerPassInterface using array of services instead of method calls. Here are the differences from @NHG 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);
    }
}

Solution 2:[2]

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 receive the array...

Solution 3:[3]

Symfony 5.3+ and php 8.0+

class NotificationsDecorator
{
    private $decorators;

    public function __construct(
        #[TaggedIterator('app.notifications_decorators')] iterable $decorators
    ) {
        $this->decorators = $decorators;
    }
}

#[Autoconfigure(tags: ['app.notifications_decorators'])]
interface DecoratorInterface
{
}

class FirstDecorator implements DecoratorInterface
{
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 mente
Solution 2 rrubiorr81
Solution 3 Artem