【问题标题】:Inject a Symfony service specified in config.yml into a bundle's service将 config.yml 中指定的 Symfony 服务注入到包的服务中
【发布时间】:2016-11-07 07:13:29
【问题描述】:

我有一个 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: 您请求了一个不存在的服务“缓存”。

即使在 config/services.yml 我已经定义:

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

文件首先加载。

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

谢谢! :-)

【问题讨论】:

    标签: php symfony dependency-injection bundle


    【解决方案1】:

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

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

    这样的引用会在服务实例化的时候被解析,必要时实例化缓存。

    【讨论】:

      【解决方案2】:

      在编译器通道而不是扩展中执行此操作。 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:

      // Wolnosciowiec\FileRepositoryBundle:
      
      public function build(ContainerBuilder $container)
      {
          parent::build($container);
      
          $container->addCompilerPass(new CacheCompilerPass());
      }
      

      【讨论】:

      • [Symfony\Component\DependencyInjection\Exception\RuntimeException] 如果参数是对象或资源,则无法转储服务容器。这也行不通:)
      • 抱歉,忘记了您必须在编译器传递中使用引用而不是实际服务。编辑了我的答案。现在您将能够使用服务名称而不是其类。
      【解决方案3】:

      我在这里找到了一个线索: 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 传递一个类名而不是服务名。它工作得很好,但如果我也能传递服务 ID 那就更好了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-01-22
        • 2017-08-08
        • 1970-01-01
        • 2016-07-15
        • 1970-01-01
        • 2012-08-26
        • 1970-01-01
        • 2016-05-09
        相关资源
        最近更新 更多