diff --git a/cookbook/console/commands_as_services.rst b/cookbook/console/commands_as_services.rst new file mode 100644 index 00000000000..c6001e246bf --- /dev/null +++ b/cookbook/console/commands_as_services.rst @@ -0,0 +1,122 @@ +.. index:: + single: Console; Commands as Services + +How to Define Commands as Services +================================== + +.. versionadded:: 2.4 + Support for registering commands in the service container was introduced in + version 2.4. + +By default, Symfony will take a look in the ``Command`` directory of each +bundle and automatically register your commands. If a command extends the +:class:`Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand`, +Symfony will even inject the container. +While making life easier, this has some limitations: + +* Your command must live in the ``Command`` directory; +* There's no way to conditionally register your service based on the environment + or availability of some dependencies; +* You can't access the container in the ``configure()`` method (because + ``setContainer`` hasn't been called yet); +* You can't use the same class to create many commands (i.e. each with + different configuration). + +To solve these problems, you can register your command as a service and tag it +with ``console.command``: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + services: + acme_hello.command.my_command: + class: Acme\HelloBundle\Command\MyCommand + tags: + - { name: console.command } + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + $container + ->register('acme_hello.command.my_command', 'Acme\HelloBundle\Command\MyCommand') + ->addTag('console.command') + ; + +Using Dependencies and Parameters to Set Default Values for Options +------------------------------------------------------------------- + +Imagine you want to provide a default value for the ``name`` option. You could +pass one of the following as the 5th argument of ``addOption()``: + +* a hardcoded string; +* a container parameter (e.g. something from parameters.yml); +* a value computed by a service (e.g. a repository). + +By extending ``ContainerAwareCommand``, only the first is possible, because you +can't access the container inside the ``configure()`` method. Instead, inject +any parameter or service you need into the constructor. For example, suppose you +have some ``NameRepository`` service that you'll use to get your default value:: + + // src/Acme/DemoBundle/Command/GreetCommand.php + namespace Acme\DemoBundle\Command; + + use Acme\DemoBundle\Entity\NameRepository; + use Symfony\Component\Console\Command\Command; + use Symfony\Component\Console\Input\InputInterface; + use Symfony\Component\Console\Input\InputOption; + use Symfony\Component\Console\Output\OutputInterface; + + class GreetCommand extends Command + { + protected $nameRepository; + + public function __construct(NameRepository $nameRepository) + { + $this->nameRepository = $nameRepository; + } + + protected function configure() + { + $defaultName = $this->nameRepository->findLastOne(); + + $this + ->setName('demo:greet') + ->setDescription('Greet someone') + ->addOption('name', '-n', InputOption::VALUE_REQUIRED, 'Who do you want to greet?', $defaultName) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $name = $input->getOption('name'); + + $output->writeln($name); + } + } + +Now, just update the arguments of your service configuration like normal to +inject the ``NameRepository``. Great, you now have a dynamic default value! + +.. caution:: + + Be careful not to actually do any work in ``configure`` (e.g. make database + queries), as your code will be run, even if you're using the console to + execute a different command. diff --git a/cookbook/console/console_command.rst b/cookbook/console/console_command.rst index 6e3721a24ef..ab509787773 100644 --- a/cookbook/console/console_command.rst +++ b/cookbook/console/console_command.rst @@ -65,55 +65,11 @@ This command will now automatically be available to run: .. _cookbook-console-dic: Register Commands in the Service Container ------------------------------------------- - -.. versionadded:: 2.4 - Support for registering commands in the service container was added in - version 2.4. - -Instead of putting your command in the ``Command`` directory and having Symfony -auto-discover it for you, you can register commands in the service container -using the ``console.command`` tag: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - services: - acme_hello.command.my_command: - class: Acme\HelloBundle\Command\MyCommand - tags: - - { name: console.command } - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - - $container - ->register('acme_hello.command.my_command', 'Acme\HelloBundle\Command\MyCommand') - ->addTag('console.command') - ; - -.. tip:: +------------------------------------------- - Registering your command as a service gives you more control over its - location and the services that are injected into it. But, there are no - functional advantages, so you don't need to register your command as a service. +Just like controllers, commands can be declared as services. See the +:doc:`dedicated cookbook entry ` +for details. Getting Services from the Service Container ------------------------------------------- diff --git a/cookbook/console/index.rst b/cookbook/console/index.rst index 878d1fc862a..6f1f939d772 100644 --- a/cookbook/console/index.rst +++ b/cookbook/console/index.rst @@ -8,3 +8,4 @@ Console usage sending_emails logging + commands_as_services