Laravel で NotificationからBroadcastする際にEvent名が設定できない件
結論
- larave-echoでchannel.listenではなくchannel.notificationを使用する。 or
- vendorオーバーライドしてbroadcastAsを実装する。
問題
環境
PHP 8.0.2
Laravel 9.27
NotificationでBroadcastするとイベント名がIlluminate\Notifications\Events\BroadcastNotificationCreatedになる。
本当はこうしたい。
コードを読んだ
LaravelではBroadcast(push通知)を利用する方法にEventで行う方法とNotificationからviaにbroadcastを指定する方法が用意されています。
Event: ShouldBroadcastを利用する。
Notification: Notificationのviaでbroadcastを指定する。
Eventから送る場合はbroadcastAs()を、ShouldBroadcastを継承したクラスで実装することでイベント名の設定が可能になります。
$name = method_exists($this->event, 'broadcastAs')
? $this->event->broadcastAs() : get_class($this->event);
一方でNotificationから送る場合、broadcastAsが実装されていません。
src/Illuminate/Notifications/Events/BroadcastNotificationCreated.php
このBroadcastNotificationCreatedは(恐らく)この後BroadcastEventを通ってBroadcastされますが、broadcastAsが実装されていないためget_class($this->event) が実行されて、
'.Illuminate\Notifications\Events\BroadcastNotificationCreated'
がイベント名にセットされます。
Laravelの更新をチェックした。
issueは上がっていたが、closeされてしまっている。
4年かかって挙げ句Closeなので修正は望み薄。該当部分の履歴を確認した。
5年前にmethod renameというコミットでbroadcastAsがなくなって、broadcastTypeが実装されているようにみえる。
該当箇所
broadcastTypeをnotificationに実装してみたが、呼ばれていないのかイベント名はセットできない。
Clientのライブラリが対応している…?
laravel-echoのライブラリ内にこれに対応したような記述がある。
/**
* Listen for an event on the channel instance.
*/
notification(callback: Function): Channel {
return this.listen('.Illuminate\\Notifications\\Events\\BroadcastNotificationCreated', callback);
}
Oh...
NotificationはNotificationというイベントだよ、ということなのかもしれない。
まとめ
対策としては冒頭の2つしか思いつかなかった。
channel.listenの代わりにchannel.notificationで購読するのが手っ取り早い対応かもしれないが、イベント名に相当する機能を含めたい場合はレスポンスデータ内に含める必要があり、callback内で処理を分岐させる必要がでてくるし、不必要なイベントまで受信することになる。
オーバーライドする場合
"autoload": {
"psr-4": {
"App\\": "app/",
(略)
},
"exclude-from-classmap": [
"vendor\\laravel\\framework\\src\\Illuminate\\Notifications\\Events\\BroadcastNotificationCreated.php"
],
"files": [
"app\\VendorOverrides\\laravel\\framework\\src\\Illuminate\\Notifications\\Events\\BroadcastNotificationCreated.php"
]
},
該当のファイルを編集する方法。Laravelのアップデートをする際には注意が必要。
以下を追記する。
public function broadcastAs()
{
if (method_exists($this->notification, 'broadcastAs')) {
return $this->notification->broadcastAs();
}
return static::class;
}
最後に
制約がないのであればEventで送るのが楽です。
Discussion