Laravelで世界の現在時刻を示すArtisanコマンドを作成してスケジューリングした

Laravel8.* 以降crontabを利用せずともスケジューラを利用できるようになったらしい。
興味本位でArtisanコマンドを作成してスケジューラーに登録してみるということを行ってみた。
##Artisanコマンドの作成
まずは、オリジナルのArtisanコマンドを作成する必要がある。
今回はコマンドを押すと世界の時間を表示するというコマンドを作成する。

```
$ php artisan make:command Time
```
コマンドを実行すると、`app/Console/Commands` というディレクトリにTime.phpというファイルが作成される。

デフォルトの状態では このような状態になっているだろう。

```php:Time.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class Time 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 int
     */
    public function handle()
    {
        return 0;
    }
}
```
Timeというクラスの中にいくつかのメソッドが登録されている。
この中で利用するメソッドやプロパティを紹介する。デフォルトではこのような表記である。

###$signature
コマンドの名前を登録するプロパティである。

```
protected $signature = 'command:name';
```

`'command:name'` の部分に登録するコマンド名を登録する。
`php artisan 〇〇` の〇〇の部分を記入する。
`php artisan migrate:fresh` では `migrate:fresh` のようにArtisanコマンドの後半部分を登録する。

###$description
コマンドの説明部分を記入するプロパティである。
`php artisan list` というコマンドを実行すると登録されているコマンドが説明とともに出力される。
マイグレーションだと以下のような説明が記述されている。

```
migrate
  migrate:fresh        Drop all tables and re-run all migrations
  migrate:install      Create the migration repository
  migrate:refresh      Reset and re-run all migrations
  migrate:reset        Rollback all database migrations
  migrate:rollback     Rollback the last database migration
  migrate:status       Show the status of each migration
```
前半がコマンドの名前、後半がコマンドの説明となっている。
この後半部分を `$description` では記述する。

###handle()
実際にコマンドの処理を記述するメソッドである。
先ほどのようにマイグレーションコマンドを例に挙げると以下のようになっていた。

```php:MigrateCommnad.php
public function handle()
    {
        if (! $this->confirmToProceed()) {
            return 1;
        }

        $this->migrator->usingConnection($this->option('database'), function () {
            $this->prepareDatabase();

            // Next, we will check to see if a path option has been defined. If it has
            // we will use the path relative to the root of this installation folder
            // so that migrations may be run for any path within the applications.
            $this->migrator->setOutput($this->output)
                    ->run($this->getMigrationPaths(), [
                        'pretend' => $this->option('pretend'),
                        'step' => $this->option('step'),
                    ]);

            // Finally, if the "seed" option has been given, we will re-run the database
            // seed task to re-populate the database, which is convenient when adding
            // a migration and a seed at the same time, as it is only this command.
            if ($this->option('seed') && ! $this->option('pretend')) {
                $this->call('db:seed', ['--force' => true]);
            }
        });

        return 0;
    }
```

こんなに長い記述を今回は実装する必要はない。
自分が必要な処理だけをhandle()ないに記述できればいいのだ。

###Time.phpへの記述
では、先ほどの事まで踏まえてTime.phpへの記述を修正する。
今回は以下のように修正した。

```php:Time.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

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

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'This command notifys you world time';

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

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        
        $time_zones = array("Asia/Tokyo", "Asia/Manila","Europe/Paris", "America/New_York", "Australia/Perth");
        $end =end($time_zones);
        foreach ($time_zones as $time_zone) {
            if ( $time_zone === $end) {
                date_default_timezone_set($time_zone);
                echo  date('Y年m月d日 H時i分s秒'), $time_zone."\n"."\n";
            } else {
                date_default_timezone_set($time_zone);
                echo  date('Y年m月d日 H時i分s秒'), $time_zone."\n";
            }
            
        }
        
    }
}

```

コマンドの名前は `world:time` として、コマンドの説明を `This command notifys you world time` とした。

あとはhandle()で登録しているように世界のいくつかの都市のタイムゾーンを取得してその現在時刻を表記するように繰り返し文を用いている。

実際にコマンドを該当のLaravelプロジェクト上で実行してみると、世界の時刻がCLI上で表示される。

```
 $ php artisan world:time
2021年04月21日 10時02分47秒Asia/Tokyo
2021年04月21日 09時02分47秒Asia/Manila
2021年04月21日 03時02分47秒Europe/Paris
2021年04月20日 21時02分47秒America/New_York
2021年04月21日 09時02分47秒Australia/Perth
```


TIme.phpへの記述は完了したので、これをスケジューラに登録していく。
スケジューラへの登録は$scheduleメソッドを用いて、Kernel.phpに登録する。
##Kernel.phpへの登録
作成したコマンドはapp/ConsoleディレクトリのKernel.phpに登録する。
先ほどのCommandsディレクトリと同じ階層である。
デフォルトでは何も記述されていないが、$commands変数に作成したコマンドの情報を登録する。

```php:Kernel.php
protected $commands = [
    //
];
```
作成した `world:time` のコマンドを登録する。

```php:Kernel.php
protected $commands = [
        \App\Console\Commands\time::class,
    ];
```
###scheduleメソッドへの登録
次にscheduleメソッドへの登録である。
scheduleメソッドのなかではcommandメソッドを用いてArtisanコマンドを登録できる。

```php:Kernel.php
protected function schedule(Schedule $schedule)
    {
        $path = storage_path('logs/test.log');
        $schedule->command('world:time')->everyMinutes()->appendOutputTo($path);
        
    }
```

ここでは先ほどの$signatureようにコマンド名をcommandメソッドの引数に加える。
この出力結果を `Storage/logs/test.log` のなかに記述するようにしているため、あらかじめtest.logファイルを作成する必要がある。

また,everyMinutes()で毎分その$scheduleメソッドを起動するような記述になっている。


##いざスケジューラーの起動

このコマンドでスケジューラーに登録された$scheduleが処理される。

```
$ php artisan schedule:work
```
スケジューラーが起動されると、このような表記がCLI上でされて実際にtest.logを確認すると正しく動作していることがわかるだろう。

```
 $ php artisan schedule:work
Schedule worker started successfully.

[2021-04-21T02:03:00+00:00] Execution #1 output:
Running scheduled command: '/usr/bin/php' 'artisan' world:time >> '/Users/ユーザー名/Desktop/application-laravel-app/storage/logs/test.log' 2>&1

[2021-04-21T02:04:00+00:00] Execution #2 output:
Running scheduled command: '/usr/bin/php' 'artisan' world:time >> '/Users/ユーザー名/Desktop/application-laravel-app/storage/logs/test.log' 2>&1
```

このようにLaravel8.* 以降crontabを利用せずともスケジューラを利用することができた。