|
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 configuring 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('Good morning!'); |
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