Symfony 将基于参数的服务注入到其他服务中

Symfony 将基于参数的服务注入到其他服务中,symfony,configuration,dependency-injection,Symfony,Configuration,Dependency Injection,我有一项服务,需要司机来做实际工作。驱动程序本身在Symfony 2的上下文中,只是另一种服务 要说明简化版本,请执行以下操作: services: # The driver services. my_scope.mailer_driver_smtp: class: \My\Scope\Service\Driver\SmtpDriver my_scope.mailer_driver_mock: class: \My\Scope\Service\Driver\Mock

我有一项服务,需要司机来做实际工作。驱动程序本身在Symfony 2的上下文中,只是另一种服务

要说明简化版本,请执行以下操作:

services:
  # The driver services.
  my_scope.mailer_driver_smtp:
    class: \My\Scope\Service\Driver\SmtpDriver

  my_scope.mailer_driver_mock:
    class: \My\Scope\Service\Driver\MockDriver

  # The actual service.
  my_scope.mailer:
    class: \My\Scope\Service\Mailer
    calls:
      - [setDriver, [@my_scope.mailer_driver_smtp]]
如上所示,我可以将两个驱动程序服务中的任何一个注入到Mailer服务中。问题当然是注入的驱动程序服务是硬编码的。因此,我想参数化
@my\u scope.mailer\u driver\u smtp

我通过向我的
parameters.yml

my_scope_mailer_driver: my_scope.mailer_driver_smtp
然后我可以在我的
config.yml
中使用它,并将参数分配给语义公开配置[1]:

my_scope:
  mailer:
    driver: %my_scope_mailer_driver%
最后,在我的bundle的
配置
类中,我在容器上设置了一个参数:

$container->setParameter('my_scope.mailer.driver', $config['mailer']['driver'] );
容器参数
my_scope.mailer.driver
的值现在等于我在
parameters.yml
中设置的
my_scope.mailer\u driver\u smtp
,我对它的理解是正确的,它只是一个字符串

如果我现在使用容器中的参数名,我会收到一个错误,抱怨没有这样的服务。例如:

services:
  my_scope.mailer:
    class: \My\Scope\Service\Mailer
    calls:
      - [setDriver, [@my_scope.mailer.driver]]
上述操作将导致错误:

[Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException]                                          
The service "my_scope.mailer" has a dependency on a non-existent service "my_scope.mailer.driver"
现在的问题是,注入这个基于容器参数的服务的正确语法是什么


[1]
@my_scope.mailer.driver
需要是服务,但不定义为服务。要检索名为
my\u scope.mailer.driver
的字符串参数,您需要用
%
将其包装起来:
%my\u scope.mailer.driver%


因此,您需要将
@%my\u scope.mailer.driver%
作为参数传递给服务。Yml解析器将用适当的参数值替换
%my_scope.mailer.driver%
,然后它将作为服务调用。

起初,我认为这只是正确传递@符号的问题。但我尝试了各种组合,得出的结论是不能将实际服务作为参数传递。也许其他人会插话告诉你怎么做

因此,我认为这只是使用服务定义并向其传递引用的问题。起初,我在通常的扩展中尝试了这一点,但是容器还没有包含所有的服务定义

所以我使用了一个编译器过程:

Pass类看起来像:

namespace Cerad\Bundle\AppCeradBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class Pass1 implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        // Set in the Extension: my_scope.mailer_driver_smtp
        $mailerDriverId = $container->getParameter('my_scope.mailer.driver');

        $def = $container->getDefinition('my_scope.mailer');

        $def->addMethodCall('setDriver', array(new Reference($mailerDriverId)));
    }
}
从服务文件中删除calls部分,它应该可以工作。我怀疑有一个更简单的方法,但也许不是

这个问题也有类似的答案

我认为使用这种定义的最好方法是使用。
这可能看起来像这样

Acme\FooBundle\DependencyInjection\acmefootextension

这将把您在
my\u scope.mailer.driver
中定义的服务别名为
my\u scope.mailer\u driver
,您可以将其用作任何其他服务

services.yml

有了这样的设计,只要您更改config.yml中的
my\u scope.mailer\u驱动程序
参数,服务就会发生变化。
请注意,如果服务不存在,扩展将引发异常。

您可以访问配置文件中的以下两个函数:

  • 服务-返回给定的服务(参见下面的示例)
  • parameter-返回特定的参数值(语法与服务类似)
因此,要将参数名称转换为服务引用,您需要以下内容:

parameters:
  my_scope_mailer_driver: my_scope.mailer_driver_smtp

services:
  my_scope.mailer:
    class: \My\Scope\Service\Mailer
    calls:
      - [setDriver, [@=service(parameter('my_scope_mailer_driver'))]]

谢谢你。我试过了,但没用。我得到了一个非常类似的错误:`服务“my_scope.mailer”依赖于一个不存在的服务“%my_scope.mailer.driver%”`(注意百分比符号)删除%Signs周围的引号如果没有引号,引号来自异常消息。我使用的是:
[setDriver,[@%my_scope.mailer.driver%]
,它给出了我的评论中显示的错误。
@%my_scope.mailer.driver%
语法当前不起作用。另外
别名:%my\u scope.mailer.driver%
也不起作用。(在Symfony 2.4.2上选中)。您需要使用InsteadInterest。你指的是我之前问过的一个问题的公认答案。你认为你可以在答案中添加上面的链接,这样我就可以接受它作为正确答案吗?谢谢,我刚注意到这个。这是一个非常好的方法。下次我要做这样的事时,我会试试的。
services:
    my_scope.mailer_driver:
        alias: my_scope.mailer_driver_smtp # Fallback

    my_scope.mailer_driver_smtp:
        class: My\Scope\Driver\Smtp

    my_scope.mailer_driver_mock:
        class: My\Scope\Driver\Mock

    my_scope.mailer:
        class: My\Scope\Mailer
        arguments:
            - @my_scope.mailer_driver
parameters:
  my_scope_mailer_driver: my_scope.mailer_driver_smtp

services:
  my_scope.mailer:
    class: \My\Scope\Service\Mailer
    calls:
      - [setDriver, [@=service(parameter('my_scope_mailer_driver'))]]