Laravel Broadcast入門: リアルタイム通信

Laravel

はじめに

「Laravelでのリアルタイムな通信方法に興味がある」

「LaravelのBroadcastについて、具体的な実装方法が知りたい」

本記事は、このようなニーズに応える記事になります。

LaravelのBroadcastという技術を使うことで、サーバー側とクライアント側でのデータのやり取りをリアルタイムに通信することができるようになります。

この技術は、主にリアルタイム性の求められるチャットサービスや通知などの機能で活躍することが多いです。

本記事では、Broadcastの概要や、Broadcastの基本的な使い方をサンプルコード付きで説明します。

✔️ 本記事を読むメリット
・Broadcastで使われる技術の概要を把握できる
・Broadcastの基本的な使い方がわかる
・Broadcastを使えるようになる

✔️ 筆者の経験
・プログラミング歴3年の現役Webエンジニア
・複数の自社サービスの立ち上げを経験

Broadcast概要

ブロードキャストはイメージとしてはテレビのようなものです。

チャンネルを作成して、チャンネルを介してイベントを放送することで、サーバー側とクライアント側でリアルタイムでの通信を可能とする技術です

実際には、WebSocket接続を介してサーバー側のLaravelイベントをブロードキャスト(放送)しており、JavaScriptはそのイベントを受信して表示します。

リアルタイムでの通信というのは、ページ更新をする必要がなく、サーバー・クライアント間でデータのやり取りを行うことができるということです。

大まかに、実装方法としては以下のような流れになります。

  1. Laravelでイベントクラスを作成
  2. チャンネルの認証設定
  3. チャンネルの認可設定
  4. イベント発行
  5. JavaScriptでイベントをリッスン

それでは、次に具体的な実装方法について見ていきましょう。

基本的な実装手順

下準備

まずは、必要なパッケージやモジュールなどをインストールする必要があります。
Laravel側ではPusherチャンネル、JavaScript側ではLaravel Echoが必要となります。
もちろん、選択肢はこれだけではなく他にもありますが、今回はデフォルトでサポートされているこの2つを使用します。

Pusherアカウントをお持ちでない方は、先にアカウントを作成しておいてください。

Pusher | Leader In Realtime Technologies
Simple, scalable and reliable. Hosted realtime APIs loved by developers and trusted by giants. Build live dashboards, no...

まずは、Pusherチャンネルをインストールします。

composer require pusher/pusher-php-server

次に、「config/broadcasting.php」の設定ファイルにPusherチャンネルの情報を設定していきます。

PUSHER_APP_ID=your-pusher-app-id
PUSHER_APP_KEY=your-pusher-key
PUSHER_APP_SECRET=your-pusher-secret
PUSHER_APP_CLUSTER=mt1

次に、.envのブロードキャストドライバーをpusherに変更します。

BROADCAST_DRIVER=pusher

これでサーバー側は設定完了です。

次に、クライアント側にLaravel EchoとPusher jsモジュールをインストールします。

npm install --save-dev laravel-echo pusher-js
npm run dev

これでクライアント側も設定完了です。

イベントクラス作成

イベントはチャンネルを介して、ブロードキャストされますが、チャンネルには以下の2種類があります。

  1. パブリックチャンネル・・・全ユーザーが購読できるチャンネル
  2. プライベートチャンネル・・・限定されたユーザーだけが購読できるチャンネル

後ほど説明しますが、プライベートチャンネルでユーザーを限定する方法は、Laravelの認証と認可を使って限定します。
これを行うことによって、認証と認可を通過したユーザーのみが購読できるようになります。

それでは、まずはイベントクラスを作成してみます。

php artisan make:event BroadcastEvent

それでは、それぞれのチャンネルでのイベントクラスを見てみましょう。

まずは、パブリックチャンネル。

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class BroadcastEvent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $message
    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct($message)
    {
        $this->message = $message;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new Channel('notification-channel, $this->message);
    }
}

次に、プライベートチャンネルです。

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Database\Eloquent\Model;

// Models
use App\Models\User;

class BroadcastEvent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $model;
    public $user;
    /**
     * Create a new event instance.
     *
     * @return void
     */
    // 第一引数は通知対象となるユーザー
    public function __construct(User $user, Model $model)
    {
        $this->user = $user;
        $this->model = $model;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('notification-channel.' . $this->user->id, $this->model);
    }
}

