人気のPHP WEBアプリケーションフレームワークLaravelのTipsを記録していきます

Laravel の 通知(Notification)を使用する

● Laravel の 通知(Notification)を使用する

1. モデルファイルに Notifiableトレイトを追加する

/app/User.php

class User extends Authenticatable
{
    use \Illuminate\Notifications\Notifiable;

(実はデフォルトでこのトレイトは記述されています。何もしなくても OK です)

2. Notification を作成する

php artisan make:notification <クラス名>

例 (掲示板にデータがポストされた通知「BoardsPosted」を作成してみます )

php artisan make:notification BoardsPosted

/app/Notifications/BoardsPosted.php が自動作成されます。

コンストラクタに通知に使用するデータ受け取りを記述します。 (後でここで受け取ったデータを加工して使用します。)

    private $board;
    public function __construct( $board )
    {
        $this->board = $board;
    }

3. 通知する

テスト的にメール送信で通知してみます

例としてログインするたびに自分自身にメールを送信するようにしてみます。

/app/Http/Controllers/HomeController.php

    public function index()
    {
        // ログインユーザー自身に通知する 
        $user = \Auth::user();
        $user->notify(new \App\Notifications\BoardsPosted($user));

        return view('home');
    }

なお、全ユーザーに通知するには次のようにします。

        // 全ユーザーに通知する 
        $users = \App\User::get();
        \Notification::send($users, new \App\Notifications\BoardsPosted(null));

.env に smtp の設定も記述します

MAIL_MAILER=smtp
MAIL_HOST=xxx.you-server.com
MAIL_PORT=587
MAIL_USERNAME=your@host.com
MAIL_PASSWORD=your-password
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=your@host.com

これだけでログインしてホーム画面に行くだけで自分自身にメールが送信されます。 とても簡単ですね

4. DBテーブルへ通知する

php artisan notifications:table
php artisan migrate

このようなテーブルが作成されます

5. 通知チャンネルに DatabaseChannel を追加する

(DatabaseChannelを追加します) app/Notifications/BoardsPosted.php

    public function via($notifiable)
    {
        return ['mail',\Illuminate\Notifications\Channels\DatabaseChannel::class];
    }

チャンネルはデフォルトでは3つ「BroadcastChannel」「DatabaseChannel」「MailChannel」があります。

DBに通知するときに好きなデータを保存できます。

    public function toArray($notifiable)
    {
        return [
            'invoice_id' => 111,
            'amount' => 123456,
        ];
    }

とすると、notificationsテーブルの「data」カラムに

{"invoice_id":111,"amount":123456}

のような json が保存されます。

notificationsテーブルは次のようなレイアウトです。

morphtoMany が設定されていますね。 これは

vendor/laravel/framework/src/Illuminate/Notifications/Notifiable.php

trait Notifiable
{
    use HasDatabaseNotifications, RoutesNotifications;
}

vendor/laravel/framework/src/Illuminate/Notifications/HasDatabaseNotifications.php

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

で設定されているようです。 全ての通知を notifications テーブルに集約する事ができます。 とてもとても簡単ですね。

参考 : https://laravel.com/docs/7.x/notifications#database-prerequisites

6. DBに保存された自分宛の未読の通知を表示する

// 自分宛の通知を表示する
$user = \Auth::user();
foreach ($user->unreadNotifications as $notification) {
    dump( $notification );
}

あるユーザー宛の全ての通知 と 未読の通知

$user = \Auth::user();
$notifications = $user->notifications;           // あるユーザー宛の全ての通知
$notifications = $user->unreadNotifications;     // あるユーザー宛の未読の通知

1件のデータに紐づいている全ての通知

$notifications = $model->notifications;           // 1件のデータに紐づいている全ての通知
$notification->markAsRead();    // 既読にする

未読の通知を既読にするには次のようにしてもOKです。

if ( $notification->read_at === null ){
	$notification->update(['read_at' => now()]);    // 既読日時をセット
}
else {
	dump( '既に既読です' );
}

bladeで通知数を表示する

{{ @Auth::user()->unreadNotifications->count() }} 件のお知らせがあります。

Blade だと次のように記述します。

<h3>{{ count($notifications) }}件の通知があります</h3>

@foreach ($notifications as $notifications)
    {{$notifications->id}} : {{$notifications->type}} <br>
@endforeach

7. notificationsテーブルを拡張する

引用 : https://bit.ly/3ewBWzT
にあるように、notificationsテーブルは拡張しておきましょう。

php artisan make:migration change_notifications_table_add_2columns  --table=notifications

マイグレーションファイルの編集 database/migrations/2020_xx_xx_xxxxx_change_notifications_table_add_2columns.php

