phpsymfonysymfony-3.4

how to get configuration parameter into a command


I'm trying to get a configuration parameter into a command.

I have added the parameter in the app/config/config.yml under the parameters key.

I can use it in a Controller using $this->getParameter("PRAMETER_NAME") in an action to do some stuff manually.

I'm writing a command to do the same stuff automatically with a CRON job.

I can't find how to get the parameter in the command.


Solution

  • Argument Binding

    In Symfony 3.4 or later, the recommended approach is to use Autowiring and Argument Binding. Allowing for the declaration of the variable name to be "bound" as an argument for ALL services defined in the config file, without needing to explicitly specify each service the parameter is used in.

    In the same configuration file that defines the command service and autowiring, add the bind option to the _defaults specification, along with the desired variable name for the parameter.

    app/config/services.yml

    parameters:
        PARAMETER_NAME: 'test'
    
    services:
        _defaults:
            bind:
                $PARAMETER_NAME: '%PARAMETER_NAME%'
            autowire: true      # Automatically injects dependencies in your services.
            autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
    
        AppBundle\:
            resource: '../../src/AppBundle/*'
            exclude: '../../src/AppBundle/{DependencyInjection,Entity,Tests}'
    
        # Additional Services Below Here
    

    Afterwards, Symfony will automatically pass the parameter value to the bound variable name when it is specified as an argument for the service constructor.

    src/AppBundle/Command/MyCommand.php

    namespace AppBundle\Command;
    
    use Symfony\Component\Console\Command\Command;
    use Symfony\Component\Console\Input\InputInterface;
    use Symfony\Component\Console\Output\OutputInterface;
    
    class MyCommand extends Command
    {
    
        private $PARAMETER_NAME;
    
        public function __construct($PARAMETER_NAME)
        {
            $this->PARAMETER_NAME = $PARAMETER_NAME;
            parent::__construct();
        }
    
        // ...
    
        public function execute(InputInterface $input, OutputInterface $output)
        {
            dump($this->PARAMETER_NAME);
            exit;
        }
    
        public static function getDefaultName()
        {
            return 'app:my_command';
        }
    }
    

    Parameter Injection

    Another approach, to avoid needing to override __construct, is to inject the parameter value using a method, by extending the service definition using calls.

    app/config/services.yml

    parameters:
        PARAMETER_NAME: 'test'
    
    services:
        _defaults:
            autowire: true      # Automatically injects dependencies in your services.
            autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
    
        AppBundle\:
            resource: '../../src/AppBundle/*'
            exclude: '../../src/AppBundle/{DependencyInjection,Entity,Tests}'
    
        AppBundle\Command\MyCommand:
            calls:
                - [setParameterName, ['%PARAMETER_NAME%']]
    
        # Additional Services Below Here
    

    src/AppBundle/Command/MyCommand.php

    namespace AppBundle\Command;
    
    use Symfony\Component\Console\Command\Command;
    use Symfony\Component\Console\Input\InputInterface;
    use Symfony\Component\Console\Output\OutputInterface;
    
    class MyCommand extends Command
    {
    
        private $parameterName;
    
        // ...
    
        public function execute(InputInterface $input, OutputInterface $output)
        {
            dump($this->parameterName);
            exit;
        }
    
        public function setParameterName($value)
        {
            $this->parameterName = $value;
        }
    }
    

    Alternatively, dependency injection can be used to inject the Container or ParameterBag into the command, allowing it to function similarly to a Controller.

    Injecting the entire Container or ParameterBag is highly discouraged. Only inject the parameter(s) and service(s) that are needed instead.

    In either of the below examples, ensure autowire and autoconfigure are enabled.

    app/config/services.yml

    parameters:
        PARAMETER_NAME: 'test'
    
    services:
        _defaults:
            autowire: true      # Automatically injects dependencies in your services.
            autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
    
        AppBundle\:
            resource: '../../src/AppBundle/*'
            exclude: '../../src/AppBundle/{DependencyInjection,Entity,Tests}'
    
        # Additional Services Below Here
    

    ContainerAwareCommand

    (depreciated as of Symfony 4.2 - removed in Syfmony 5.0+)

    Using ContainerAwareCommand works similarly to Parameter Injection, but instead of calling setParameterName(). With autowiring and autoconfig enabled, Symfony will automatically inject the entire container using setContainer() implemented from ContainerAwareInterface.

    src/AppBundle/Command/MyCommand.php

    namespace AppBundle\Command;
    
    use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
    use Symfony\Component\Console\Input\InputInterface;
    use Symfony\Component\Console\Output\OutputInterface;
    
    class MyCommand extends ContainerAwareCommand
    {
    
        // ...
    
        public function execute(InputInterface $input, OutputInterface $output)
        {
            dump($this->getContainer()->getParameter('PARAMETER_NAME'));
            exit;
        }
    }
    

    ParameterBag Injection

    Requires Symfony 4.1+

    To inject the ParameterBag with Dependency Injection when autowire is enabled, add ParameterBagInterface to the service constructor.

    src/AppBundle/Command/MyCommand.php

    namespace AppBundle\Command;
    
    use Symfony\Component\Console\Command\Command;
    use Symfony\Component\Console\Input\InputInterface;
    use Symfony\Component\Console\Output\OutputInterface;
    use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
    
    class MyCommand extends Command
    {
    
        private $parameterBag;
    
        public function __construct(ParameterBagInterface $parameterBag)
        {
             $this->parameterBag = $parameterBag;
             parent::__construct();
        }
    
        // ...
    
        public function execute(InputInterface $input, OutputInterface $output)
        {
            dump($this->parameterBag->get('PARAMETER_NAME'));
            exit;
        }
    
        public static function getDefaultName()
        {
            return 'app:my_command';
        }
    }