How to access an application parameters from a service?

后端 未结 9 742
梦如初夏
梦如初夏 2020-12-23 10:49

From my controllers, I access the application parameters (those in /app/config) with

$this->container->getParameter(\'my_param\')
<         


        
相关标签:
9条回答
  • 2020-12-23 11:24

    You can pass parameters to your service in the same way as you inject other services, by specifying them in your service definition. For example, in YAML:

    services:
        my_service:
            class:  My\Bundle\Service\MyService
            arguments: [%my_param1%, %my_param2%]
    

    where the %my_param1% etc corresponds to a parameter named my_param1. Then your service class constructor could then be:

    public function __construct($myParam1, $myParam2)
    {
        // ...
    }
    
    0 讨论(0)
  • 2020-12-23 11:28

    Symfony 3.4 here.

    After some researches, I don't think passing parameters to a class/service via it's constructor, is always a good idea. Imagine if you need to pass to a controller/service some more parameters than 2 or 3. What then? Would be ridiculous to pass, let's say, up to 10 parameters.

    Instead, use the ParameterBag class as a dependency, when declaring the service in yml, and then use as many parameters as you wish.

    A concrete example, let's say you have a mailer service, like PHPMailer, and you want to have the PHPMailer connection parameters in the paramters.yml file:

    #parameters.yml
    parameters:
        mail_admin: abc@abc.abc
        mail_host: mail.abc.com
        mail_username: noreply@abc.com
        mail_password: pass
        mail_from: contact@abc.com
        mail_from_name: contact@abc.com
        mail_smtp_secure: 'ssl'
        mail_port: 465
    
    #services.yml
    services:
        app.php_mailer:
            class: AppBundle\Services\PHPMailerService
            arguments: ['@assetic.parameter_bag'] #here one could have other services to be injected
            public: true
    
    # AppBundle\Services\PHPMailerService.php
    ...
    use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
    ...
    class PHPMailerService
    {
        private $parameterBag;
        private $mailAdmin;
        private $mailHost;
        private $mailUsername;
        private $mailPassword;
        private $mailFrom;
        private $mailFromName;
        private $mailSMTPSecure;
        private $mailPort;
    }
    public function __construct(ParameterBag $parameterBag)
    {
        $this->parameterBag = $parameterBag;
    
        $this->mailAdmin      = $this->parameterBag->get('mail_admin');
        $this->mailHost       = $this->parameterBag->get('mail_host');
        $this->mailUsername   = $this->parameterBag->get('mail_username');
        $this->mailPassword   = $this->parameterBag->get('mail_password');
        $this->mailFrom       = $this->parameterBag->get('mail_from');
        $this->mailFromName   = $this->parameterBag->get('mail_from_name');
        $this->mailSMTPSecure = $this->parameterBag->get('mail_smtp_secure');
        $this->mailPort       = $this->parameterBag->get('mail_port');
    }
    public function sendEmail()
    {
        //...
    }
    

    I think this is a better way.

    0 讨论(0)
  • 2020-12-23 11:30

    The Clean Way 2018

    Since 2017 and Symfony 3.4 there is much cleaner way - easy to setup and use.

    Instead of using container and service/parameter locator anti-pattern, you can pass parameters to class via it's constructor. Don't worry, it's not time-demanding work, but rather setup once & forget approach.

    How to set it up in 2 steps?

    1. config.yml

    # config.yml
    parameters:
        api_pass: 'secret_password'
        api_user: 'my_name'
    
    services:
        _defaults:
            autowire: true
            bind:
                $apiPass: '%api_pass%'
                $apiUser: '%api_user%'
    
        App\:
            resource: ..
    

    2. Any Controller

    <?php declare(strict_types=1);
    
    final class ApiController extends SymfonyController
    {
        /**
         * @var string 
         */
        private $apiPass;
    
        /**
         * @var string
         */
        private $apiUser;
    
        public function __construct(string $apiPass, string $apiUser)
        {
            $this->apiPass = $apiPass;
            $this->apiUser = $apiUser;
        }
    
        public function registerAction(): void
        {
            var_dump($this->apiPass); // "secret_password"
            var_dump($this->apiUser); // "my_name"
        }
    }
    

    Instant Upgrade Ready!

    In case you use older approach, you can automate it with Rector.

    Read More

    This is called constructor injection over services locator approach.

    To read more about this, check my post How to Get Parameter in Symfony Controller the Clean Way.

    (It's tested and I keep it updated for new Symfony major version (5, 6...)).

    0 讨论(0)
  • 2020-12-23 11:32

    Instead of mapping your needed parameters one by one, why not allowing your service to access the container directly? Doing so, you do not have to update your mapping if there is new parameters added (which relate to your service).

    To do so:

    Make following changes to your service class

    use Symfony\Component\DependencyInjection\ContainerInterface; // <- Add this
    
    class MyServiceClass
    {
        private $container; // <- Add this
        public function __construct(ContainerInterface $container) // <- Add this
        {
            $this->container = $container;
        }
        public function doSomething()
        {
            $this->container->getParameter('param_name_1'); // <- Access your param
        }
    }
    

    Add @service_container as "arguments" in your services.yml

    services:
      my_service_id:
        class: ...\MyServiceClass
        arguments: ["@service_container"]  // <- Add this
    
    0 讨论(0)
  • 2020-12-23 11:35

    @richsage is correct (for Symfony 3.?) but it did not work for my Symfony 4.x. So here is for Symfony 4.

    in services.yaml file

    parameters:
        param1: 'hello'
    
    Services:
        App\Service\routineCheck:
                arguments:
                    $toBechecked: '%param1%'  # argument must match in class constructor
    

    in your service class routineCheck.php file do constructor like so

    private $toBechecked;
    
    public function __construct($toBechecked)
    {
        $this->toBechecked = $toBechecked;
    }
    
    public function echoSomething()
    {
        echo $this->toBechecked;
    }
    

    Done.

    0 讨论(0)
  • 2020-12-23 11:37

    As solution to some of issues mentioned, I define an array parameter then inject it. Adding a new parameter later just requires addition to parameter array without any change to service_container arguments or construct.

    So extending on @richsage answer:

    parameters.yml

    parameters:
        array_param_name:
            param_name_1:   "value"
            param_name_2:   "value"
    

    services.yml

    services:
        my_service:
            class:  My\Bundle\Service\MyService
            arguments: [%array_param_name%]
    

    Then access inside class

    public function __construct($params)
    {
        $this->param1 = array_key_exists('param_name_1',$params)
            ? $params['param_name_1'] : null;
        // ...
    }
    
    0 讨论(0)
提交回复
热议问题