Laravel8.* 以降crontabを利用せずともスケジューラを利用できるようになったらしい。
興味本位でArtisanコマンドを作成してスケジューラーに登録してみるということを行ってみた。
Artisanコマンドの作成
まずは、オリジナルのArtisanコマンドを作成する必要がある。
今回はコマンドを押すと世界の時間を表示するというコマンドを作成する。
$ php artisan make:command Time
コマンドを実行すると、app/Console/Commands
というディレクトリに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()
実際にコマンドの処理を記述するメソッドである。
先ほどのようにマイグレーションコマンドを例に挙げると以下のようになっていた。
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
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変数に作成したコマンドの情報を登録する。
protected $commands = [
//
];
作成した world:time
のコマンドを登録する。
protected $commands = [
\App\Console\Commands\time::class,
];
scheduleメソッドへの登録
次にscheduleメソッドへの登録である。
scheduleメソッドのなかではcommandメソッドを用いてArtisanコマンドを登録できる。
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を利用せずともスケジューラを利用することができた。