簡単に説明すると、イベントクラスのbroadcastOn()メソッドの中で、チャンネルを介してブロードキャストを行なっています。
パブリックチャンネルの場合はChannelクラス、プライベートチャンネルの場合はPrivateChannnelクラスのインスタンスを作成し、返しています。
どちらのクラスもイベントクラスを作成した段階でuseされているはずですので、介したいチャンネルによってクラスを切り替えるだけですね。

プライベートチャンネルの例では、イベントクラスのインスタンスの第一引数に通知対象のユーザー、第二引数にはブロードキャストしたいデータを渡しています。

また、パブリックチャンネルの場合は全ユーザーにブロードキャストされるので、あとはJavaScript側でイベントをリッスンするだけです。
プライベートチャンネルの方は、どんなユーザーに限定してブロードキャストするのかを決めなければいけませんね。

本記事では、プライベートチャンネルでの実装を説明していきます。

そのため、次に認証と認可の設定を行なっていきます。

認証の設定

まずは、config/app.phpのBroadcastServiceProviderをコメントアウトしておきます。

// config/app.php

'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class, // コメントアウト
        Illuminate\Bus\BusServiceProvider::class,
        Illuminate\Cache\CacheServiceProvider::class,

次に、BroadcastServiceProviderで認証ルートを設定していきます。

// Providers/BroadcastServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;

class BroadcastServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Broadcast::routes(['middleware' => 'auth:api']);

        require base_path('routes/channels.php');
    }
}

アプリのミドルウェアで設定している認証を使って、ブロードキャストの認証を行います。

認可の設定

次に、認可を設定して購読できるユーザーを限定します。

以下はチャンネル名に含まれるユーザーIDと受信するユーザーのIDが一致しているか、userはauthチェックを通っているかの2つを確認しています。

// routes/channels.php

<?php

/*
|--------------------------------------------------------------------------
| Broadcast Channels
|--------------------------------------------------------------------------
|
| Here you may register all of the event broadcasting channels that your
| application supports. The given channel authorization callbacks are
| used to check if an authenticated user can listen to the channel.
|
*/

Broadcast::channel('notification-channel.{id}', function ($user, $id) {
    return ((int) $user->id === (int) $id) && auth()->check();
});

イベント発行

イベントを発行したい場所でイベント発行の処理を書きます。

// app/Services/ExternalApi/ReceptionOnPage.php

use App\Events\BroadcastEvent;

foreach ($users as $user) {
      if (isset($user)) {
                       // 第一引数は通知対象のユーザー
           event(new BroadcastEvent($user, $tasks));
       }
 }

イベントをリッスン

最後に、Laravel Echoモジュールを入れてイベントをリッスンします。

JavaScript側では、Laravel Echoの初期化を行う必要があります。

プライベートチャンネルの場合は、authEndpointを設定します。
デフォルトの認証ルートは「/Broadcasting/auth」になっており、このルートにアクセスすると、「Providers/BroadcastServiceProvider.php」で定義したauth middlewareでauthを確認しにいきます。

この際、トークンベースの認証方式を採用しているような場合は、ヘッダーにAPI用のトークンをつけないとアクセスできないので注意です。

以下のサンプルコードでは、フロントエンドにVueを採用した場合のコードになっています。

// Laravel Echoを初期化
window.Pusher = require("pusher-js");
window.Echo = new Echo({
   broadcaster: "pusher",
   key: process.env.pusher.key,
   cluster: process.env.pusher.cluster,
   encrypted: true,
   authEndpoint: process.env.pusher.authUrl,
   auth: {
     headers: {
       authorization: this.getCookieValue("auth._token.local1"),
     },
    },
  });

const userId = this.$store.state.auth.user.id;

// ブロードキャスト通知
 window.Echo.private(`notification-channel.${userId}`).listen(
   "BroadcastEvent",
    (data) => {
      console.log(data);
      this.broadcastNotifications.push(data.model);
    }
 );

これでリッスンができるようになります。

実際に、イベントを発行してみてフロント側でページ更新なくリッスンできるか試してみてください。

最後に

いかがでしたでしょうか?

通常、Laravelを用いた開発ではサーバー側とクライアント側で同期的にデータをやり取りしているので、サーバーからレスポンスが返ってくるまでにラグがあったりなどします。

ただ、今回のBroadcastという技術を使うことでリアルタイムにデータのやり取りを行うことができるようになります。

これは非常に面白い技術で、いろいろな用途で利用できるかと思いますので、ぜひお試しください!

ではでは😆

コメント

タイトルとURLをコピーしました