【问题标题】:Symfony2: How to inject ALL parameters in a service?Symfony2:如何在服务中注入所有参数?
【发布时间】:2012-03-07 04:25:11
【问题描述】:

如何在服务中注入所有参数?

我知道我可以做到:arguments: [%some.key%],它将parameters: some.key: "value" 传递给服务__construct。

我的问题是,如何在服务中注入parameters 下的所有内容?

我需要这个来制作导航管理器服务,其中将通过所有配置条目根据不同的设置生成不同的菜单/导航/面包屑。

我知道我可以根据需要注入任意数量的参数,但由于它会使用其中的许多参数并且会随着时间的推移而扩展,所以我认为最好在一开始就传递整个事情。

其他方法可能是我可以像在控制器 $this -> container -> getParameter('some.key'); 中那样获取服务内部的参数,但我认为这会违背依赖注入的想法?

提前致谢!

【问题讨论】:

标签: php service configuration symfony


【解决方案1】:

注意:我知道从设计的角度来看,这个解决方案不是最好的,但它确实有效,所以请避免投反对票。

您可以注入 \AppKernel 对象,然后像这样访问所有参数:

config.yml:

my_service:
    class: MyService\Class
    arguments: [@kernel]

MyService\Class里面:

public function __construct($kernel)
{
    $this->parameter = $kernel->getContainer()->getParameter('some.key');
    // or to get all:
    $this->parameters = $kernel->getContainer()->getParameterBag()->all();
}

【讨论】:

  • 正是我需要的,我忘记了内核。谢谢!
  • 哇!我有一个完全不同的问题,你的回答解决了我的问题!
  • 注入内核的坏主意
  • 确实,应该尽量少注入。服务最好永远不知道它直接需要的服务/对象以外的任何东西。
  • 在 service.yml 中:app.my_service class: MyClass calls: - [ setContainer, [‘@service_container’] ],然后是 class MyClass implements ContainerAwareInterface 并添加方法 public function setContainer(ContainerInterface $container = null){ $this->container = $container; }。就是这样
【解决方案2】:

AppKernel 可以工作,但它比注入容器更糟糕(从范围的角度来看),因为内核中有更多的东西。

您可以查看缓存目录中的 xxxProjectContainer。事实证明,各种参数作为一个大数组直接编译到其中。所以你可以注入容器,然后只提取参数。违反法律条文,但不违反法律精神。

class MyService {
    public function __construct($container) {
        $this->parameters = $container->parameters; // Then discard container to preclude temptation

我发现我可以这样做:

    $container = new \arbiterDevDebugProjectContainer();
    echo 'Parameter Count ' . count($container->parameters) . "\n";

因此,您实际上可以创建一个服务,该服务基本上具有主容器的一个空副本,并注入它只是为了获取参数。必须考虑可能很痛苦的开发/调试标志。

我怀疑你也可以通过编译器通过但从未尝试过。

【讨论】:

  • 我不建议直接注入容器服务,因为它会破坏依赖注入的概念。
【解决方案3】:

我相信您应该单独传递参数。我认为这是设计使然,因此您的服务类不依赖于 AppKernel。这样你就可以在 Symfony 项目之外重用你的服务类。在测试服务类时有用的东西。

【讨论】:

    【解决方案4】:

    作为替代方法,您实际上可以通过 Container->getParameterBag 在您的捆绑 DI 扩展中将应用程序参数注入您的服务

        <?php
    
        namespace Vendor\ProjectBundle\DependencyInjection;
    
        use Symfony\Component\DependencyInjection\ContainerBuilder;
        use Symfony\Component\Config\FileLocator;
        use Symfony\Component\HttpKernel\DependencyInjection\Extension;
        use Symfony\Component\DependencyInjection\Loader;
    
        /**
         * This is the class that loads and manages your bundle configuration
         *
         * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
         */
        class VendorProjectExtension extends Extension {
    
            /**
             * {@inheritDoc}
             */
            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');
                /** set params for services */
                $container->getDefinition('my.managed.service.one')
                        ->addMethodCall('setContainerParams', array($container->getParameterBag()->all()));
                $container->getDefinition('my.managed.service.etc')
                        ->addMethodCall('setContainerParams', array($container->getParameterBag()->all()));
    
            }
    }
    

