我有一个服务,让司机做实际的工作。驱动程序本身在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_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
的值现在等于我在my_scope.mailer_driver_smtp
中设置的parameters.yml
,因为我对它的理解是正确的,只是一个字符串。
如果我现在使用容器中的参数名称,我会收到一个错误,抱怨没有这样的服务。 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
答案 0 :(得分:5)
此问题的回答类似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.driver
中定义的服务与my_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
通过这样的设计,只要您更改 config.yml 中的my_scope.mailer_driver
参数,服务就会发生变化。
请注意,如果服务不存在,扩展将引发异常。
答案 1 :(得分:5)
使用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'))]]
答案 2 :(得分:3)
起初我认为这只是让@符号正确传递的问题。但我尝试了各种组合,并得出结论,你不能将实际服务作为参数传递。也许其他人会插话并展示如何做到这一点。
那么我认为只是使用服务定义并将其传递给引用的问题。起初我在通常的扩展中尝试了这个,但容器还没有包含所有的服务定义。
所以我使用了编译器传递:http://symfony.com/doc/current/cookbook/service_container/compiler_passes.html
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)));
}
}
从服务文件中取出调用部分,它应该可以工作。我怀疑有一种更简单的方法,但也许不是。
答案 3 :(得分:0)
@my_scope.mailer.driver
需要是服务,但未定义为服务。要检索名为my_scope.mailer.driver
的字符串参数,您需要使用%
包裹它:%my_scope.mailer.driver%
。
因此,您需要将@%my_scope.mailer.driver%
作为参数传递给服务。 Yml解析器将使用适当的参数值替换%my_scope.mailer.driver%
,然后才会将其作为服务调用。