簡単3ステップ!Laravelのデータベース通知をカスタマイズ

どうも!エンジニアの小林です。よろしくお願いします。

現在クラフでは自社サービスを絶賛開発中でして、バックエンドのフレームワークにはPHPで人気のLaravelを採用しております。

最近、Laravelに標準で組み込まれている、データベース通知機能をカスタマイズする機会がありました。色々なサイトを参考にさせていただき無事完成することができたので、備忘も兼ねて記事にしました。初学者の方でもわかりやすいように丁寧に解説したいと思います。

はじめに、Laravelのデータベース通知機能というのは、画面右上のベルマークをクリックすると自分宛ての通知が表示されるという、WEBアプリでよく見かける”アレ”を作成するための機能になります。

標準の通知機能では「〇〇プロジェクトに招待されました。」などの固定文言を通知するのには適しているのですが、プロジェクト名が「△△プロジェクト」に更新されたら通知内容も更新後のプロジェクト名を表示したい、という場合は、固定文言ではなく通知データに対してプロジェクトデータを紐付けておくと扱いやすくなります。

今回は、通知レコードに対してリソースを紐付けて保存・取得するというカスタマイズをおこないました。

1 やりたいこと

  • 通知レコードの保存時に、関連するリソースをデータベースへ保存するようにカスタマイズする。
  • 通知レコードの取得時に、関連するリソースを紐付けて取得するようにカスタマイズする。
  • 関連するリソースは様々なクラスを扱えるようにモデルのポリモーフィズムを使用する。
  • 前提として、今回の記事はLaravelのデータベース通知機能がすでに実装されていることを前提としています。

またLaravelのバージョンは8.xを使用しております。

2 通知レコードの保存時に関連するリソースをデータベースへ保存する

2-1 notificationsテーブルを拡張する

まずはじめに、notificationsテーブルに関連リソースを保存するカラムを追加するために、マイグレーションファイルを作成します。

nullableMorphsというのが、ポリモーフィズムの指定となり、クラス名とキーとなる値を保持することで、任意のクラスとリレーションすることが可能となります。

以下のマイグレーションを実行すると、「notifications」テーブルに「resource_type」列と「resource_id」列が追加されます。

    public function up()
    {
        Schema::table(‘notifications’, function (Blueprint $table) {
            $table->nullableMorphs(‘resource’);
        });
    }
    public function down()
    {
        Schema::table(‘notifications’, function (Blueprint $table) {
            $table->dropMorphs(‘resource’);
        });
    }

2-2 DatabaseChannelを拡張する

関連するリソースがnotificationsテーブルへ保存されるように、DatabaseChannelを拡張したクラスを作成します。

buildPayload() の戻り値に「resource_type」と「resource_id」を追加しています。

app/Notifications/Channels/DatabaseChannel.php

namespace App\Notifications\Channels;
use Illuminate\Notifications\Channels\DatabaseChannel as BaseDatabaseChannel;
use Illuminate\Notifications\Notification;
class DatabaseChannel extends BaseDatabaseChannel
{
    protected function buildPayload($notifiable, Notification $notification)
    {
        return [
            ‘id’ => $notification->id,
            ‘type’ => get_class($notification),
            ‘data’ => $this->getData($notifiable, $notification),
            ‘read_at’ => null,
            ‘resource_type’ => isset($notification->resource) ? get_class($notification->resource) : null,  // ←ここを追加しました
            ‘resource_id’ => isset($notification->resource) ? $notification->resource->getKey() : null,  // ←ここを追加しました
        ];
    }
}

2-3 Notificationを修正する

2-2で拡張したDatabaseChannelが使用されるように通知クラスを修正します。

via() で、拡張したDatabaseChannelを指定しています。

また、コンストラクタの引数で関連するリソースのインスタンスを引き渡し、クラス変数にセットしています。

関連リソースには例としてProjectモデルをセットしていますが、モデルクラスならなんでもOKです!

[修正前]

App\Notifications\ProjectCreated.php

namespace App\Notifications;
use App\Models\Project;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
class ProjectCreated extends Notification
{
    use Queueable;
    private $project;
    public function __construct(Project $project)
    {
        $this->project = $project;
    }
    public function via($notifiable)
    {
        return [‘database’];
    }
    public function toDatabase($notifiable)
    {
        return [
            ‘project_id’ => $this->project->id,
            ‘project_name’ => $this->project->name,
        ];
    }
}

[修正後]

App\Notifications\ProjectCreated.php

namespace App\Notifications;
use App\Models\Project;
use App\Notifications\Channels\DatabaseChannel;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
class ProjectCreated extends Notification
{
    use Queueable;
    public $resource;  // ←変更
    public function __construct(Project $project)
    {
        $this->resource = $project;  // ←変更
    }
    public function via($notifiable)
    {
        return [DatabaseChannel::class];  // ←変更
    }
    public function toDatabase($notifiable)
    {
        return [
            // ←削除
        ];
    }
}

2-4 通知処理を実行する

以上で関連リソースがDBに保存されるように修正することができましたので、通知処理を実行してみます。

以下のように通知処理を実行し、notificationsテーブルの「resource_type」に「\App\Models\Project」が、「resource_id」に「1」が登録されていればOKです!

    $user = User::find(1);
    $project = Project::find(1);
    $user->notify(new ProjectCreated($project));

3 通知レコードの取得時に関連リソースを紐付けて取得する

3-1 DatabaseNotificationモデルを拡張する

次に関連リソースを取得することができるようにDatabaseNotificationモデルを拡張したクラスを作成します。

resource()で、 関連リソースとのポリモーフィックリレーションを追加しています。

App\Models\DatabaseNotification.php

namespace App\Models;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Notifications\DatabaseNotification as BaseNotification;
class DatabaseNotification extends BaseNotification
{
    public function resource()
    {
        return $this->morphTo();
    }
}

3-2 Userモデルにnotificationsメソッドを追加する

3-1で拡張したDatabaseNotificationモデルが使われるように、Userモデルへnotificationsメソッドを追加します。

これは `Illuminate\Notifications\HasDatabaseNotifications::notifications()` をオーバーライドしています。

app/Models/User.php

    public function notifications()
    {
        return $this->morphMany(DatabaseNotification::class, ‘notifiable’)
            ->orderBy(‘created_at’, ‘desc’);
    }

3-3 通知を取得する

それでは通知レコードを取得してみます。

`load(‘resource’)` で、関連リソースを紐付けています。

通知レコードに、Projectデータが紐付いて取得できればOKです!

    $notifications = $user->notifications->load(‘resource’)->toArray();
    dump($notifications);

4 おわりに

最後までお読みいただきありがとうございます!

基底の通知クラスを拡張することによって、簡単に通知機能をカスタマイズすることができました!

この記事がどなたかの参考になれば幸いです。