简体   繁体   English

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

[英]Symfony2: How to inject ALL parameters in a service?

How can I inject ALL parameters in a service? 如何在服务中注入所有参数?

I know I can do: arguments: [%some.key%] which will pass the parameters: some.key: "value" to the service __construct. 我知道我可以做到: arguments: [%some.key%]它将把parameters: some.key: "value"传递给服务__construct。

My question is, how to inject everything that is under parameters in the service? 我的问题是,如何注入服务中parameters下的所有内容?

I need this in order to make a navigation manager service, where different menus / navigations / breadcrumbs are to be generated according to different settings through all of the configuration entries. 我需要这样做是为了提供导航管理器服务,其中通过所有配置条目根据不同的设置生成不同的菜单/导航/面包屑。

I know I could inject as many parameters as I want, but since it is going to use a number of them and is going to expand as time goes, I think its better to pass the whole thing right in the beginning. 我知道我可以根据需要注入任意数量的参数,但是由于它将使用多个参数,并且随着时间的推移会不断扩展,因此我认为最好一开始就将整个过程传递出去。

Other approach might be if I could get the parameters inside the service as you can do in a controller $this -> container -> getParameter('some.key'); 其他方法可能是,如果可以像在控制器$this -> container -> getParameter('some.key');那样获取服务内部的参数$this -> container -> getParameter('some.key'); , but I think this would be against the idea of Dependency Injection? ,但是我认为这与依赖注入的想法背道而驰吗?

Thanks in advance! 提前致谢!

It is not a good practice to inject the entire Container into a service. 将整个容器注入服务中不是一个好习惯。 Also if you have many parameters that you need for your service it is not nice to inject all of them one by one to your service. 同样,如果您有许多服务所需的参数,那么将所有这些参数一一注入到服务中也是不好的。 Instead I use this method: 相反,我使用这种方法:

1) In config.yml I define the parameters that I need for my service like this: 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) Then I inject this root parameter to my service like: 2)然后将这个根参数注入到我的服务中,例如:

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

3) In may service I can access these parameters like: 3)在May服务中,我可以访问以下参数:

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) I can also set different values for different environments. 4)我也可以为不同的环境设置不同的值。 For example in config_dev.yml 例如在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'

Another variant how to get parameters easy - you can just set ParameterBag to your service. 如何轻松获取参数的另一个变体-您只需将ParameterBag设置为服务即可。 You can do it in different ways - via arguments or via set methods. 您可以通过不同的方式进行操作-通过参数或通过set方法。 Let me show my example with set method. 让我用set方法显示示例。

So in services.yml you should add something like: 因此,在services.yml中,您应该添加以下内容:

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

and in class MyService\\Class just add use: 并在类MyService \\ Class中添加使用:

use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;

and create 2 methods: 并创建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);
}

and now you can use in class: 现在您可以在课堂上使用:

$this->getParameter('your_parameter_name');

I believe you're supposed to pass the parameters individually. 我相信您应该分别传递参数。 I think it's made that way by design so your service class is not dependent on the AppKernel. 我认为它是通过设计实现的,因此您的服务类不依赖于AppKernel。 That way you can reuse your service class outside your Symfony project. 这样,您可以在Symfony项目之外重用服务类。 Something that is useful when testing your service class. 测试服务类时有用的东西。

AppKernel would work but it's even worse (from a scope perspective) than injecting the container since the kernel has even more stuff in it. AppKernel可以工作,但是(从作用域的角度来看)它比注入容器还要糟糕,因为内核中有更多东西。

You can look at xxxProjectContainer in your cache directory. 您可以在缓存目录中查看xxxProjectContainer。 Turns out that the assorted parameters are compiled directly into it as a big array. 原来,各种参数作为一个大数组直接编译到其中。 So you could inject the container and then just pull out the parameters. 因此,您可以注入容器,然后仅提取参数。 Violates the letter of the law but not the spirit of the law. 违反法律条文,但不违反法律精神。

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

And just sort of messing around I found I could do this: 只是一团糟,我发现我可以做到这一点:

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

So you could actually create a service that had basically a empty copy of the master container and inject it just to get the parameters. 因此,您实际上可以创建一个服务,该服务基本上具有一个主容器的空副本,然后注入它只是为了获取参数。 Have to take into account the dev/debug flags which might be a pain. 必须考虑可能会很痛苦的dev / debug标志。

I suspect you could also do it with a compiler pass but have never tried. 我怀疑您也可以通过编译器传递来实现,但是从未尝试过。

Note: I know that this solution is not BEST from design point of view, but it does the job, so please avoid down-voting. 注意:从设计的角度来看,我知道此解决方案不是最佳解决方案,但它确实可以完成工作,因此请避免投票不足。

You can inject \\AppKernel object and then access all parameters like this: 您可以注入\\ AppKernel对象,然后像这样访问所有参数:

config.yml: config.yml:

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

And inside MyService\\Class : MyService\\Class内部:

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

Suggestion to define a service at services.yml, which will inject the parameterBag and allow access to any of your parameter 建议在services.yml中定义服务,这将注入parameterBag并允许访问您的任何参数

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

Inject your service, and u can get your parameter using below 注入您的服务,您可以使用以下方法获取参数

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

As alternative approach would be that you can actually inject application parameters into your service via Container->getParameterBag in you bundle DI Extension 作为替代方法,您可以通过捆绑包DI扩展中的Container-> getParameterBag实际上将应用程序参数注入服务中

    <?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()));

        }
}

Please note that we can not inject ParameterBag object directly, cause it throws: 请注意,我们不能直接注入ParameterBag对象,因为它引发:

[Symfony\\Component\\DependencyInjection\\Exception\\RuntimeException] [Symfony \\ Component \\ DependencyInjection \\ Exception \\ RuntimeException]
Unable to dump a service container if a parameter is an object or a resource. 如果参数是对象或资源,则无法转储服务容器。

Tested under Symfony version 2.3.4 在Symfony 2.3.4版下测试

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM