346
343

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Laravelでコマンドラインアプリケーションを作成する

Last updated at Posted at 2017-01-30

最近仕事でLaravelを使い始めました。
で、バッチ用にコマンドライン処理を作成する機会がありました。
公式ドキュメント見れば大体分かるのですが、忘れっぽいので備忘としてやったことをまとめておきます。

環境

Laravel Framework 5.4.0
PHP 7.0.13

Commandクラスの雛形生成

CLIで実行するクラスは、LaravelのCommandクラスを継承して作成します。
これをartisanコマンドとして追加し、それを実行させることになります。

このクラスの雛形は、artisanを使用して生成できます。

雛型生成
$ php artisan make:command SampleCommand

上記の場合はSampleCommandというクラスを作成しています。
これを叩くと、app/Console/Commands配下に、以下のようにクラスが生成されます。

SampleCommand.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class SampleCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:name';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        //
    }
}

コメント見れば大体わかりますが、

  • $signature:artisanコマンドとして登録するときの識別子
  • $description:コマンドの説明
  • コンストラクタ
  • handle:実際の処理を記述する

という感じです。

ちなみにこんな感じで叩けば

雛型生成
$ php artisan make:command sample/SampleCommand

app/Console/Commands/sample配下にクラスが生成され、名前空間もそれに応じた形に設定されます。

sample/SampleCommand.php

<?php

namespace App\Console\Commands\sample;

use Illuminate\Console\Command;

class SampleCommand extends Command

...以下略

Commandクラスをartisanコマンドとして登録する

先程作成したCommandクラスをartisanコマンドとして登録します。
app/Console/Kernel.phpの$commands配列に、作成したクラスを追加します。

Kernel.php
<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        Commands\SampleCommand::class
    ];
    
...以下略

追加できたら、artisan listでコマンド登録がされているかを確認。

$ php artisan list

Laravel Framework 5.4.0

...中略

 command
  command:name         Command description

登録したクラスのsignature、descriptionに基づいて登録したコマンドが表示されます。
これで、

$ php artisan command:name

と叩くことで、先程のCommandクラスが実行できる状態になっています。

次にCommandクラスの必要な箇所を変更してみます。

signatureの変更

artisanコマンドとして登録するときの識別子になります。

生成時
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:name';

名前空間

コロンで区切ることで、名前空間としてコマンドのグルーピングができます。
上記の例であれば「command」という名前空間になるので、artisan listでは

 command
  command:name         Command description

「command」でグルーピングして表示されます。
もちろん指定しなくても問題なく、

名前空間指定無し
    protected $signature = 'name';

その場合は、Available Commandsとして表示されます。

Available commands:
...中略
  name                 Command description

実行時引数の指定

signatureには、実行時引数の定義をすることもできます。

必須引数の指定

必須引数の指定
    protected $signature = 'sample:sample {name}';

第1引数必須、引数に指定された値は、プログラム中では「name」というキーで取得できるようになります。
引数は複数指定することが可能です。

必須引数の指定
    protected $signature = 'sample:sample {name} {age}';

引数指定無しで実行すると、こんな感じでエラーとなります。

必須引数指定なし
$ php artisan sample:sample


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

引数の数が合わない場合もエラーとなります。

引数の数が違う
$ php artisan sample:sample laravel hoge fuga

  [Symfony\Component\Console\Exception\RuntimeException]
  Too many arguments, expected arguments "command" "name" "age".

引数の取得はこんな感じ。

実行時引数取得
    public function handle()
    {
        $name = $this->argument("name");
        $this->info("Hello $name");
    }

引数指定して実行するとこんな感じになります。

必須引数指定あり
$ php artisan sample:sample laravel

Hello laravel

任意引数の指定

任意引数の指定
    protected $signature = 'sample:sample {name?}';

第1引数は任意になります。
値の取得方法は先程と同じです。
任意指定かつ、指定されなかった場合のデフォルト値を指定することもできます。

デフォルト値の指定
    protected $signature = 'sample:sample {name=laravel}';

引数に説明をつける

Help用に説明をつけることができます。
引数定義に、コロン区切りで記述します。

引数説明の指定
    protected $signature = 'sample:sample {name=laravel : 名前を指定} {age? : 年齢を指定}';

指定すると、Help表示時に説明も表示されるようになります。

Help表示
$ php artisan sample:sample -h
Usage:
  command:sample [<name>] [<age>]

