如何在没有继承的情况下共享Command配置?

时间:2016-05-31 15:21:04

标签: php symfony oop architecture

假设我有一个命令只做一个人:

class PlainTextHelloWorldCommand extends Command
{
    protected function configure()
    {
        $this
            ->setName('text:hello')
            ->addArgument('receiver', InputArgument::REQUIRED, 'Who do you want to greet?');
    }


    /**
     * @param InputInterface  $input
     * @param OutputInterface $output
     * @return int|null|void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $receiver = $input->getArgument('receiver');
        $output->writeln("Hello {$receiver}!");
    }
}

另一个命令现在也需要receiver参数:

class HtmlHelloCommand extends Command
{
    /**
     *
     * @throws InvalidArgumentException
     */
    protected function configure()
    {
        $this
            ->setName('html:hello')
            ->addArgument('receiver', InputArgument::REQUIRED, 'Who do you want to greet?');
    }


    /**
     * @param InputInterface  $input
     * @param OutputInterface $output
     * @return int|null|void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $receiver = $input->getArgument('receiver');
        $output->writeln("<html><h1>Hello {$receiver}!</h1></html>");
    }
}

我现在正在思考如何不重复自己。

我想分享逻辑来添加参数并解析输入,以便它在一个地方。

我知道我可以创建一个“ReceiverAwareCommand”,但如果我获得更多参数会发生什么?

我不希望SendEmailCommand扩展MessageAwareGreeterCommand扩展receiverAwareCommand ...`。这条道路似乎导致地狱,因此我喜欢避免继承。

此外,我的示例是简化的,因为两个示例命令基本相同。情况不一定如此。此外,我有大约10个参数,而每个命令最多可能需要4个参数。

我只想在没有自己的情况下在需要时设置这些参数。

我正在思考Decorator模式的方向,但在这种情况下我对如何设置它们感到困惑,所以感觉错误。

因此我想知道:如何实现这一目标?

2 个答案:

答案 0 :(得分:1)

作为symfony 4。

我创建了一个名为Configure的类,并在其中添加了3个静态函数。

namespace MyApp\Commands;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface, InputArgument, InputOption};

class Configure {
    static function arguments(Command $command, array $arguments) {
        foreach($arguments as $argument) {
            // Include here all of your re-useable arguments.
            switch($argument) {
                case 'arg1':
                    $command->addArgument('arg1', InputArgument::REQUIRED, 'Description.');
                break;
                case 'arg2':
                    $command->addArgument('arg2', InputArgument::REQUIRED, 'Description');
                break;
                // ...
                // Add more here.
            }
        }
    }

    static function options(Command $command, array $options) {
        foreach($options as $option) {
            // Include here all of your re-usable options.
            switch($option) {
                case 'opt1':
                    $command->addOption('opt1', NULL, InputOption::VALUE_REQUIRED, '.');
                break;
                case 'opt2':
                    $command->addOption('opt2', NULL, InputOption::VALUE_REQUIRED, '.');
                break;
                // ...
                // Add more here.
            }
        }
    }

    static function validate(InputInterface $input) {
        // Include here all of your default validations.
        if ($input->getOption('opt2') && !$input->getOption('opt1'))
            throw new \Exception('You must send --opt1 when sending --opt2');
        // ...
        // Add more here.
    }
}

在命令中,我选择要重新使用的参数/选项。

namespace MyApp\Commands;

use Symfony\Component\Console\Input\{InputInterface, InputArgument, InputOption};
use Symfony\Component\Console\{Command\Command, Output\OutputInterface};

class MyCommand extends Command {
    protected static $defaultName = 'my:command';

    protected function configure() {
        // Here you set the arguments/options that are unique to this
        // command.
        $this
            ->setDescription('Description.')
            ->addOption('only_here', 'o', NULL, 'Description.');

        // You can pick and choose which of your default arguments/option
        // should be included in this command.
        Configure::arguments($this, ['arg1', 'arg2']);
        Configure::options($this, ['opt2']);
    }

    protected function execute(InputInterface $input, OutputInterface $output) {
        Configure::validate($input);
        // ...
        // My stuff here.
    }
}

答案 1 :(得分:0)

(我知道你不想继承,但我找不到更好的主意)

您可以通过添加方法BaseCommand定义add…Argument来定义所有参数:

class BaseCommand extends Command
{
    static $format = '%s';

    protected function configure()
    {
        parent::configure();

        // Define a fake command (IIRC Symfony throws an error without this).
        $this
            ->setName('command:base')
            ->setDescription('Base command')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        parent::initialize($input, $output); //initialize parent class method

        $receiver = $input->getArgument('receiver');
        $output->writeln(sprintf($this::$format, $receiver));
    }

    /***** Define arguments *****/
    protected function addReceiverArgument()
    {
        $this
            ->addArgument('receiver', InputArgument::REQUIRED, 'Who do you want to greet?')
        ;

        return $this;
    }

    protected function addOtherArgument()
    {
        $this
            ->addArgument('other', InputArgument::REQUIRED, 'Other argument')
        ;

        return $this;
    }
}

然后您可以在子类中重用这些方法:

class PlainTextHelloWorldCommand extends BaseCommand
{
    protected function configure()
    {
        parent::configure();

        $this
            ->setName('text:hello');

        $this
            ->addReceiverArgument()
        ;
    }
}

如果另一个命令需要2个参数,那很简单:

class HtmlHelloCommand extends BaseCommand
{
    // Change the output formatting.
    static $format = '<html><h1>Hello %s!</h1></html>';

    protected function configure()
    {
        parent::configure();

        $this
            ->setName('html:hello')
        ;

        $this
            ->addReceiverArgument()
            ->addOtherArgument()
        ;
    }
}

然后你可以调用命令:

$ php app/console text:hello aaa
Hello aaa!
$ php app/console html:hello aaa


  [Symfony\Component\Console\Exception\RuntimeException]  
  Not enough arguments (missing: "other").                

$ php app/console html:hello aaa bbb
<html><h1>Hello aaa!</h1></html>