Notificationをデータベースに入れる
Notificationはジョブキュー的に処理されるし、別にメール通知とは限らない。
そもそもイベント・リスナみたいなもの。イベントが発火してリスナが受け取るのと同じように、通知が発生して通知連絡のクラスが起動する。
今回は、Notificationをデータベースにキューイングして構造を見ておこうと思う。
実験環境を準備する。
プロジェクトを作る
composer create-project laravel/laravel database-notification-sample cd database-notification-sample sed -i config/app.php -e 's|UTC|Asia/Tokyo|' sed -i .env -e 's/DB_/#DB_/' -e '/mysql/i DB_CONNECTION=sqlite' php artisan migrate
通知をやり取りするテーブルを作成する。
php artisan notifications:table php artisan migrate
クラスを作る。送出側
まずは、通知を送る側クラスを作る。
通知の本体を作る
php artisan make:notification MyNotification
このような感じで作られる。
app/Notifications/MyNotification.php
<?php namespace App\Notifications; use App\Models\MyModel; use Illuminate\Notifications\Notification; class MyNotification extends Notification { public function __construct( protected MyModel $model) { } public function via(){ return ['database']; } public function toArray(){ return ['testing' => 'Test']; } }
通知を送る側を作る
通知を送る側は、コマンドとして作り、任意のタイミングで実行できるようにする。
php artisan make:command SendNotification
app/Console/Commands/SendNotification.php
<?php namespace App\Console\Commands; use App\Models\MyModel; use Illuminate\Console\Command; use App\Notifications\MyNotification; use Illuminate\Support\Facades\Notification; class SendNotification extends Command { protected $signature = 'app:send-notification'; public function handle() { $model = MyModel::first(); Notification::send($model,new MyNotification($model)); } }
サンプルのモデルを作る
通知は、モデルに対し行われる。なので、モデルを用意する。
php artisan make:model MyModel -m
app/Models/MyModel.php
use Notifiable;
で通知を受け取るフラグを立てる。
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Notifications\Notifiable; class MyModel extends Model { use Notifiable; }
データベースに通知を置く
データベースに通知を置くのが本題なので、通知を設置するテーブルを用意する。
Seederを作る
php artisan make:seeder MyModelSeeder
Seeder を実行する。
php artisan migrate php artisan db:seed --class MyModelSeeder
database/seeders/MyModelSeeder.php
seeder は次のようにして、いくつかモデルのレコードを用意した。
<?php namespace Database\Seeders; use App\Models\MyModel; use Illuminate\Database\Seeder; class MyModelSeeder extends Seeder { public function run():void { foreach (range(0, 10) as $idx) { ( new MyModel() )->save(); } } }
通知を送る。
コマンドからモデルへ通知を送信する。
php artisan app:send-notification
通知を送出後にDBのテーブルを確認する。
id | type | notifiable_type | notifiable_id | data | read_at | created_at | updated_at |
---|---|---|---|---|---|---|---|
a3df8a96-f7ae-44ca-bf55-ba94662128f1 | App\Notifications\MyNotification | App\Models\MyModel | 1 | {"testing":"Test"} | 2024-02-13 14:39:13 | 2024-02-13 14:39:13 |
通知を見ればわかるが、どのモデルのID番号へ、通知を送る。そしてReadフラグ(日付)をつけて区別している。これで、データベース通知の仕様が見えてくる
model <- notification
通知を読み取る。
通知の処理をする起動ポイントを作る。
php artisan make:command ReadNotification
app/Console/Commands/ReadNotification.php
<?php namespace App\Console\Commands; use App\Models\MyModel; use Illuminate\Console\Command; class ReadNotification extends Command { protected $signature = 'app:read-notification'; public function handle() { /** @var MyModel $model */ foreach (MyModel::all() as $model) { /** @var \Illuminate\Notifications\DatabaseNotification $notification */ foreach ($model->notifications as $notification) { dump($notification->type); dump($notification->data); } } } }
通知の読み取りを実行する
実行結果
# app/Console/Commands/ReadNotification.php:18 "App\Notifications\MyNotification" # app/Console/Commands/ReadNotification.php:19 array:1 [ "testing" => "Test" ]
覚えること。
DatabaseNotificationは、notificationsテーブル
とmodel
とモデル・リレーションしている。
mophyを使ってる。
Notificationはモデルへ通知するのが基本。
ReadAtをnull / not null を使ってフラグとして管理している。
マニュアルを一読すると、「イベント」のように見えるけど、イベントとはぜんぜん違う仕組みだとわかる。
どこにもキューが出てこないし、queue:work
も出てこない。
通知(notification)を消すには、通常のデータベースと同じ処理になる。
DatabaseNotificationは次のように定義されている
class DatabaseNotification extends Model
xxXXXedと書かれるので、イベントのように見えるがイベントではないし、Notificationのチャンネルがあるわけでもない。よってこれは、ただの1対多のモデル・リレーションである。
モデルに「メッセージ(Data)」を紐づけて送っている。モデルにイベントを通知すると考えると、データベース通知は、やや過剰な仕様であり、Dataにデータを紐付ける程度のものである。
サンプルやドキュメントで「InvoicePaid」「XXXCreated」みたいな通知クラスをみかける。まるでイベント名であるが、イベント・リスナの枠組みではないとわかる。メール・Slackと、データベース通知は、2つはまったく違う。Slack・Mainではキューも使っている。一方でデータベース通知はテーブルに書くだけであり、キューの枠組みではないとわかる。これは初見殺しで、ややこしい。
この違いは、use ShouldQueue;
やuse Queueable;
の有無である。データベース通知をキューにいれるかは、コーディングで明確に書く必要がある。ただ、データベース通知ではキューをは必要がないので特に書かれない。
2024-05-30
下書きから更新