将 config.yml 中指定的 Symfony 服务注入到包的服务中
Inject a Symfony service specified in config.yml into a bundle's service
我有一个 config.yml 允许开发人员传递缓存服务名称的地方。
file_repository:
cache_service: "cache"
现在我有了包配置
<?php
namespace Wolnosciowiec\FileRepositoryBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* @package Wolnosciowiec\FileRepositoryBundle\DependencyInjection
*/
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('file_repository');
$rootNode
->children()
->scalarNode('cache_service')
->isRequired()
->end()
->end()
;
return $treeBuilder;
}
}
我有扩展名:
<?php
namespace Wolnosciowiec\FileRepositoryBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
/**
* @package Wolnosciowiec\FileRepositoryBundle\DependencyInjection
*/
class FileRepositoryExtension extends Extension
{
/**
* Load configuration definition from "Configuration.php"
*
* @param array $configs
* @param ContainerBuilder $container
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');
// inject the cache service into the Comrade Reader
$comrade = $container->getDefinition('wolnosciowiec.comrade.reader');
$comrade->addMethodCall('setCache', $container->get($config['cache_service']));
}
}
在最后几行中,我在 setCache() 方法中将服务(在配置中指定)注入到包的内部服务中。
但我明白了:
ContainerBuilder.php 行 816 中的 ServiceNotFoundException:
您请求了一项不存在的服务 "cache"。
即使在config/services.yml中我已经定义:
services:
cache:
class: Doctrine\Common\Cache\ApcuCache
文件首先加载。
如何将 switchable/configurable 服务正确地注入到捆绑服务中?
谢谢! :-)
在 Compiler Pass 而不是 Extension 中执行。 Extension 仅加载配置,尚未将其处理到 Container Builder 中。 Compiler Passes 负责操纵服务定义,如 in the documentation.
所述
首先,在扩展中,必须读取配置入口并将其作为参数存储在容器中:
public function load(array $configs, ContainerBuilder $container)
{
// ...
$container->setParameter('cache_service', $config['cache_service']);
}
然后,创建您的 Compiler Pass 并在那里配置您的服务。由于服务尚未实例化,因此您必须使用对您的服务的引用。从容器中检索服务的名称:
// Wolnosciowiec\DependencyInjection\CacheCompilerPass:
use Symfony\Component\DependencyInjection\Reference;
class CacheCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
// get the service name from the container
$id = $container->getParameter('cache_service');
// inject the cache service into the Comrade Reader
$comrade = $container->getDefinition('wolnosciowiec.comrade.reader');
$comrade->addMethodCall('setCache', new Reference($id)));
}
}
然后,在您的 Bundle 中注册您的 Compiler Pass class:
// Wolnosciowiec\FileRepositoryBundle:
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new CacheCompilerPass());
}
我在这里找到了一条线索:
Alter service (ClientManager) based on configuration inside bundle extension class
所以,最后通过一个妥协,编译器传递看起来像这样:
<?php
namespace Wolnosciowiec\FileRepositoryBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\Serializer\Serializer;
class CacheCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$config = $container->getExtensionConfig('file_repository');
// inject the cache service into the Comrade Reader
$comrade = $container->getDefinition('wolnosciowiec.comrade.reader');
$comrade->addMethodCall('setUrl', [$config[0]['url']]);
$comrade->addMethodCall('setToken', [$config[0]['token']]);
// serializer
$serializer = new Definition(Serializer::class, []);
$serializer->setPublic(false);
$container->setDefinition('wolnosciowiec.file_repository.serializer', $serializer);
$comrade->addMethodCall('setSerializer', array($serializer));
// cache
$cache = new Definition($config[0]['cache_class'], []);
$cache->setPublic(false);
$container->setDefinition('wolnosciowiec.file_repository.cache', $cache);
$comrade->addMethodCall('setCache', array($cache));
}
}
妥协我的意思是我必须从 config.yml 传递一个 class 名称而不是服务名称。它工作得很好,但如果我也能够传递服务 ID 会更好。
构建容器时,您无法从容器中获取服务,只能获取服务定义。您要做的是:
use Symfony\Component\DependencyInjection\Reference;
...
$comrade->addMethodCall('setCache', new Reference($config['cache_service']));
...
实例化服务时会解析此类引用,必要时实例化缓存。
我有一个 config.yml 允许开发人员传递缓存服务名称的地方。
file_repository:
cache_service: "cache"
现在我有了包配置
<?php
namespace Wolnosciowiec\FileRepositoryBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* @package Wolnosciowiec\FileRepositoryBundle\DependencyInjection
*/
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('file_repository');
$rootNode
->children()
->scalarNode('cache_service')
->isRequired()
->end()
->end()
;
return $treeBuilder;
}
}
我有扩展名:
<?php
namespace Wolnosciowiec\FileRepositoryBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
/**
* @package Wolnosciowiec\FileRepositoryBundle\DependencyInjection
*/
class FileRepositoryExtension extends Extension
{
/**
* Load configuration definition from "Configuration.php"
*
* @param array $configs
* @param ContainerBuilder $container
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');
// inject the cache service into the Comrade Reader
$comrade = $container->getDefinition('wolnosciowiec.comrade.reader');
$comrade->addMethodCall('setCache', $container->get($config['cache_service']));
}
}
在最后几行中,我在 setCache() 方法中将服务(在配置中指定)注入到包的内部服务中。
但我明白了:
ContainerBuilder.php 行 816 中的 ServiceNotFoundException: 您请求了一项不存在的服务 "cache"。
即使在config/services.yml中我已经定义:
services:
cache:
class: Doctrine\Common\Cache\ApcuCache
文件首先加载。
如何将 switchable/configurable 服务正确地注入到捆绑服务中?
谢谢! :-)
在 Compiler Pass 而不是 Extension 中执行。 Extension 仅加载配置,尚未将其处理到 Container Builder 中。 Compiler Passes 负责操纵服务定义,如 in the documentation.
所述首先,在扩展中,必须读取配置入口并将其作为参数存储在容器中:
public function load(array $configs, ContainerBuilder $container)
{
// ...
$container->setParameter('cache_service', $config['cache_service']);
}
然后,创建您的 Compiler Pass 并在那里配置您的服务。由于服务尚未实例化,因此您必须使用对您的服务的引用。从容器中检索服务的名称:
// Wolnosciowiec\DependencyInjection\CacheCompilerPass:
use Symfony\Component\DependencyInjection\Reference;
class CacheCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
// get the service name from the container
$id = $container->getParameter('cache_service');
// inject the cache service into the Comrade Reader
$comrade = $container->getDefinition('wolnosciowiec.comrade.reader');
$comrade->addMethodCall('setCache', new Reference($id)));
}
}
然后,在您的 Bundle 中注册您的 Compiler Pass class:
// Wolnosciowiec\FileRepositoryBundle:
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new CacheCompilerPass());
}
我在这里找到了一条线索: Alter service (ClientManager) based on configuration inside bundle extension class
所以,最后通过一个妥协,编译器传递看起来像这样:
<?php
namespace Wolnosciowiec\FileRepositoryBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\Serializer\Serializer;
class CacheCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$config = $container->getExtensionConfig('file_repository');
// inject the cache service into the Comrade Reader
$comrade = $container->getDefinition('wolnosciowiec.comrade.reader');
$comrade->addMethodCall('setUrl', [$config[0]['url']]);
$comrade->addMethodCall('setToken', [$config[0]['token']]);
// serializer
$serializer = new Definition(Serializer::class, []);
$serializer->setPublic(false);
$container->setDefinition('wolnosciowiec.file_repository.serializer', $serializer);
$comrade->addMethodCall('setSerializer', array($serializer));
// cache
$cache = new Definition($config[0]['cache_class'], []);
$cache->setPublic(false);
$container->setDefinition('wolnosciowiec.file_repository.cache', $cache);
$comrade->addMethodCall('setCache', array($cache));
}
}
妥协我的意思是我必须从 config.yml 传递一个 class 名称而不是服务名称。它工作得很好,但如果我也能够传递服务 ID 会更好。
构建容器时,您无法从容器中获取服务,只能获取服务定义。您要做的是:
use Symfony\Component\DependencyInjection\Reference;
...
$comrade->addMethodCall('setCache', new Reference($config['cache_service']));
...
实例化服务时会解析此类引用,必要时实例化缓存。