Laravel上のテーブルに保存されたデータから日々の売上の集計を行いたい、Laravelの関連ファイルのバックアップを行いたいなど、定期的に決められた処理を自動的に実行したい場合にタスクスケジュールを利用すると簡単に実装することができます。

通常Unix/Linux系のOSを使用している場合に定期的な処理を行いたい場合はCronを利用し、実行したいタスク毎にエントリー(ジョブの登録)を追加していきます。

LaravelもCronを利用していますがLaravelのタスクスケジュールを設定する場合は、Cronへのエントリーを1つ加えるだけでタスク毎にCronへのエントリーを追加する必要がありません。各タスク処理はLaravel側で行うため、タスクの追加・削除を効率よく行うことができます。

Cronの理解

Laravelのタスクスケジュールの実行にはCronが利用されているので、まずCronとはなにかとCronの設定方法を確認しておきます。

Cronとは

Linux/Unix系OSでは、ある特定の処理を定期的に実行したい場合にCronを利用します(MACでも利用可能です)。Cronはバックグランドで常時稼働しているのでcrontab(Cron Table)にエントリーを記述することでエントリーの内容に沿って定期的に処理を行うことができます。

crontab(Cron Table)の書式

crontabのエントリーの記述方法にはルールがあり下記の書式にしたがって記述する必要があります。左の*(アスタリスク)は分、2つ目は時間、3つ目は日時、4つ目は月の設定、5つ目は曜日の設定を行います。


分 時 日 月 曜日 
*  *  *  *  * command

commandに実行したい処理を記述します。

各*(アスタリスク)には下記の数値を指定することができます。

  • 分(0-59)
  • 時(0-23)
  • 日(1-31)
  • 月(1-12)
  • 曜日(0-6) ※0は日曜日

cronのコマンド

cronで使用するコマンドは下記の通りです。


#crontabの編集を行い場合
$crontab -e

#crontabに追加されているエントリーを確認
$crontab -l

#crontabのエントリーを削除
$crontab -r
MACの場合crontab -eでエントリーの追加を行ってもcrontab: “/usr/bin/vi” exited with status 1等のメッセージが出力され保存することができません。その場合はexport EDITOR=/usr/bin/vimを設定してcrontab -eを実行してください。installing new crontabが表示されれば保存可能となります。

crontabへのエントリー追加

実際にエントリーを追加/更新する場合はcrontab -eコマンドを実行します。追加したエントリーはcrontab -lコマンドで確認することができます。

【エントリーの例】

毎分特定の処理を行い場合はすべてを*にします。


* * * * * command

毎時10分に特定の処理を行いたい場合は下記のように記述します。0:10、1:10、2:10…で実行されます。


10 * * * * command

毎朝8:00に特定の処理を行いたい場合は下記ように記述します。


0 8 * * * command

月曜日の朝8:00に処理を行いたい場合は下記のように記述します。


0 8 * * 1 command

【実行例】

実際にcronにエントリーを追加して動作確認を行ってみましょう。


* * * * * ls >> /Users/reffect/cron.log

上記を設定するとreffectユーザのホームディレクトリである/Users/reffectの下にcron.logが作成され、cron.logファイルが作成されます。

このようにcronは書式さえ理解できれば簡単に定期処理を行うことができます。

cronの設定がわかったので、cronを利用するLaravelのタスクスケジュールの設定方法を確認していきます。

Laravelのタスクスケジュールの設定

Laravelのタスクスケジュールはapp/Console/Kernel.phpファイルの中に記述します。タスクを実行する方法にはCommand(コマンド)を利用した方法、Closure(クロージャー)を利用した方法、直接コマンドを利用した方法などいくつかあります。本文書では1つ1つ設定方法を確認しておきます。

app/Console/Kernel.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 = [
        //
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        // $schedule->command('inspire')
        //          ->hourly();
    }

    /**
     * Register the commands for the application.
     *
     * @return void
     */
    protected function commands()
    {
        $this->load(__DIR__.'/Commands');

        require base_path('routes/console.php');
    }
}

Cronへの登録

Laravelのタスクスケジュールを設定するためには、cronに必ず下記の1行を追加しておく必要があります。crontab -eコマンドで追加してください。


* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1

path-to-your-projectはLaravelをインストールしたディレクトリを設定してください。各自の環境により異なる値になります。

cronへのエントリーがすべて*(アスタリスク)になっているので、毎分php artisan schedule:runが実行されます。Laravelでタスクスケジュールを追加している場合は、scheduleを経由して登録されたタスクが設定された時刻に実行されます。

Commandとは