    public function up()
    {
        Schema::table('notifications', function (Blueprint $table) {
            $table->unsignedBigInteger('resource_id')->after('type');
            $table->string('resource_type')->after('type');
        });
    }

    public function down()
    {
        Schema::table('notifications', function (Blueprint $table) {
            $table->dropColumn('resource_id');
            $table->dropColumn('resource_type');
        });
    }

app/Notifications/MyDatabaseChannel.php

DatabaseChannel を拡張してオリジナルの MyDatabaseChannel を作成します。

<?php 

namespace App\Notifications;

use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Channels\DatabaseChannel;

class MyDatabaseChannel extends DatabaseChannel
{
    protected function buildPayload($notifiable, Notification $notification)
    {
        return [
            'id' => $notification->id,
            'type' => get_class($notification),
            'data' => $this->getData($notifiable, $notification),
            'resource_type' => $notification->resource->getMorphClass() ?? null,
            'resource_id' => $notification->resource->getKey() ?? null,
            'read_at' => null,
        ];
    }    
}

app/Notifications/BoardsPosted.php も変更します。

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class BoardsPosted extends Notification
{
    use Queueable;

    public $resource;

    public function __construct( $resource )
    {
        $this->resource = $resource;
    }

    public function via($notifiable)
    {
        return [\App\Notifications\MyDatabaseChannel::class];
    }

    public function toMail($notifiable)
    {
        return (new MailMessage)
                    ->line('システムからのお知らせです。')
                    ->action('Notification Action', url('/'))
                    ->line('それではどうぞよろしくお願いいたします');
    }

    public function toArray($notifiable)
    {
        return [];
    }
    
}

● 通知対象のモデル側から、ある書き込み(対象モデル)をユーザーに対して通知したかどうかを調べる

データに対して
「unread_notifications」で未読の通知が、
「notifications」で全ての通知が
取得できます。

app/Notification.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Notification extends Model
{
    protected $table = 'notifications';

    protected $guarded = ['id', 'created_at', 'updated_at'];

    protected static function boot()
    {
        parent::boot();
    }
}

通知対象のモデル側 app/Board.php に「フック」と「リレーション」と「メソッド」を追加

    protected static function boot()
    {
        parent::boot();

        /**
         * ● フック : deleted() 削除時にモデルに紐づく全ての「通知」を削除する
         */
        static::deleted(function ($model) {
            foreach ($model->notifications as $notification) {
                $notification->delete();
            }
        });
    }

    /**
     * ● (Notification) 1対多 ポリモーフィックリレーション : ->unread_notifications で書き込みに紐づく「現在ログイン中ユーザーが未読の通知」を取得します。
     *
     * ソート順 : sort_no , DESC
     */
    public function unread_notifications()
    {
        return $this->morphMany('App\Notification', 'resource')->where('read_at','=',null)->where('notifiable_type','=','App\User')->where('notifiable_id','=',optional(\Auth::user())->id)->orderBy('created_at','ASC');
    }

    /**
     * ● (Notification) 1対多 ポリモーフィックリレーション : ->notifications で書き込みに紐づく全ての通知を取得します
     * ソート順 : sort_no , DESC
     */
    public function notifications()
    {
        return $this->morphMany('App\Notification', 'resource')->orderBy('created_at','ASC');
    }

    /**
     * ● メソッド : ->is_notified('App\User',1) id=1 のユーザーに対して通知を完了したかどうかを返します。
     */
    public function is_notified( $class_name, $id )
    {
        foreach ($this->notifications as $k => $v) {
            if ( $v->notifiable_type === $class_name && $v->notifiable_id === $id ){
                return true;
            }
        }        
        return false;
    }

これでコントローラーから次のようにして「ある書き込み」が「あるユーザーに対して」通知済みかどうかを知る事ができます。
またモデルのデータを削除したときにそれに紐づく全てのDB通知も削除されます。

        // id = 26 の board
        $board = \App\Board::find(26);
        if ( $board->is_notified('App\User',1) ){
            dump( '1 のユーザーには通知済み' );
        }
        if ( $board->is_notified('App\User',2) ){
            dump( '2 のユーザーには通知済み' );
        }

● [laravel]Notificationを使ってメール送るときにMailableを使う

https://bit.ly/2BIlFKc

添付ファイル1
添付ファイル2
No.1800
01/15 10:07

edit

添付ファイル