Laravelで定期的にタイマー実行する全16実例!(Task Scheduling)

さてさて、ここのところ本業の開発が忙しかったのであまり記事を更新することができていませんでしたが、たまにいただくお問い合わせがとても励みなっているので少しずつでも有益な情報を公開できればいいな、と考えている今日この頃です。(今後ともどうぞよろしくお願いいたします😊✨)

さて、開発する案件が増えてくるとクライアント様からのご要望でしばしば上がってくるのが「自動バックアップ」だったりします。

つまり、

毎週日曜日の深夜2:00に定期的にバックアップをとりたい!

といったものですね。

もしこれを実現しようと考えた場合は、cron(Linuxサーバーなどに備わっているタイマー実行プログラム)を設定すればいいのですが、実行するプログラムの数が多くなってくるとcronをいちいち設定するのはめんどうだったりします。

そこで登場するのが今日紹介するLaravelの「Task Scheduling」です。

Task Schedulingを使うとcronを直接設定するより直感的にタイマー実行させることができるようになりますし、毎回sshなどでcronを設定しなくてもよくなる(Laravelのコードを変更するだけでOK)ので、保守もしやすくなると言っていいでしょう。

ということで、今回はLaravelでタイマー実行させる方法を紹介します!

実行環境: Laravel 5.8

Task Schedulingの仕組み

もしかすると少し勘違いしてしまうかもしれませんが、Task Schedulingといっても、実はcronを使ってプログラムを実行することになります。

仕組みは以下のようになっています。

  1. cronを使って「1分ごとに」Task Scheduleにアクセス
  2. Task Scheduling がプログラムを実行するべきかどうかを判別
  3. その結果で、プログラム実行 or 無視

つまり、とにかく最大限起動して必要があればそのコードを実行するという仕組みになっています。

そのため、デメリットとしてはcronで特定の時間だけ実行するよりサーバーへの負荷が大きくなってしまう傾向があります。(そのため、あまり性能が良くないサーバーを使っている場合は直接 cron を利用することをおすすめします)

Task Schedulingを有効にする

前項目でも書きましたが、Task Scheduling は cron を使うので、サーバーに以下のようにタイマー設定しましょう。

まずはコマンドラインでサーバーにログインし、以下のコマンドを実行します。

sudo crontab -e

するとコマンドライン上でテキストディターが起動し、cronを設定することができるようになるので、以下のタイマー設定を追加して保存しましょう。

※ 例えば、エディタがviでしたら、起動したらiを押して入力モードにし、以下のタイマー設定を追加、そしてEsc を押すとモードが戻るので:wq!と入力しEnterキーを押して保存します。

* * * * * php /PATH/TO/YOUR/LARAVEL/artisan schedule:run >> /dev/null 2>&1

ちなみにこのタイマー設定は以下のような意味になっています。

1.* * * * * ← 毎分実行します

2.php /PATH/TO/YOUR/LARAVEL/artisan schedule:run ← あなたのLaravelが設置されているフォルダの artisanファイルでスケジュールを実行する(cronはシステム全体に影響するものなので絶対パスで指定するか、もしくはcdコマンドで移動してから実行します)

3./dev/null 2>&1 ← エラー出力しない、エラーメールを送信しない

さぁ、これでLaravelのTask Schedulingが有効になりました。
では、次の項目からは実際にプログラムを実行してみましょう!

Task Scheduling でプログラムを実行する

Task schedulingで任意のプログラムを実行する方法は4つありますが、どの方法でもapp/Console/Kernel.phpの中で設定をすることができます。
それぞれ見ていきましょう。

基本的な使い方 call()

一番簡単な実行方法はcall()を使ったものです。
以下のように関数内に好きなコードを記述することができます。

protected function schedule(Schedule $schedule)
{
    $schedule->call(function() {

        echo 'call が呼ばれました!';

    });
}

では、この状態で一度手動で実行してみましょう。コマンドはphp artisan schedule:runです。

※ ちなみに、今の状態では時間指定をしていないので毎回実行されることになります。

なお、関数内のコードが長くなってしまったり別のLaravelサイトでも使いまわしをしたい場合は、以下のようにクラス化しておくといいでしょう。

まず専用クラスを作ります。(今回は/app/Hello.phpに作ります)

<?php

namespace App;

class Hello {

    public function __invoke() {

        echo '__invoke() が呼ばれました!';

    }

}

そして、以下のようにcall()へインスタンスを入れます。

protected function schedule(Schedule $schedule)
{
    $schedule->call(new \App\Hello($schedule));
}

実行するとこうなります。

command()でArtisanコマンドを実行する

Task Schedulingでプログラムを実行する2つ目の方法は、LaravelのArtisanコマンドを実行させる方法です。

例えば、view:clearで定期的にビューのキャッシュをクリアしたい場合は以下のようになります。

$schedule->command('view:clear');

job()でキューを実行する

キューというのは、簡単に言うと「やらなきゃいけないけど処理が重たいから後で分散して実行する」プログラムのことです。(詳しくはまたそのうち記事を書きたいと思います)

そして、キューを実行するには以下のようにします。

$schedule->job(new Announcement());

exec()で直接コマンドを実行する

以下のようにすると直接コマンドを実行することができます。(※ ただし、実行権限をきちんと設定しておかないと動かない場合があります)

$schedule->exec('bash ./sh/test.sh');

時間指定する

では、Task Schedulingの “キモ” となる時間指定をする方法を紹介していきます。