app/Console/Kernel.phpファイル内の各所にCommandという単語がいくつも記述されています。

Commandって何?と思われた方も多いかと思いますがCommandという単語を意識することなくLaravelを使ったことがある人は知らず知らずのうちに利用しています。

データベースを作成する際に実行するphp artisan migrateのmigrateもCommandの一つです。Commandはphp artisan listコマンドで確認することができます。


$ php artisan list
Laravel Framework 5.8.32

Usage:
  command [options] [arguments]

Options:
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
      --env[=ENV]       The environment the command should run under
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  clear-compiled       Remove the compiled class file
  down                 Put the application into maintenance mode
  dump-server          Start the dump server to collect dump information.
  env                  Display the current framework environment

複数のCommandが事前に登録されていますが、自分でCommandを作成・登録することができます。作成したCommandは、タスクスケジュールで利用することができます。

Commandの作成

Commandはphp artisan make:commandで作成することができます。

動作確認のためWriteLogというCommandを作成します。


$ php artisan make:command WriteLog
Console command created successfully.

作成されたWriteLogはapp¥Console¥Commandsの下に作成されます。中身を下記のようになっています。


namespace App\Console\Commands;

use Illuminate\Console\Command;

class WriteLog 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, $description, handle()を設定していきます。

$signatureはCommandの名前の入力します。Commandを実行する際に利用します。:(コロン)で分けることでグループ化することができます。グループ化等についてはCommandを登録した後の確認で説明します。

ここでは$signatureを下記のように設定を行います。


protected $signature = 'writelog:info';

$descriptionにはCommandの説明を記述します。


protected $description = 'write info messages in log file';

handle()には実行したい処理内容を記述します。loggerのヘルパー関数を仕様してinfoレベルのメッセージをログファイルに書き込むだけのシンプルな処理内容です。


public function handle()
{
    logger()->info('This is WriteLog Command.');
}

Commandの作成は完了しましたが、Commandを作成しただけでは実行することができません。Commandを実行するためには、Commandの登録が必要になります。

Commandの登録

Commandの登録はapp¥Console¥Kernel.phpで行います。デフォルトでは何も設定されていない状態ですが$commands変数に作成したCommandの情報を登録します。


protected $commands = [
    //
];

作成したWriteLog Commandを追加します。


protected $commands = [
    Commands\WriteLog::Class,
];

追加した後にphp artisan listコマンドを実行するとKernel.phpに登録したWritelogの情報が表示されます。$signatureで入力した名前と$descriptionで入力した説明もここに表示されます。


$ php artisan list
  ・
  ・
 view
  view:cache           Compile all of the application's Blade templates
  view:clear           Clear all compiled view files
 writelog
  writelog:info       write info messages in log file

もし$signatureで:(コロン)を使用せずに名前だけ($signature = ‘writelog’;)入力した場合にphp artisan listを実行してもCommandは表示されます。


$ php artisan list
・
・
Available commands:
  clear-compiled       Remove the compiled class file
  down                 Put the application into maintenance mode
  dump-server          Start the dump server to collect dump information.
  env                  Display the current framework environment
  help                 Displays help for a command
  inspire              Display an inspiring quote
  list                 Lists commands
  migrate              Run the database migrations
  optimize             Cache the framework bootstrap files
  preset               Swap the front-end scaffolding for the application
  serve                Serve the application on the PHP development server
  tinker               Interact with your application
  up                   Bring the application out of maintenance mode
  writelogo            write info messages in log file

$signatureの設定値がCommand:nameでもCommandでもphp artisan listコマンドを実行すると表示されますが、表示される位置が異なります。Commandでは先頭のほうに表示、Command:nameではグループの名前にソートされアルファベット順になるので、writelogのグループ名では最後に表示されます。

登録したCommandの実行

Commandの登録が確認できたので、php artisanを使用して実行してみましょう。


$ php artisan writelogo:info

実行するとstorage/logs/の下にあるログファイルに下記のメッセージが追加されます。


[2019-08-20 03:44:14] local.INFO: This is WriteLog Command.

タスクスケジュールへの登録

Commandが単独で動作することが確認できたので、定期的にCommandが実行されるようにスケジュールへの登録を行います。登録はKernel.phpファイルで行います。

動作確認なので毎分実行させるためにscheduleメソッドの中に下記のように記述します。everyMinuteをつけることで毎分実行することができます。


protected function schedule(Schedule $schedule)
{
    $schedule->command('writelog:info')
             ->everyMinute();
}

登録後に毎分ログファイルの中にinfoメッセージが登録されれたタスクスケジュールへの登録が正常に行われたことになります。ログを見ると1分毎にログに書き込まれています。


