前回に引き続き、LaravelのBroadcast機能について紹介していきたいと思います。

作ったもの

自分がサイトを閲覧している最中に誰かが別の場所(端末・ブラウザ)で自分のアカウントを使ってログインしたら、そのIPアドレスを通知する機能を作ってみました。
(字幕付きの動画です。字幕が非表示の場合はお手数ですが手動でONにしてみてください。)

実装

今回実装したコードはGithubで公開しています。
プレーンなLaravel5.5との差分で、大体の内容が掴んでいただけるかと思います。

1. ユーザのログイン完了イベントに対するイベントリスナを設定する

(コミットをみる)

  • App\Listener\LoginSuccessを作成し、’Illuminate\Auth\Events\Login’をListenするようにしました。

2. Broadcastイベントを定義する

(コミットをみる)

今回のメインその1です。

app/Events/BroadcastUserLogined.php.php

  • まずはShouldBroadcastインターフェイスを実装したイベントクラスを作成します(コードを見る
    • broadcastOnメソッドでクライアントから接続するための「チャンネル」をリターンします。今回は接続に認証が必要な「プライベートチャンネル」として定義しました。
    • broadcastWithメソッドでは、クライアントに通知するデータの定義を行っています。今回は、ログインしたクライアントのIPアドレスを通知します。

routes/channels.php

  • 次に、認証のための設定を./routes/channel.phpで行います(コードを見る
    • 第1引数は認証するチャンネル名です。EventのbroadcastOnで設定したのと同じ名前を設定します。
    • 第2引数はクロージャで、認証結果をBoolで返します。今回は、ログイン中のユーザ―IDと、チャンネル名に含まれる数字が一致していることを条件としました。

config/app.php

  • 最後に、標準で無効化されているサービスプロバイダを有効にしました

3. ユーザのログイン完了イベントListener内で、Broadcastする

(コミットをみる)

  • 1の実装手順で作成したListenerから、イベントのBroadcastを行います。
    • broadcastヘルパ関数に2で作成したイベントのインスタンスを渡すと、ブロードキャストが行われます
    • ブロードキャストを行うと、既にそのチャンネルに接続しているクライアントに通知が行われま
    • ソケットのIDを使って自分だけ除外するなどの調整もできるようでした。

クライアント側の実装

(コミットをみる)

今回のメインその2です。

resources/assets/js/bootstrap.js

  • laravel-echo のインスタンスを初期化して、グローバル変数としました
    • このままだとwindow.ioが未定義のエラーが発生するためsocket.ioをグローバル変数としました
    • ドキュメントによると、socket.ioを別途scriptタグで読み込めばよいはずなのですが、どうもうまくいかなかったのでこうしています。

resources/assets/js/components/MessageComponent.vue

  • window.Echoでプライベートチャンネルに接続し、イベントをListenしています
    • listenするイベント名は、ShouldBroadcastインタフェースを継承して作成したEventのクラス名です
    • Eventのクラスに別名を返すbroadcastAsメソッドを実装すると、イベント名を変更することもできるようです。
    • チャンネル名にユーザのIDが含まれるため、コンポーネントのプロパティとして受け取ることにしました

resources/assets/js/app.js

  • コンポーネントをVueに登録します。

resources/views/layouts/app.blade.php

  • コンポーネントを読み込んでいます。

まとめ

いかがだったでしょうか。
Websocketというとサーバ側の実装が面倒なイメージがあるのですが、
laravel echo server のおかげで、そんなにたくさんのコードを書くことなく機能が実装できました。
最後に、私が一番ハマった点を共有して、記事を終わらせていただきたいと思います。それでは!

ドライバー設定の確認

正しく設定ができているはずなのに、一向にイベントが発火しない、、
そんなときは[.env]設定ファイルの設定値を確認してください。
[BROADCAST_DRIVER]の値が[log] (標準のまま)になっている可能性があります。
その場合、ログファイルに下記のように書きだされるだけで、ブラウザにイベントが通知されることはありません。
今回の記事のようにlaravel-echo-serverをredisとつなぐ場合は、設定値を[redis]にする必要があります。

local.INFO: Broadcasting [App\Events\BroadcastUserLogined] on channels [private-user.logined.2] with payload:
{
    "client_ip": "192.168.33.1",
    "socket": null
}

私はこの設定を見落としており、小一時間ハマりました。
設定サンプル