※ なお、コードが見やすくなるので、以下のTask Schedulingに時間指定をしてみます。

$schedule->command('xxx');

1分、5分、10分、15分、30分ごとに実行する

// 1分ごと
$schedule->command('xxx')->everyMinute();

// 5分ごと
$schedule->command('xxx')->everyFiveMinutes();

// 10分ごと
$schedule->command('xxx')->everyTenMinutes();

// 15分ごと
$schedule->command('xxx')->everyFifteenMinutes();

// 30分ごと
$schedule->command('xxx')->everyThirtyMinutes();

1時間ごとに実行する

// ノーマル
$schedule->command('xxx')->hourly();

// 分を指定する場合
$schedule->command('xxx')->hourlyAt('15');

1日ごとに実行する

// ノーマル
$schedule->command('xxx')->daily();

// 時刻を指定する場合(この例は、毎日9時に実行)
$schedule->command('xxx')->dailyAt('9:00');

// 1日に2回(この例では、7時と19時に実行)
$schedule->command('xxx')->twiceDaily(7, 19);

1週間ごとに実行する

// ノーマル
$schedule->command('xxx')->weekly();

// 時刻、曜日を指定する場合(この例は、毎週日曜の朝9時に実行)
$schedule->command('xxx')->weeklyOn(0, '9:00');

また、曜日ごとに指定する場合は以下のように指定することもできます。

$schedule->command('xxx')->weekly()->mondays();       // 月曜日
$schedule->command('xxx')->weekly()->tuesdays();      // 火曜日
$schedule->command('xxx')->weekly()->wednesdays();    // 水曜日
$schedule->command('xxx')->weekly()->thursdays();     // 木曜日
$schedule->command('xxx')->weekly()->fridays();       // 金曜日
$schedule->command('xxx')->weekly()->saturdays();     // 土曜日
$schedule->command('xxx')->weekly()->sundays();       // 日曜日

※ ここで重要なのが毎週特定の曜日に実行させたい場合はweekly()と一緒に使わないといけないという部分です。もし忘れてしまうと「指定曜日なら何回でも」実行されてしまうからです。

1ヶ月ごとに実行する

// ノーマル
$schedule->command('xxx')->monthly();

// 日にち、時刻を指定する場合(この例は、毎月15日の朝9時に実行)
$schedule->command('xxx')->monthlyOn(15, '9:00');

4半期ごと(3ヶ月に一回)実行する

$schedule->command('xxx')->quarterly();

1年ごとに実行する

$schedule->command('xxx')->yearly();

平日だけ実行する

// 平日(月〜金)に1時間ごとに実行
$schedule->command('xxx')
    ->weekdays()
    ->hourly();

週末だけ実行する

// 週末(土、日)に1時間ごとに実行
$schedule->command('xxx')
    ->weekends()
    ->hourly();

特定の時間に実行

// 9:00時実行
$schedule->command('xxx')->at('9:00');

時間の範囲を指定して実行する

// 9時〜12時なら、1時間ごとに実行
$schedule->command('xxx')
    ->between('9:00', '12:00')
    ->hourly();

独自の判別をして実行

$schedule->command('xxx')->when(function(){

    return true; // trueなら実行、falseなら無視

});

もしくは、when()とは逆にskip()を使うこともできます。

$schedule->command('xxx')->skip(function(){

    return false; // trueなら無視、falseなら実行

});

その他の設定方法

環境を指定して実行

// ローカル環境のときだけ実行
$schedule->command('xxx')->environments(['local']);

タイムゾーンを指定して実行

// America/Vancouver で9:00なら実行
$schedule->command('xxx')
    ->timezone('America/Vancouver')
    ->at('9:00');

もしくは、全体的にタイムゾーンを指定したい場合は以下のようにscheduleTimezone()を設置すればOKです。

class Kernel extends ConsoleKernel
{
    // 省略

    protected function scheduleTimezone()
    {
        return 'America/Vancouver';
    }

おまけ:テストのためにLaravelの時刻を固定する

Tast Schedulingはとても便利なのですが、一点だけ困ることがありました。それが、「実行テスト」です。

通常のコードをテストするにはブラウザなどですぐ確認できますが、Task Scheduling の場合は、うまくコードが実行されるかその時間にならないとわからないという状況になってしまいます。

しかし、これでは一日中パソコンの前で待っていないといけなくなってしまいますので、「おまけ」として現在時刻をテストとして強制的に変更する方法をご紹介します。

やり方はこうなります。

$test_dt = Carbon::create(2019, 5, 31, 19);
Carbon::setTestNow($test_dt);

この例では、Laravelの中では2019年5月31日の19時で時間が固定されることになります。

ちなみに、このコードは実行するプログラムの直前にでも書けばいいですが、もしLaravel全体の時間を変更したい場合はapp/Providers/AppServiceProvider.phpboot()内にでも書いておけばいいでしょう。

おわりに

ということで今回はLaravelを使ってプログラム実行のタイマー予約をご紹介しました。

最近では通常のレンタルサーバーであってもcronが普通に使えるようになっていますので、それほど手間ひまをかけずにこの機能を利用できるんじゃないでしょうか。

ただ、私のケースでいうと毎分必ずプログラムが呼び出されるという冒頭に書いたデメリットがあるのでこれまではあまり使ってはいませんでしたが、こちらも最近のサーバーは性能が良くなってきていますし、それもそれほど問題じゃないのかななんて思ったりもしています。

みなさんも一度チャレンジしてみてはいかがでしょうか。

ではでは〜!