我有一个参数,应该代表我services.xml
文件中的服务数组:
<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>
现在我想将此集合作为一组服务注入另一个服务:
<services>
<service id="notifications_decorator" class="\NotificationsDecorator">
<argument>%decorators.all%</argument>
</service>
</services>
但它不起作用。无法理解为什么。我错过了什么?
答案 0 :(得分:5)
因此,您注入的参数数组没有服务数组。您可以通过以下服务注入服务:
<services>
<service id="notifications_decorator" class="\NotificationsDecorator">
<argument type="service" id="decorator1"/>
<argument type="service" id="decorator2"/>
<argument type="service" id="decorator3"/>
</service>
</services>
或(在我看来更好的方式)tag decorators
服务并在编译过程中将它们注入notifications_decorator
。
更新:使用标记服务
在您的情况下,您必须像这样修改您的服务:
<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>
另外,您应该从decorators.all
部分删除<parameters>
参数。接下来,您必须为addDectorator
添加\NotificationsDecorator
函数:
class NotificationsDecorator
{
private $decorators = array();
public function addDecorator($decorator)
{
$this->decorators[] = $decorator;
}
// more code
}
如果您为decorator
编写一些界面并将其添加为$decorator
函数的addDecorator
类型,那就太棒了。
接下来,你必须编写自己的编译器传递并向他们询问有关标记服务的信息,并将这些服务添加到另一个服务(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))
);
}
}
}
最后,您应该在捆绑类中将DecoratorCompilerPass
添加到Compiler
,如:
class AcmeDemoBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new DecoratorCompilerPass());
}
}
祝你好运!
答案 1 :(得分:2)
使用标记服务(或任何您需要的)和CompilerPassInterface
使用服务数组而不是方法调用的一点点不同方法。
以下是与@NHG回答的区别:
<!-- 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 :(得分:0)
你可以做到:
app.example_conditions:
class: AppBundle\Example\Conditions
arguments:
[[ "@app.example_condition_1", "@app.example_condition_2", "@app.example_condition_3", "@app.example_condition_4" ]]
并在AppBundle\Example\Conditions
中收到数组......