Arguments:
  name                  名前を指定 [default: "laravel"]
  age                   年齢を指定

Options:

...以下略

オプションの指定

signatureには、実行時オプションの定義をすることもできます。

基本的な指定

オプションの指定
    protected $signature = 'sample:sample {--dry-run}';
オプション指定して実行
$ php artisan sample:sample --dry-run

オプションの取得はこんな感じ。

オプション取得
    public function handle()
    {
        $dry_run = $this->option("dry-run");
    }

該当オプションの指定があった場合はtrue、なければfalseが取得されます。

値を指定するオプションの指定

オプションの指定
    protected $signature = 'sample:sample {--greeting=}';
オプション指定して実行
$ php artisan sample:sample --greeting=Hello

値の取得方法は先程と同じです。
オプションを省略しても、実行時エラーにはなりません。

値を指定してデフォルト値も設定するオプションの指定

オプションの指定
    protected $signature = 'sample:sample {--greeting=Hello}';

この指定の場合、実行時にオプションを設定しなくても値が取得できます。

「Good morning」が取得できる
$ php artisan sample:sample '--greeting=Good morning'
「Hello」が取得できる
$ php artisan sample:sample --greeting=
$ php artisan sample:sample --greeting
$ php artisan sample:sample

オプションに説明をつける

引数と同じく、Help用に説明をつけることができます。
オプション定義に、コロン区切りで記述します。

オプション説明の指定
    protected $signature = 'sample:sample {--greeting=Hello : 挨拶を指定}';

指定すると、Help表示時に説明も表示されるようになります。

Help表示
$ php artisan sample:sample -h
Usage:
  command:sample [options]

Options:
      --greeting[=GREETING]  挨拶を指定 [default: "Hello"]
...以下略

オプションのショートカットを設定する

オプションの完全名の前に、パイプ区切りでショートカット名を指定できます。

オプションのショートカットの指定
    protected $signature = 'sample:sample {--g|greeting=Hello : 挨拶を指定}';
オプションのショートカット指定で実行
$ php artisan sample:sample '-gGood evening'

descriptionの変更

artisanコマンドの説明になります。

descriptionを変更
    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'サンプルコマンドアプリケーションです';

artisan list表示時、Help表示時に説明も表示されるようになります。

description表示
$ php artisan list
Laravel Framework 5.4.0

...中略

 command
  command:sample       サンプルコマンドアプリケーションです
  
...中略
  
$ php artisan command:sample -h
Usage:
  command:sample [options]

...中略
  
Help:
  サンプルコマンドアプリケーションです

コンストラクタ

インジェクションしたい場合は、コンストラクタで行っておけばいいですね。

    private $messageCreator;

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct(MessageCreator $messageCreator)
    {
        parent::__construct();
        $this->messageCreator = $messageCreator;
    }

コンストラクタの段階では、実行時引数やオプションの値は取得できません。

処理の記述

具体的な処理はhandleに記述していきます。
実際に使ってみた機能についてまとめてみます。

対話的処理

コンソールで色々できます。

SampleInteractiveCommand.php
<?php
class SampleInteractiveCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'sample:interactive';


...中略
    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $this->info('start');

        $name = $this->ask('名前を入力してください');
        $age = $this->ask('年齢を入力してください');

        $this->info("名前 : $name");
        $this->info("年齢 : $age");
        if ($this->confirm('この内容で実行してよろしいですか?')) {
            $this->info("$name $age years old");
        } else {
            $this->info('cancel');
        }

        $this->info('end');
    }
}
実行イメージ
$ php artisan sample:interactive
start

 名前を入力してください:
 > laravel

 年齢を入力してください:
 > 5

名前 : laravel
年齢 : 5

 この内容で実行してよろしいですか? (yes/no) [no]:
 > yes

laravel 5 years old
end
$ php artisan sample:interactive
start

 名前を入力してください:
 > Taro

 年齢を入力してください:
 > 10

名前 : Taro
年齢 : 10

 この内容で実行してよろしいですか? (yes/no) [no]:
 > no

cancel
end

コンソールへのメッセージ出力

SampleConsoleOutputCommand.php
class SampleConsoleOutputCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'sample:console-output';

... 中略

    public function handle()
    {
        $this->info('info');
        $this->line('line');
        $this->comment('comment');
        $this->question('question');
        $this->error('error');

        $this->table(
            ['名前', '年齢'],
            [
                ['Taro', 10],
                ['Laravel', 5],
            ]
        );
    }
}

