Symfony CommandをDIを使って

Pocket

Symfony Console CommandsをSymfony DependencyInjection Componentを使用してServiceとして実行する方法の備忘録です。
DIを使用することでCommandのテストでモックを使用できます。

Symfony 4.4を使用しています。
2021年11月12日時点の最新である5.3ではないことに注意してください。

ディレクトリ構成

--path/to/project
  |
  |-- bin
      |
      |-- console
  |
  |-- config
      |
      |-- service.yml
  |
  |-- src
      |
      |-- Command
          |
          |-- MessageCommand.php
     |
     |-- Service
         |
         |-- MessageGenerator.php
  |
  |-- composer.json

必要なSymfony Componentをインストール

必要なコンポーネントをインストールします。

$ composer req symfony/console:^4.4
$ composer req symfony/dependency-injection:^4.4
$ composer req symfony/config:^4.4
$ composer req symfony/yaml:^4.4

※ 一般にはcomposer initcomposer.jsonを作成しますが、本記事は簡単化のため上記の方法で進めます。

composer.jsonにautoloadを追加

今回作成するアプリの名前空間はFoo\Console\として進めます。

"autoload": {
    "psr-4": {
        "Foo\\Console\\": "src/"
    }
},

autoload.phpを更新します。

$ composer dump-autoload

MessageCommand.phpとMessageGenerator.phpを作成

src/Command/MessageCommand.php

<?php

namespace Foo\Console\Command;

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

class MessageCommand extends Command
{

   const NAME = 'command:message';
   private $messageGenerator;

    public function __construct($messageGenerator)
    {
        parent::__construct(self::NAME);
        $this->messageGenerator = $messageGenerator;
    }

    protected function configure()
    {
        $this->addUsage('挨拶を表示');

        $this->addArgument(
            'message',
            InputArgument::REQUIRED,
            'Message for display'
        );

        $this->addOption(
            'name',
            null,
            InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
            'first name, last name',
            ['foo']
        );

    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $message = $input->getArgument('message');
        $name = $input->getOption('name');
        $output->writeln($this->messageGenerator->getMessage($message, $name));

        return 0;
    }
}

src/Service/MessageGenerator.php

<?php
namespace Foo\Console\Service;

class MessageGenerator {

    public function getMessage(string $message, array $name): string
    {
        return sprintf('%s! %sさん', $message, implode(' ', $name));
    }

}

config/service.ymlを作成

config/service.ymlでServiceを定義します。

services:
  message.generator:
    class: Foo\Console\Service\MessageGenerator

  # command
  command.message:
    class: Foo\Console\Command\MessageCommand
    arguments:
      $messageGenerator: '@message.generator'

  # app
  app:
    class: Symfony\Component\Console\Application
    calls:
      - add: ['@command.message']
    public: true

コマンド実行ファイルbin/consoleを作成

bin/consoleファイルを作成して実行権を付与します。

$ mkdir bin
$ touch bin/console
$ chmod u+x bin/console

bin/consoleでコンテナビルダーを使ってSymfony Console Commandsをサービスとして実行します。

#!/usr/bin/env php
<?php

require __DIR__ . '/../vendor/autoload.php';

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

$containerBuilder = new ContainerBuilder();
$loader = new YamlFileLoader($containerBuilder, new FileLocator(__DIR__));
$loader->load(__DIR__ . '/../config/services.yaml');

$containerBuilder->compile(true);

$application = $containerBuilder->get('app');
$application->run();

コマンドの実行

$ bin/console command:message "Hello World" --name=Hiroshi --name=Sawai
//Hello! Hiroshi Sawaiさん 

コメント

No comments yet.

コメントの投稿

改行と段落タグは自動で挿入されます。
メールアドレスは表示されません。