代码之家  ›  专栏  ›  技术社区  ›  Krzysztof Wesołowski

注入配置中指定的Symfony服务。yml进入捆绑服务

  •  0
  • Krzysztof Wesołowski  · 技术社区  · 9 年前

    我有一个配置。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()方法中将服务(在配置中指定)注入到bundle的内部服务中。

    ContainerBuilder中的ServiceNotFoundException。php第816行: 您已请求不存在的服务“缓存”。

    即使在配置/服务中。yml I已定义:

    services:
        cache:
            class: Doctrine\Common\Cache\ApcuCache
    

    首先加载文件。

    如何将可切换/可配置服务正确地注入捆绑包的服务?

    谢谢!:-)

    3 回复  |  直到 9 年前
        1
  •  1
  •   dbu    6 年前

    构建容器时,不能从容器中获取服务,只能获取服务定义。你想做的是:

    use Symfony\Component\DependencyInjection\Reference;
    
    ...
    $comrade->addMethodCall('setCache', new Reference($config['cache_service']));
    ...
    

    在实例化服务时,将解析此类引用,并在必要时实例化缓存。

        2
  •  1
  •   Bartosz Zasada    9 年前

    在编译器传递而不是扩展中执行。扩展只加载配置,还没有将其处理到容器生成器中。正如所描述的,编译器传递了操作服务定义的责任 in the documentation .

    public function load(array $configs, ContainerBuilder $container)
    {
        // ...
        $container->setParameter('cache_service', $config['cache_service']);
    }
    

    然后,创建编译器传递并在那里配置服务。由于服务尚未实例化,因此必须使用对服务的引用。从容器中检索服务的名称:

    // 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类中注册编译器传递:

    // Wolnosciowiec\FileRepositoryBundle:
    
    public function build(ContainerBuilder $container)
    {
        parent::build($container);
    
        $container->addCompilerPass(new CacheCompilerPass());
    }
    
        3
  •  0
  •   Krzysztof Wesołowski    9 年前

    我在这里找到了一条线索: 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));
        }
    }
    

    我的意思是,我必须从配置中传递一个类名。yml而不是服务名称。它工作得很好,但如果我也能传递服务id,那就更好了。

    推荐文章