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?
So, you inject array of parameters no array of services. You can inject service by service via:
<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 better way) tag decorators
services and inject them to notifications_decorator
during compilation passes.
UPDATE: Working with Tagged Services
In your case you have to modify your services like this:
<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>
Additionaly you should remove decorators.all
parameter from <parameters>
section. Next, you have to add sth like addDectorator
function for \NotificationsDecorator
:
class NotificationsDecorator
{
private $decorators = array();
public function addDecorator($decorator)
{
$this->decorators[] = $decorator;
}
// more code
}
It would be great if you write some interface for decorator
's and add this as type of $decorator
for addDecorator
function.
Next, you have to write own compiler pass and ask them about tagged services and add this services to another one (simillarly 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 bundle class like:
class AcmeDemoBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new DecoratorCompilerPass());
}
}
Good luck!
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With