    请注意,我们不能直接注入 ParameterBag 对象,因为它会抛出:

    [Symfony\Component\DependencyInjection\Exception\RuntimeException]
    如果参数是对象或对象,则无法转储服务容器 资源。

    在 Symfony 版本 2.3.4 下测试

    【讨论】:

    • 注入 DI 的坏主意
    • $container->getParameterBag()->all() 实际上返回array
    【解决方案5】:

    将整个 Container 注入到服务中并不是一个好习惯。此外,如果您的服务需要许多参数,那么将所有参数一一注入您的服务并不好。相反,我使用这种方法:

    1) 在 config.yml 中,我定义了我的服务所需的参数,如下所示:

     parameters:
        product.shoppingServiceParams:
            parameter1: 'Some data'
            parameter2: 'some data'
            parameter3: 'some data'
            parameter4: 'some data'
            parameter5: 'some data'
            parameter6: 'some data'
    

    2) 然后我将这个根参数注入到我的服务中,例如:

    services:
      product.shoppingService:
        class: Saman\ProductBundle\Service\Shopping
        arguments: [@translator.default, %product.shoppingServiceParams%]
    

    3) 在可能的服务中,我可以访问这些参数,例如:

    namespace Saman\ProductBundle\Service;
    
    use Symfony\Bundle\FrameworkBundle\Translation\Translator;
    
    class Shopping
    {   
        protected $translator;
        protected $parameters;
    
        public function __construct(
            Translator $translator, 
            $parameters
            ) 
        {
            $this->translator = $translator;
            $this->parameters = $parameters;
        }
    
        public function dummyFunction()
        {
            var_dump($this->getParameter('parameter2'));
        }
    
        private function getParameter($key, $default = null)
        {
            if (isset($this->parameters[$key])) {
                return $this->parameters[$key];
            }
    
            return $default;
        }  
    }
    

    4) 我还可以为不同的环境设置不同的值。例如在 config_dev.yml

     parameters:
        product.shoppingServiceParams:
            parameter1: 'Some data for dev'
            parameter2: 'some data for dev'
            parameter3: 'some data for dev'
            parameter4: 'some data for dev'
            parameter5: 'some data for dev'
            parameter6: 'some data'
    

    【讨论】:

    • 绝对是最好的方法恕我直言!
    • @Saman 如果您为未找到的参数返回 null,为什么还要使用 array_key_exists? isset() 会产生相同的结果,只是速度更快。 - 否则很好的答案。
    • 谢谢,是的,你绝对会写
    • 但是如果我想在参数列表中不存在键的情况下抛出异常,那么我必须使用 array_key_exists
    【解决方案6】:

    建议在 services.yml 中定义一个服务,该服务将注入 parameterBag 并允许访问您的任何参数

    service.container_parameters:
      public: false
      class: stdClass
      factory_service: service_container
      factory_method: getParameterBag
    

    注入您的服务,您可以使用下面的方法获取您的参数

    $parameterService->get('some.key');
    

    【讨论】:

      【解决方案7】:

      如何轻松获取参数的另一种变体 - 您只需将 ParameterBag 设置为您的服务。你可以用不同的方式来做——通过参数或通过 set 方法。让我用 set 方法展示我的例子。

      所以在 services.yml 中你应该添加如下内容:

      my_service:
          class: MyService\Class
          calls:
              - [setParameterBag, ["@=service('kernel').getContainer().getParameterBag()"]]
      

      在 MyService\Class 类中添加 use:

      use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
      

      并创建 2 个方法:

      /**                                                                                                                                                                      
       * Set ParameterBag for repository                                                                                                                                       
       *                                                                                                                                                                       
       * @param ParameterBagInterface $params                                                                                                                                  
       */
      public function setParameterBag(ParameterBagInterface $params)
      {
          $this->parameterBag = $params;
      }
      
      /**                                                                                                                                                                      
       * Get parameter from ParameterBag                                                                                                                                       
       *                                                                                                                                                                       
       * @param string $name                                                                                                                                                   
       * @return mixed                                                                                                                                                        
       */
      public function getParameter($name)
      {
          return $this->parameterBag->get($name);
      }
      

      现在你可以在课堂上使用:

      $this->getParameter('your_parameter_name');
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多