|
4 | 4 | How to Define Commands as Services
|
5 | 5 | ==================================
|
6 | 6 |
|
7 |
| -By default, Symfony will take a look in the ``Command`` directory of each |
8 |
| -bundle and automatically register your commands. If a command extends the |
9 |
| -:class:`Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand`, |
10 |
| -Symfony will even inject the container. |
11 |
| -While making life easier, this has some limitations: |
| 7 | +If you're using the :ref:`default services.yml configuration <service-container-services-load-example>`, |
| 8 | +your command classes are already registered as services. Great! This is the recommended |
| 9 | +setup, but it's not required. Symfony also looks in the ``Command`` directory of |
| 10 | +each bundle and automatically registers those classes as commands. |
12 | 11 |
|
13 |
| -* Your command must live in the ``Command`` directory; |
14 |
| -* There's no way to conditionally register your command based on the environment |
15 |
| - or availability of some dependencies; |
16 |
| -* You can't access the container in the ``configure()`` method (because |
17 |
| - ``setContainer()`` hasn't been called yet); |
18 |
| -* You can't use the same class to create many commands (i.e. each with |
19 |
| - different configuration). |
| 12 | +.. note:: |
20 | 13 |
|
21 |
| -To solve these problems, you can register your command as a service and tag it |
22 |
| -with ``console.command``: |
| 14 | + You can also manually register your command as a service by configure the service |
| 15 | + and :doc:`tagging it </service_container/tags>` with ``console.command``. |
23 | 16 |
|
24 |
| -.. configuration-block:: |
| 17 | +In either case, if your class extends :class:`Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand`, |
| 18 | +you can access public services via ``$this->getContainer()->get('SERVICE_ID')``. |
25 | 19 |
|
26 |
| - .. code-block:: yaml |
| 20 | +But if your class is registered as a service, you can instead access services by |
| 21 | +using normal :ref:`dependency injection <services-constructor-injection>`. |
27 | 22 |
|
28 |
| - # app/config/config.yml |
29 |
| - services: |
30 |
| - AppBundle\Command\MyCommand: [console.command] |
| 23 | +For example, suppose you want to log something from within your command:: |
31 | 24 |
|
32 |
| - .. code-block:: xml |
33 |
| -
|
34 |
| - <!-- app/config/config.xml --> |
35 |
| - <?xml version="1.0" encoding="UTF-8" ?> |
36 |
| - <container xmlns="http://symfony.com/schema/dic/services" |
37 |
| - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
38 |
| - xsi:schemaLocation="http://symfony.com/schema/dic/services |
39 |
| - http://symfony.com/schema/dic/services/services-1.0.xsd"> |
40 |
| -
|
41 |
| - <services> |
42 |
| - <service id="AppBundle\Command\MyCommand"> |
43 |
| - <tag name="console.command" /> |
44 |
| - </service> |
45 |
| - </services> |
46 |
| -
|
47 |
| - </container> |
48 |
| -
|
49 |
| - .. code-block:: php |
50 |
| -
|
51 |
| - // app/config/config.php |
52 |
| - use AppBundle\Command\MyCommand; |
53 |
| -
|
54 |
| - $container->register(MyCommand::class) |
55 |
| - ->addTag('console.command') |
56 |
| - ; |
57 |
| -
|
58 |
| -Using Dependencies and Parameters to Set Default Values for Options |
59 |
| -------------------------------------------------------------------- |
60 |
| - |
61 |
| -Imagine you want to provide a default value for the ``name`` option. You could |
62 |
| -pass one of the following as the 5th argument of ``addOption()``: |
63 |
| - |
64 |
| -* a hardcoded string; |
65 |
| -* a container parameter (e.g. something from ``parameters.yml``); |
66 |
| -* a value computed by a service (e.g. a repository). |
67 |
| - |
68 |
| -By extending ``ContainerAwareCommand``, only the first is possible, because you |
69 |
| -can't access the container inside the ``configure()`` method. Instead, inject |
70 |
| -any parameter or service you need into the constructor. For example, suppose you |
71 |
| -store the default value in some ``%command.default_name%`` parameter:: |
72 |
| - |
73 |
| - // src/AppBundle/Command/GreetCommand.php |
74 | 25 | namespace AppBundle\Command;
|
75 | 26 |
|
| 27 | + use Psr\Log\LoggerInterface; |
76 | 28 | use Symfony\Component\Console\Command\Command;
|
77 | 29 | use Symfony\Component\Console\Input\InputInterface;
|
78 |
| - use Symfony\Component\Console\Input\InputOption; |
79 | 30 | use Symfony\Component\Console\Output\OutputInterface;
|
80 | 31 |
|
81 |
| - class GreetCommand extends Command |
| 32 | + class SunshineCommand extends Command |
82 | 33 | {
|
83 |
| - protected $defaultName; |
| 34 | + private $logger; |
84 | 35 |
|
85 |
| - public function __construct($defaultName) |
| 36 | + public function __construct(LoggerInterface $logger) |
86 | 37 | {
|
87 |
| - $this->defaultName = $defaultName; |
| 38 | + $this->logger = $logger; |
88 | 39 |
|
| 40 | + // you *must* call the parent constructor |
89 | 41 | parent::__construct();
|
90 | 42 | }
|
91 | 43 |
|
92 | 44 | protected function configure()
|
93 | 45 | {
|
94 |
| - // try to avoid work here (e.g. database query) |
95 |
| - // this method is *always* called - see warning below |
96 |
| - $defaultName = $this->defaultName; |
97 |
| - |
98 | 46 | $this
|
99 |
| - ->setName('demo:greet') |
100 |
| - ->setDescription('Greet someone') |
101 |
| - ->addOption( |
102 |
| - 'name', |
103 |
| - '-n', |
104 |
| - InputOption::VALUE_REQUIRED, |
105 |
| - 'Who do you want to greet?', |
106 |
| - $defaultName |
107 |
| - ) |
108 |
| - ; |
| 47 | + ->setName('app:sunshine') |
| 48 | + ->setDescription('Hello PhpStorm'); |
109 | 49 | }
|
110 | 50 |
|
111 | 51 | protected function execute(InputInterface $input, OutputInterface $output)
|
112 | 52 | {
|
113 |
| - $name = $input->getOption('name'); |
114 |
| - |
115 |
| - $output->writeln($name); |
| 53 | + $this->logger->info('Waking up the sun'); |
| 54 | + // ... |
116 | 55 | }
|
117 | 56 | }
|
118 | 57 |
|
119 |
| -Now, just update the arguments of your service configuration like normal to |
120 |
| -inject the ``command.default_name`` parameter: |
121 |
| - |
122 |
| -.. configuration-block:: |
123 |
| - |
124 |
| - .. code-block:: yaml |
125 |
| -
|
126 |
| - # app/config/config.yml |
127 |
| - parameters: |
128 |
| - command.default_name: Javier |
129 |
| -
|
130 |
| - services: |
131 |
| - AppBundle\Command\MyCommand: |
132 |
| - arguments: ["%command.default_name%"] |
133 |
| - tags: [console.command] |
134 |
| -
|
135 |
| - .. code-block:: xml |
136 |
| -
|
137 |
| - <!-- app/config/config.xml --> |
138 |
| - <?xml version="1.0" encoding="UTF-8" ?> |
139 |
| - <container xmlns="http://symfony.com/schema/dic/services" |
140 |
| - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
141 |
| - xsi:schemaLocation="http://symfony.com/schema/dic/services |
142 |
| - http://symfony.com/schema/dic/services/services-1.0.xsd"> |
143 |
| -
|
144 |
| - <parameters> |
145 |
| - <parameter key="command.default_name">Javier</parameter> |
146 |
| - </parameters> |
147 |
| -
|
148 |
| - <services> |
149 |
| - <service class="AppBundle\Command\MyCommand"> |
150 |
| - <argument>%command.default_name%</argument> |
151 |
| - <tag name="console.command" /> |
152 |
| - </service> |
153 |
| - </services> |
154 |
| -
|
155 |
| - </container> |
156 |
| -
|
157 |
| - .. code-block:: php |
158 |
| -
|
159 |
| - // app/config/config.php |
160 |
| - use AppBundle\Command\MyCommand; |
161 |
| -
|
162 |
| - $container->setParameter('command.default_name', 'Javier'); |
163 |
| -
|
164 |
| - $container |
165 |
| - ->register(MyCommand::class) |
166 |
| - ->setArguments(array('%command.default_name%')) |
167 |
| - ->addTag('console.command') |
168 |
| - ; |
169 |
| -
|
170 |
| -Great, you now have a dynamic default value! |
| 58 | +If you're using the :ref:`default services.yml configuration <service-container-services-load-example>`, |
| 59 | +the command class will automatically be registered as a service and passed the ``$logger`` |
| 60 | +argument (thanks to autowiring). In other words, *just* by creating this class, everything |
| 61 | +works! You can call the ``app:sunshine`` command and start logging. |
171 | 62 |
|
172 | 63 | .. caution::
|
173 | 64 |
|
174 |
| - Be careful not to actually do any work in ``configure`` (e.g. make database |
175 |
| - queries), as your code will be run, even if you're using the console to |
176 |
| - execute a different command. |
| 65 | + You *do* have access to services in ``configure()``. However, try to avoid doing |
| 66 | + any work (e.g. making database queries), as that code will be run, even if you're |
| 67 | + using the console to execute a different command. |
0 commit comments