[2019-08-20 04:13:01] local.INFO: This is WriteLog Command.  
[2019-08-20 04:14:00] local.INFO: This is WriteLog Command.  
[2019-08-20 04:15:01] local.INFO: This is WriteLog Command.  
[2019-08-20 04:16:00] local.INFO: This is WriteLog Command.  
[2019-08-20 04:17:00] local.INFO: This is WriteLog Command.  
[2019-08-20 04:18:01] local.INFO: This is WriteLog Command.  
[2019-08-20 04:19:00] local.INFO: This is WriteLog Command.

クロージャーを利用した方法

ここまではCommandを利用しましたが、クロージャーを使っても同じことを実行することができます。Kernel.phpファイルにcallメソッドを使ってクロージャーを記述します。


$schedule->call(function(){

    logger()->info('This message was writtend by Closure');

});

毎分ログファイルに上記のinfoメッセージが追加されます。


[2019-08-20 04:42:00] local.INFO: This message was writtend by Closure  
[2019-08-20 04:43:00] local.INFO: This message was writtend by Closure

シェルコマンドを利用した方法

シェルコマンドを利用する場合は、execメソッドを使用します。

動作確認なので、touchコマンドを利用して空ファイルの作成を行うタスクを追加します。


$schedule->exec('touch /Users/reffect/cron/new_file.txt')
         ->everyMinute();

指定したディレクトリの中にnew_file.txtファイルが作成されます。

処理結果をファイルに保存

タスクスケジュールを実行した内容を保存するためにsendOutputToメソッドを利用します。簡単な例ですが、月末のディスク容量を確認したい場合等にも利用することができます。


$schedule->exec('df -k')
         ->everyMinute()
         ->sendOutputTo(storage_path('logs/disk.log'));

storage_pathを設定しているので、Laravelインストールディレクトリのstorage/logsの下にdisk.logファイルが作成され、そのファイルにdf -kコマンドの出力結果が保存されます。

またCommandにはinfoメソッドもあり、Commandの中でinfoメソッドを使用することで指定したファイルにログファイルとは別で情報を保存することができます。

ここではinfoメソッドをファイルに書き込むために使用していますが、infoメソッドはphp artisanでCommandを実行した時にコンソールにメッセージを表示させるメソッドです。info以外にもerrorやline, comment等のメソッドがありますが、ファイルに書き込む場合に使用した場合はメソッドの違いがありません。コンソールだと文字の背景色がメソッド毎に変わったりといった違いがあります。

Kernel.phpファイルでsendOutputToメソッドを追記します。


$schedule->command('writelog:info')
         ->everyMinute()
         ->sendOutputTo(storage_path('logs/write.log'));

infoメソッドを使って、ログファイルの書き込みの前後にメッセージを書き込みます。


public function handle()
{
    $this->info('start logging');
    
    logger()->info('This is WriteLog Command.');

    $this->info('end logging');
}

loggerのヘルパー関数のinfoはログファイルへの書き込み、$this->infoメソッドはsendOutputToメソッドで指定したファイルへの書き込みを行います。

sendOutputToメソッドはファイル名を指定しただけでとファイルの上書きを行うので、ファイルに追記していきたい場合はsendOutputToの第2引数をtrueに設定するかappendOutputToメソッドを利用してください。


->sendOutputTo(FILE_PATH,ture);

->appendOutputTo(FILE_PATH);        

保存したファイルをメールで送信

sendOutputToで作成したファイルをメールで送信することができます。

ここでのメールの中身はsendOutputToに保存した内容です。

メールで送信するためにはメール設定が必要となりますが、動作確認を行うためなので.envファイルのMAIL_DRIVERをsmtpからlogに変更してメールの内容をログファイルに書き出します。


MAIL_DRIVER=log        

Laravelでのメール設定については下記を参考にしてください。

メールで送信するためにemailOutputToメソッドを使用します。


$schedule->command('writelog:info')
         ->everyMinute()
         ->sendOutputTo(storage_path('logs/write.log'))
         ->emailOutputTo('foo@example.com');

タスクスケジュールに登録しているので時間が経過するとログファイルにメールの内容が書き出されます。


[2019-08-20 05:52:01] local.DEBUG: Message-ID: 
Date: Tue, 20 Aug 2019 05:52:01 +0000
Subject: Scheduled Job Output For ['/usr/bin/php' 'artisan' writelog:info]
From: Example 
To: foo@example.com
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

start logging
end logging    

メール設定を適切に行えばメール送信できることが確認できます。