如何将命令定义为服务

3.4 版本
维护中的版本

默认时,Symfony将查找每一个bundle中的 Command 目录,并自动注册你的命令。如果一个命令继承的是 ContainerAwareCommand,Symfony甚至将容器注入进去,尽管开发得更轻松了,但这也有一些局限性:

  • 你的命令必须存放在 Command 目录;
  • 不可能基于环境,或基于某些依赖的可用性,来有条件地注册你的命令;
  • configure() 方法中你无法访问容器 (因为 setContainer 还没被调用呢);
  • 你不可以使用同一个类创建多个命令 (如,每个命令使用不同的配置时)。

要解决这些问题,你可以把命令注册为服务,并打上 console.command 标签:

1
2
3
4
5
6
# app/config/config.yml
services:
    app.command.my_command:
        class: AppBundle\Command\MyCommand
        tags:
            - { name: console.command }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
    http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <services>
        <service id="app.command.my_command"
            class="AppBundle\Command\MyCommand">
            <tag name="console.command" />
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
// app/config/config.php
$container
    ->register(
        'app.command.my_command',
        'AppBundle\Command\MyCommand'
    )
    ->addTag('console.command')
;

使用依赖和参数来为选项设置默认值 

假设你要为 name 选项提供一个默认值。你可以把下列之一作为 addOption() 方法的第五个参数:

  • 一个写死的字符串;
  • 一个常量参数 (如,来自 parameters.yml 的内容);
  • 由某个服务计算出来的值 (比如一个repository)/。

通过继承 ContainerAwareCommand,只有第一种是可行的,因为你无法在 configure() 方法内部访问容器。取而代之的是,注入你需要的任何参数或服务到构造器中。例如,假设你把默认值存到了 %command.default_name% 这种参数中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// src/AppBundle/Command/GreetCommand.php
namespace AppBundle\Command;
 
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 $defaultName;
 
    public function __construct($defaultName)
    {
        $this->defaultName = $defaultName;
 
        parent::__construct();
    }
 
    protected function configure()
    {
        // try to avoid work here (e.g. database query)
        // this method is *always* called - see warning below
        // 此处应避免进行操作(如,数据库查询)
        // 这个方法将 *始终* 会被调用 - 见下文中的警告部分
        $defaultName = $this->defaultName;
 
        $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);
    }
}

现在,只需像平常一样去更新你的服务配置中的arguments部分,即可注入 command.default_name 参数:

1
2
3
4
5
6
7
8
9
10
# app/config/config.yml
parameters:
    command.default_name: Javier

services:
    app.command.my_command:
        class: AppBundle\Command\MyCommand
        arguments: ["%command.default_name%"]
        tags:
            - { name: console.command }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
    http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <parameters>
        <parameter key="command.default_name">Javier</parameter>
    </parameters>
 
    <services>
        <service id="app.command.my_command"
            class="AppBundle\Command\MyCommand">
            <argument>%command.default_name%</argument>
            <tag name="console.command" />
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
// app/config/config.php
$container->setParameter('command.default_name', 'Javier');
 
$container
    ->register(
        'app.command.my_command',
        'AppBundle\Command\MyCommand',
    )
    ->setArguments(array('%command.default_name%'))
    ->addTag('console.command')
;

了不起呀,你现在有了一个动态提供的默认值!

注意不要在 configure 中做任何真正的事(比如进行数据库查询),因为你的代码将会运行,哪怕在你使用控制台来执行一个别的命令时。

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)