【Laravel】Task Schedulingを使って予約通知機能を作る


今回、タスク管理アプリを作っていた際にメールで通知機能を作ろうと思って苦戦した末に完成したので共有します。この通りに作成していただければ完成します。

Task Schedulingとは処理を定期的に実行することです。例えば、毎日7時にメールを送信するなどといった機能です。詳しくはこちらの記事を参考にしてください。
https://laravel.com/docs/5.1/scheduling

① まず、ファイルを作成しましょう。

php artisan make:command Batch

② 実行するコマンドの名前になります。コンソールにて、ここで付けた名前で実行します。

app/Console/Commands/Batch.php
protected $signature = 'command:test';

③ php artisan でコマンド一覧などを表示させたときに、説明が出てきます。わかりやすい説明文を書いておきましょう。

app/Console/Commands/Batch.php
protected $description = 'laravel commandのtest';

④ handleメソッドに実際に行いたい処理を書きましょう

app/Console/Commands/Batch.php
public function handle()
    {
        // バッチ処理の誤差
        $from = date('Y/m/d H:i:s', strtotime('-5 seconds'));
        $to = date('Y/m/d H:i:s', strtotime('+5 seconds'));

        // $formから$toの間のタスクをデータベースから取り出す。
        $todos = Todo::where('deadline', '>', $from)
                     ->where('deadline', '<', $to)
                     ->get();
        //メール送信をする
        foreach ($todos as $todo) {
            Mail::raw($todo->deadline, function ($message) use ($todo) {
                $message->to($todo->user->email)->subject($todo->todo);
            });
        }

    }

⑤ ターミナルを開いて、crontab -eでcronの設定編集画面を開く

* * * * * cd /自分のプロジェクトへの絶対パス && php artisan schedule:run >> /dev/null 2>&1

viで開くので、iを入力し、上記を貼り付けたらescを押して、:wqで保存して抜けることができます。

⑥ 定期的に実行するコマンドを書きましょう

app/Console/Kernel.php
$schedule->command('command:test')->everyMinute();

everyMinute(); は1分に1回実行します。

詳しくはこちらの記事を参考にしてください。
https://codelikes.com/laravel-command-scheduling/#toc1

⑦ メールを設定しましょう

.env
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=xxxxx@gmail.com
MAIL_PASSWORD=生成したパスワード
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=xxxxx@gmail.com
MAIL_FROM_NAME="好きな名前"

今回はGmailを使います。
パスワードの設定はこの記事を参考にして設定してください。
https://www.howtonote.jp/google-account/2step-verify/index6.html

 コードの説明

app/Console/Commands/Batch.php
$from = date('Y/m/d H:i:s', strtotime('-5 seconds'));
$to = date('Y/m/d H:i:s', strtotime('+5 seconds'));

定期処理はlaravel.logを確認すると

コマンドが実行されるのは[2021-04-05 17:39:00]のときもあれば[2021-04-05 17:40:01]の場合もあったのでその誤差を取っています。strtotime('+1hour -5 seconds') strtotime('+1hour +5 seconds') とすることで1時間前のタスクを取得することもできます。

詳しくはこちらの記事を参考にしてください。
http://bashalog.c-brains.jp/14/04/02-100000.php

app/Console/Commands/Batch.php
$todos = Todo::where('deadline', '>', $from)
             ->where('deadline', '<', $to)
             ->get();

formからtoの間のタスクをデータベースから探して取り出します。
詳しくはこちらの記事を参考にしてください。
https://blog.capilano-fw.com/?p=665#get

app/Console/Commands/Batch.php
$message->to($todo->user->email)->subject($todo->todo);

$todo->user->emailでタスクの持ち主のメールアドレスに送信します。

詳しくはこちらの記事を参考にしてください。Eloquent:リレーションの知識が必要です。理解できなければ予備のメールアドレスを記入するのでも構いません。
https://readouble.com/laravel/8.x/ja/eloquent-relationships.html#one-to-one

 完成のコードはこちら

app/Console/Commands/Batch.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Models\Todo;
use Illuminate\Support\Facades\Mail;

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

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'laravel commandのtest';

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

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        // バッチ処理の誤差
        $from = date('Y/m/d H:i:s', strtotime('-5 seconds'));
        $to = date('Y/m/d H:i:s', strtotime('+5 seconds'));

        $todos = Todo::where('deadline', '>', $from)
                     ->where('deadline', '<', $to)
                     ->get();

        foreach ($todos as $todo) {
            Mail::raw($todo->deadline, function ($message) use ($todo) {
                $message->to($todo->user->email)->subject($todo->todo);
            });
        }
    }
}



app/Console/Kernel.php
<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use App\Console\Commands\Batch;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        //
        Batch::class,
    ];

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

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

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





今回の参考記事はこちらになります。
https://laravel.com/docs/5.1/scheduling
https://codelikes.com/laravel-command-scheduling/#toc1
http://bashalog.c-brains.jp/14/04/02-100000.php