【Laravel】タスクスケジューラを使ってみる!

どーも!

たかぽんです!

今回はLaravelのタスクスケジューラを使ってみようと思います!

1つ注意点として、一部、cron(後述)についてはUNIX系OS, (MacやLinux)の方向けとなります...

タスクスケジューラを使ってみる!

さて、タスクスケジューラをつかってみるんですが...

そもそもタスクスケジューラってなに?

と言う方もい多いかもしれません...!

まぁ、この記事を検索して来た方は知ってそうですが...

そもそもタスクスケジューラとは、定期的に実行したい処理を行う際に使います。

例えばメールで毎日朝4時に決まってメールを送る機能があったとして、新しい通知の入力を前日に行ったらその次の日の朝4時に定期メールを出す...

とかだとこのスケジューリングが最適ですね。

公式リファレンスだと以下の項目になります。

基本は以下に従いますが、本記事のみみていただければ実装できるよう解説していきますので、本記事を読み終えた後読んでみると理解が進んでいいかと思います!

実際にとても簡単なものを実装してみます!

ただログを出すだけのタスクをスケジューリングしてみましょう!

タスクスケジューラを理解しよう!

さて、それでは早速やっていきます!

前提として、初期状態のlaravelのリポジトリを作成している前提でお話しします。

もしまだの方は以下を参考にlaravelをlocal環境にご用意してハンズオンで行うのをおすすめします。

Laravelもう準備OK!というかたは早速、タスクスケジューラを設定するファイルをのぞいてみましょう!

Kernel.phpの準備

タスクスケジューラを設定ファイルのパスは "app/Console/Kernel.php" です。

このファイルで、どんなタスクをどれくらいのスケジュールで実行するの?って言うのを定義してあげます。

例えばAというタスクを毎分実行します!とか、毎日何時に実行します!とかですね。

以下が実際のファイルの中身になります。

<?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');
    }
}

ここに繰り返す処理を書いていくんですが、まずはそのまま直接内容を書いてみましょう!

そのまま書く場合、"クロージャを使って書く"等と言われたりしますね。

上記のKernel.phpのscheduleメソッドを以下のように書き換えます。

     /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        // クロージャでタスクの登録(直接ここに処理も記述)
        $schedule->call(function () {
            \Log::info('毎分ログ出力テスト - closure');
        })->everyMinute();
    }

さて、クロージャで書いたのはいいのですが、上記の手法はあまりよしとはされていません...

というのも、一行程度で済む処理ならクロージャでいいんですが、大抵定期処理で行う場合行数が多くなってしまいます。

あくまでKernelはタスクの設定だけにとどめて、実際の処理は別のところ似合った方がやはりみやすいですよね。

そのため、artisanコマンドとして定期的に行いたい処理を別のファイルとして書いておき、それを呼び出すようにする方法も試しておきましょう。

定期実行に使うartisanコマンドを作成する

では、実際の定期実行次に行う処理をするartisanコマンドを作っていきましょう。

artisanコマンドの作り方に関しては以下の記事で解説をしていますので、ご参考にしてみてください。

以下の記事のままコマンドを作成して、一部微修正する形で動くので、よければそちらを作成すると理解が進むかと思います。

上記記事を読み終えた方、もしくはすでにご存知の方は以下のようにCronTestCommandとしてコマンドを作成しましょう。(あるいは一部修正しましょう!)

やっていることはClosureと同様で一行のログを出すだけです。

(今回はお試しのためログ出しだけですが...、実際はこのログを出す代わりにメールの送信やDBの操作などを書いていくイメージですね!)

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

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

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'cron動作確認用のテストコマンドです。';
    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        // ここに書いた処理が実際に定期実行される処理!!!
        \Log::info('ログ出力テスト - command');
    }
}

上記ファイルの追加により、"display:cron-test"と言う名前のコマンドが登録できました。

実行したら、"ログ出力テスト - command"というログを出すことができます。

clonの設定をする

さて、ここまでできたら次はclonの登録をしていきましょう!

いきなりcron・・・?クーロンってなんだ・・・?ってなるかと思います...。

(お恥ずかしながら僕も最初は?でした...w)

公式のリファレンスでもいきなりcron登録....みたいなこと書かれているけど、登録の方法とかはないんですよね...w

(知ってて当たり前と言われればそうなのかも...?)

cronとは、MacやLinuxなどの、UNIX系のOSで使用される、定期実行を管理してくれるプログラムです。

本来、laravelでタスクのスケジューリング(いつどのタスクをじっこうするのか?)を管理するのでcronいらないのでは?と思いますが、cronがlaravelのスケジューリング用のコマンド(schedule:runというartisanコマンド)を毎分実行します。

