今のプロジェクトでは独自に作成したコンソールコマンドが40~50個ほどあります。バッチ処理であったり、クロンで定期実行している処理であったりと様々です。システムが大きくなるとそれだけ必要な処理が増えるので致し方ありません。しかし時折、このコマンドは何をやっているんだっけ?と見返した時にすぐ思い出せるようにコード量を少なく、シンプルに保っておきたいものです。

そう思いながら今まで作成したコマンドらを眺めていると、毎回決り文句のように記述している処理がいくつかある事に気づきました。気づいてしまったが故にDRYアレルギーが発症してしまい、どこか適切な場所に共通処理として切り出せないだろうか?と調べてみると、CommandにはEventが用意されていました。

CommandのEvent

公式ドキュメントの”Artisan Console”ページの最後に少しだけCommandのEventに関する説明があります。それによると、Commandが実行される際は以下の3つのイベントが発行されるそうです。

  • ArtisanStarting: artisan実行開始時
  • CommandStarting: Commandが実行される直前、つまりbefore処理
  • CommandFinished: Commandが実行された後、つまりafter処理

デフォルトでは上記のEventに対するListenerは用意されていないので、新たに作成し紐付ける必要があります。

before/after処理を追加

ここでは例として、CommandStartingイベントとCommandFinishedイベントを使いコマンド実行前と後に処理を追加してみましょう。まずはbefore処理から。

CommandStartingListener作成

before処理を実装する為のListenerクラスを作成します。以下のコマンドを実行。

php artisan make:listener CommandStartingListener

CommandStartingListenerクラスのhandle()にbefore処理を追加します。以下では開始ログを出力してみました。

<?php

namespace App\Listeners;

use Illuminate\Support\Facades\Log;

class CommandStartingListener
{
    /**
     * Handle the event.
     *
     * @param  object  $event
     * @return void
     */
    public function handle($event)
    {
        // 開始ログを出力
        Log::info($event->command."を開始しました。");
    }
}

EventとListenerを紐付け

作成したCommandStartingListenerはそのままでは発火されません。EventServiceProviderにてCommandStartingイベントに紐付けましょう。

<?php

namespace App\Providers;

use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        Registered::class => [
            SendEmailVerificationNotification::class,
        ],
        // CommandStartingイベントにリスナを紐付け
        \Illuminate\Console\Events\CommandStarting::class => [
            \App\Listeners\CommandStartingListener::class,
        ],
    ];

    /**
     * Register any events for your application.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}

同様の手順でCommandFinishedListenerを作成し、CommandFinishedイベントに紐付け、終了ログを出力してみましょう。EventServiceProviderは最終的に以下のようになります。

...
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        Registered::class => [
            SendEmailVerificationNotification::class,
        ],
        // CommandStartingイベントにリスナを紐付け
        \Illuminate\Console\Events\CommandStarting::class => [
            \App\Listeners\CommandStartingListener::class,
        ],
        // CommandFinishedイベントにリスなを紐付け
        \Illuminate\Console\Events\CommandFinished::class => [
            \App\Listeners\CommandFinishedListener::class,
        ],
    ];
...

最後に

適当なコマンドを実行しログ出力されているか試してみましょう。以下ではルートのリストを出力するコマンドを実行しています。

php artisan route:list

laravel.logに開始と終了のログが出力されましたね。

[2022-05-08 08:21:18] local.INFO: route:listを開始しました。  
[2022-05-08 08:21:18] local.INFO: route:listを終了しました。  

By hikaru