/app/User.php
class User extends Authenticatable
{
use \Illuminate\Notifications\Notifiable;
(実はデフォルトでこのトレイトは記述されています。何もしなくても OK です)
php artisan make:notification <クラス名>
例 (掲示板にデータがポストされた通知「BoardsPosted」を作成してみます )
php artisan make:notification BoardsPosted
/app/Notifications/BoardsPosted.php が自動作成されます。
コンストラクタに通知に使用するデータ受け取りを記述します。 (後でここで受け取ったデータを加工して使用します。)
private $board;
public function __construct( $board )
{
$this->board = $board;
}
テスト的にメール送信で通知してみます
例としてログインするたびに自分自身にメールを送信するようにしてみます。
/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
これだけでログインしてホーム画面に行くだけで自分自身にメールが送信されます。 とても簡単ですね
php artisan notifications:table
php artisan migrate
このようなテーブルが作成されます
(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
// 自分宛の通知を表示する
$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
引用 : 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 のユーザーには通知済み' );
}