schedule:runではKernelに登録されているタスクの中から、実行する必要のあるもののみ(定義されているのが毎分ならその毎分、毎時なら毎時)実行するようになっています。

本来はcronの中に毎分laravelのAコマンドを、毎時laravelのBコマンドを実行する...といったことを書く必要があるので、crontabに書く行数が定期実行したい処理の数だけ増えますが、cronでは一つのartisanコマンドだけを実行するようにしてあげることで、laravel側だけいじれば全て管理できるようになるわけですね!

今回、筆者はMacで試しているので、Macの方はそのままお進みください。

実はlaravelのversion 8.xからだとこのcronを使わずに動作確認ができるらしいんですが、筆者はまだversion 7.13なので...試せず...

一旦cron版でいこうと思います。(できれば近いうちに8.xでも試して記事起こせればなと...。)

気になる方は、"schedule:task"コマンドについて調べてみるといいかもしれません。

その手法であればWindowsの方もcronを使わずに動作確認ができそうな気がしてます...!(8.x限定だけど!)

では、今回はあなたのPCのOSでcronを使用して書いていきます。

まず最初にLaravelのプロジェクトフォルダのフルパスを確認しておきます。

確認方法は...

コンソールで最初にLaravelのインストール時につけた名前のディレクトリに移動します。

そこで、pwdコマンドで確認することができます。

僕の場合はLaravelのプロジェクト名を"MyProject"と名前をつけたので、cdでそこへ移動し、pwdを打つと..

(base) Takapon:MyProject taka$ pwd
/Users/taka/MyProject

"/Users/taka/MyProject"これが僕の場合のフルパスになります。

皆さんの場合もおそらく違うため、各自控えておきましょう。

では、上記を控えたら、コンソールにて"crontab -l"コマンドをつかって、現在のcronの設定内容を確認します。

このコマンドを実行する場所はどこでも大丈夫です。

(base) Takapon:MyProject taka$ crontab -l
* * * * * cd /Users/taka/MyProject && php artisan schedule:run >> /dev/null 2>&1

上記のコマンドはcronの設定なんですが、毎分"php artisan schedule:run"というコマンドを実行して、出力結果は捨てるという処理になっています。

もしこのコマンドですでに何かしらの設定が書かれている場合、その設定を消さないように気をつけつつ修正する必要があります。

個人でしかPC使ってないし、今までcronとか使ったことないです!という方は気にしなくても大丈夫かと思います!

(個人のPCで既に定期実行が設定されていることは少ないとおもいますが、すでにサービスとして動いているサーバーなどのcrontabを編集する際など....結構設定されている場合があると思うので注意です!既存で動いている定期実行へ影響を及ぼす可能性がありますので!)

では、-lで現状を確認したら、-eで編集をします。

以下を入力するとvimというコンソール上でファイルの編集ができるエディタが立ち上がります。

(base) Takapon:MyProject taka$ crontab -e

立ち上がったら、以下をコピペして入力するんですが、このVimが少し曲者で操作を覚えていない人だと少し苦労します...(Vimerと呼ばれる方々からは愛されているエディターですね...!)

最低限の編集をしている動画を後述しておきますので、そちらを参考にしてみてください。

* * * * * cd /Users/taka/MyProject && php artisan schedule:run >> /dev/null 2>&1

上記の"/Users/taka/MyProject"の箇所は先ほどの自分のLaravelプロジェクトへのフルパスを入れてください。

他はそのままで大丈夫です。

では、設定方法をご説明します。

まず、以下のコマンドで現在のcronの設定内容を確認します。

crontab -l

次に設定のファイルを設定します。

以下のコマンドを実行すると編集するエディタが開きます。

crontab -e

エディタを開くと、最初はそのままでは入力できないので、"A"キーを押します。

すると、左下に "- INSERT -"という文字がでるので、この状態だとちゃんと入力できるようになります。

上記のパス部分を各自のものに修正したcronの設定内容をコピーして、貼り付けます。

貼り付け終えたらMacだとおそらく左上にあるんですが、エスケープキー(esc)を押すと"- INSERT -"が消えます。

この状態で、":wq"を入力してEnterを押すとセーブしてvimを閉じることができます。

この際、crontabの編集はOSのsystemに近いファイルになるため、本当に修正してもいい?といったメッセージが出ますが、問題なければOKを押してください。

閉じたら再度crontab -lで中身が変わっているかを確認しておきましょう。

(この際、追加した行のしたに一行空行が入っているかどうかも確認しておいてください。おそらくデフォルトで入るようになっている気がしますが...もし入っていなかったら再度編集をして入れてください。cronの設定では末尾に空行がないとその直前の一行が読み込まれないようです...!)

