Symfony2: How to inject ALL parameters in a service?

28,658

Solution 1

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:

config.yml:

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

And inside MyService\Class:

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

Solution 2

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:

 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:

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

3) In may service I can access these parameters like:

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. For example in 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'

Solution 3

Another variant how to get parameters easy - you can just set ParameterBag to your service. You can do it in different ways - via arguments or via set methods. Let me show my example with set method.

So in services.yml you should add something like:

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

and in class MyService\Class just add use:

use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;

and create 2 methods:

/**                                                                                                                                                                      
 * 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');

Solution 4

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. That way you can reuse your service class outside your Symfony project. Something that is useful when testing your service class.

Solution 5

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.

You can look at xxxProjectContainer in your cache directory. 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.

I suspect you could also do it with a compiler pass but have never tried.

Share:
28,658
Tony Bogdanov
Author by

Tony Bogdanov

Updated on June 14, 2020

Comments

  • Tony Bogdanov
    Tony Bogdanov almost 4 years

    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.

    My question is, how to inject everything that is under parameters in the service?

    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');, but I think this would be against the idea of Dependency Injection?

    Thanks in advance!