これ実行すると

コンソール出力結果

色つけたり、テーブル形式で整形した結果を画面に表示してくれます。

実行終了時の戻り値

シェルで実行して、Commandの戻り値をみて云々したい、みたいな場合は、handleから整数値を戻すことで対応できます。

SampleExitCodeCommand.php
class SampleExitCodeCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'sample:exit-code {--force-error : 強制的にエラー扱いにする}';

... 中略

    public function handle()
    {
        $this->info('start');

        if ($this->option('force-error')) {
            $this->error('error!');
            return config('command.exit_code.ERROR');
        }

        $this->info('end');
        return config('command.exit_code.SUCCESS');
    }
}
config/command.php
<?php

return [
    'exit_code' => [
        'SUCCESS' => 0,
        'ERROR' => 1
    ],
];
実行イメージ
$ php artisan sample:exit-code
start
end
$ echo $?
0
$ php artisan sample:exit-code --force-error
start
error!
$ echo $?
1

実行時引数のvalidation

どうやるのが望ましいものなのかよく分からなかったので、Validatorファサード使いました。

class SampleValidateCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'sample:validate {name : 名前を指定} {age? : 年齢を指定}';

... 中略

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $this->info('start');

        try {

            $this->validate();

        } catch (ValidationException $e) {
            $this->error('validation error!');
            foreach ($e->validator->getMessageBag()->all() as $error) {
                $this->error($error);
            }
            return config('command.exit_code.ERROR');
        }

        $this->info('end');
        return config('command.exit_code.SUCCESS');
    }

    /**
     * Validation
     */
    private function validate()
    {
        \Validator::validate(
            array_filter($this->arguments()),
            [
                'name' => 'max:10',
                'age' => 'numeric|min:20',
            ],
            [
                'name.max' => '名前は10文字以内で入力してください',
                'age.numeric' => '年齢は数値を入力してください',
                'age.min' => '年齢は20才以上で入力してください'
            ]
        );
    }
}
実行イメージ
$ php artisan sample:validate laravel 20
start
end
$ php artisan sample:validate laravel123! 19
start
validation error!
名前は10文字以内で入力してください
年齢は20才以上で入力してください

こういうのもありました。
使ってみてもいいかもしれないですね。

プログレスバー

SampleProgressBarCommand.php
class SampleProgressBarCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'sample:progress-bar';

... 中略

    public function handle()
    {
        $count = 5;

        // 標準的なプログレスバー
        {
            $progressBar = $this->output->createProgressBar($count);
            $i = 0;
            while ($i++ < $count) {
                $this->processing();
                $progressBar->advance();
            }
            $progressBar->finish();
        }

        echo PHP_EOL;

        // プログレスバーのデザイン変更
        {
            $progressBar = $this->output->createProgressBar($count);
            $progressBar->setBarCharacter('>');
            $progressBar->setEmptyBarCharacter('-');
            $progressBar->setProgressCharacter('!');
            $i = 0;
            while ($i++ < $count) {
                $this->processing();
                $progressBar->advance();
            }
            $progressBar->finish();
        }

        echo PHP_EOL;

        // プレースホルダーでメッセージを定義して表示
        {
            $i = 0;
            $progressBar = $this->output->createProgressBar($count);
            $progressBar->setFormatDefinition('custom', ' %current%/%max% -- %message% (%currentNo%)');
            $progressBar->setFormat('custom');
            while ($i++ < $count) {
                $this->processing();
                $progressBar->setMessage('processing...');
                $progressBar->setMessage('No.' . $i, 'currentNo');
                $progressBar->advance();
            }
            $progressBar->finish();
        }

        echo PHP_EOL;
    }

    /**
     * 擬似的な処理 サンプルなのでバーの表示を見やすくするために遅延をさせる
     */
    private function processing()
    {
        sleep(1);
    }
}

これ実行すると、こんな感じで進捗状況が表示されます。

laravel_command_progress_bar-compressor.gif

上述してきたように、既にLaravelで用意されている機能を組み合わせるだけで、簡単にコマンドラインアプリケーションが作成できます。

サンプル

作成したサンプルソース群を以下に置いてあります。

参考ページ

以下を参考にさせていただきました。
ありがとうございました。

346
343
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
346
343

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?