確認を終えたらこれでcronの設定は終わりです!

上記手順を行っている動画を以下に用意したので、ご参考にしてみてください。

ログで動いているかを確認する

さて、それではcronの設定も終われば裏ではちゃんと実行され始めているはずです。

ただ、確認する術がないので、ログをみて確認してみましょう。

今回、KernelやCommandで"\Log"でログを出すようにしました。

デフォルトではlaravelのログが毎日作成され、そこへ出力されるようになっているはずです。

そのため、そのログが保存されているファイルへ移動して、その中身をみてみましょう!

コンソール上で以下の場所へ移動して、tailコマンドを使ってログファイルの末尾5行を最初に表示してみてみましょう。

少し詳しく説明すると、プロジェクトファイルの直下にstorageというフォルダがあるので、その中のlogsの中に今日の日付(筆者の場合は10/18日のもの)のログを対象に、tailコマンドを実行して、5行だけ出してみます。

(base) Takapon:logs taka$ pwd
/Users/taka/MyProject/storage/logs
(base) Takapon:logs taka$ ls
laravel-2019-08-25.log	laravel-2019-12-16.log	laravel-2020-02-22.log	laravel-2020-03-14.log	laravel-2020-04-15.log	laravel-2020-04-18.log	laravel-2020-05-01.log	laravel.log
laravel-2019-12-07.log	laravel-2020-02-16.log	laravel-2020-02-23.log	laravel-2020-03-26.log	laravel-2020-04-16.log	laravel-2020-04-25.log	laravel-2020-10-18.log
(base) Takapon:logs taka$ tail -f -n 5 laravel-2020-10-18.log
[2020-10-18 14:59:00] local.INFO: ログ出力テスト - command
[2020-10-18 15:00:00] local.INFO: 毎分ログ出力テスト - closure
[2020-10-18 15:00:01] local.INFO: ログ出力テスト - command
[2020-10-18 15:01:00] local.INFO: 毎分ログ出力テスト - closure
[2020-10-18 15:01:00] local.INFO: ログ出力テスト - command

tailを実行したままにしておくと、ログが連なって増えて行っているのがわかるかと思います。

これが、1分ごとにクロージャとコマンドで処理しているログ出しが行われているんですね。

"cat laravel-2020-10-18.log"としても今まで出たログの確認はできますが...

tailコマンドを使うことで追記された分も表示を更新してくれるので結構便利です。

もしもここに表示されない場合はログの出し場所が初期設定とは異なる場合があるので、試しに"laravel.log"も覗いてみるといいかもしれません。

(ちなみにこちらは日付関係なく全てのログをこの"laravel.log"に追記していく運用で使うものになっています。

それでもなかった場合はそもそもcronが動かせていないか、独自で別の場所にLaravelのログを書き出している可能性が高いのでそちらも調べてみるといいでしょう。

cronの動作確認は以下で可能です。

(初回はおそらくパスワードを聞かれるので、入力しましょう。)

(base) Takapon:MyProject taka$ sudo /usr/sbin/cron status
cron: cron already running, pid: 392

cron already runningになっていればcronは走っているはずです。

これでログが出ていない場合はLaravelのログ周りだったり、cronの設定間違い等の可能性が高いです。

間違い等がないか確認をして再度みてみてください。

確認を終えたらcronの設定を戻す

必ずしも必須ではないんですが、このcron、ずっと後ろで動いてしまうので、cronの設定は戻しておきましょう。

もちろん、実際に運用で使うなら話は別ですが、そうでないなら消しておいた方がいいです。

とはいっても皆さんはもう既にご存知、"crontab -e"コマンドで中身を削除して"crontab -l"で何も表示されていなければOKですので、先ほどの設定時のを参考に削除しておきましょう。

また、不要であればCommandやKernelの記述等も削除して大丈夫です。

まとめ

今回はLaravelでタスクスケジューリングを行いました!

ちょっと長くなってしまったんですが...仕方なし...

cronを使っての方法で動作確認までしましたが、知識がないと結構てこずります...ました...

たぶんcron知らない人だと公式リファレンスだけみて理解できなさそう...

記事中で少し触れましたが8.xで新しくでたコマンドで"schedule:work"というものがあり、おそらくですが、それがcronを使わずともできるので、laravelのサーバーでcronのような動きをしてくれるもの(毎分php artisan schedule:runを実行する)を用意してくれているんだと思います...

ちょっと時間があれば触ろうと思うので、お待ちいただければと...!

それでわ!

おすすめの記事