2014-03-13 108 views
5

我有一个服务需要驱动程序来完成实际工作。驱动程序本身在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]] 

正如上述说明,我可以注入任意两个司机服务纳入梅勒服务。问题当然是被注入的驱动程序服务是硬编码的。所以,我想参数化@my_scope.mailer_driver_smtp

我通过添加条目到为此我parameters.yml

my_scope_mailer_driver: my_scope.mailer_driver_smtp 

然后我可以在config.yml使用该和参数分配给语义暴露配置[1]:

my_scope: 
    mailer: 
    driver: %my_scope_mailer_driver% 

在最后,在我的包的Configuration类中,我在容器上设置了一个参数:

$container->setParameter('my_scope.mailer.driver', $config['mailer']['driver']); 

容器参数my_scope.mailer.driver的值现在等于我在parameters.yml中设置的my_scope.mailer_driver_smtp,也就是我对此的理解是正确的,只是一个字符串。

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

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] http://symfony.com/doc/current/cookbook/bundles/extension.html

+1

你需要使用[' $ container-> setAlias'](http://stackoverflow.com/a/19356276/1607098)而不是 – Touki

+0

有趣。你指出我之前问过的问题的接受答案。你认为你可以在回答中添加上面的链接,以便我可以接受它作为正确答案吗?谢谢。 – Luke

回答

5

This question has a similar answer here

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

的Acme \ FooBundle \ DependencyInjection \ AcmeFooExtension

public function load(array $configs, ContainerBuilder $container) 
{ 
    $configuration = new Configuration; 
    $config = $this->processConfiguration($configuration, $configs); 

    $loader = new Loader\YamlFileLoader(
     $container, 
     new FileLocator(__DIR__.'/../Resources/config') 
    ); 
    $loader->load('services.yml'); 

    $alias = $config['mailer']['driver']; 
    $container->setAlias('my_scope.mailer_driver', $alias); 
} 

这将别名您在my_scope.mailer.drivermy_scope.mailer_driver定义的服务,您可以像任何其他使用服务

services.yml

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 

有了这样的设计,当你改变my_scope.mailer_driver参数在config.yml服务将改变。
请注意,如果该服务不存在,扩展将引发异常。

0

@my_scope.mailer.driver需要是一个服务,但不限定为服务。要检索名为my_scope.mailer.driver的字符串参数,您需要用%包装它:%my_scope.mailer.driver%

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

+0

谢谢你。我试过这个,但它不起作用。我得到一个非常类似的错误:“服务”my_scope.mailer“依赖于不存在的服务”%my_scope.mailer.driver%“(注意百分号) – Luke

+0

删除%符号的引号 –

+0

没有引号围绕它,引号来自异常消息。我使用这个:'[setDriver,[@%my_scope.mailer.driver%]]',它给出了我的评论中显示的错误。 – Luke

3

起初我以为这只是让@符号正确传入的问题。但我尝试了各种组合,并得出结论:您无法将实际服务作为参数传递。也许别人会加入并展示如何做到这一点。

那么我认为这只是一个使用服务定义并将其传递给参考的问题。起初,我在通常的扩展中尝试了这一点,但容器尚未包含所有的服务定义。

所以我使用的编译过程:http://symfony.com/doc/current/cookbook/service_container/compiler_passes.html

通行证类的样子:

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))); 
    } 
} 

采取电话部分出了服务档案,它应该工作。我怀疑有一种更简单的方法,但可能不是。

4

随着service container expression language您可以访问以下两个功能配置文件:

  • 服务 - 返回给定服务(见下面的例子);
  • 参数 - 返回一个特定的参数值(语法就像服务)

所以要参数名称转换为服务引用你需要的东西是这样的:

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'))]] 
+0

我刚注意到这一点。这是一个非常好的做法。下次我会尝试做这样的事情。 – Luke