Laravel BroadcastingとPusherでリアルタイムUI更新を試す
記事について
Laravel の Broadcasting と Pusher でリアルタイムの UI 更新をためしてみた記事です 😺
何か改善点などあればご指摘ください 🙏
Laravel Broadcasting についての説明
In many modern web applications, WebSockets are used to implement realtime, live-updating user interfaces. When some data is updated on the server, a message is typically sent over a WebSocket connection to be handled by the client. This provides a more robust, efficient alternative to continually polling your application for changes.
最近の多くのウェブアプリケーションでは、リアルタイムでライブ更新されるユーザーインターフェイスを実装するために WebSocket が使用されています。サーバー上のデータが更新されると、通常、WebSocket 接続を介してメッセージが送信され、クライアントで処理されます。これは、アプリケーションの変更を継続的にポーリングするよりも、より堅牢で効率的な代替手段を提供します。
抜き出すと、↓ という感じでしょうか。
- リアルタイムに UI を更新するために WebSocket 接続によるメッセージの送信が利用されている
- クライアントが定期的にアプリケーションや DB を見にいくよりも効率的
- Laravel では WebSocket 接続を介してイベントをブロードキャスティングできる
Pusher の登録
https://pusher.com/ から登録します。
Laravel 側の設定
Laravel アプリケーションの作成
今回は Laravel Sail を用いて環境を構築します。
↓ を実行するだけ。
curl -s "https://laravel.build/realtime_laravel" | bash
Provider 有効化
イベントをブロードキャストするために、config/app.php
のBroadcastServiceProvider
の行のコメントアウトを外します。
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class, ←これ
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
])->toArray(),
Laravel に Pusher ライブラリをインストール
ブロードキャストドライバに Pusher を使用するため、Pusher ライブラリをインストールします。
composer require pusher/pusher-php-server
.env に Pusher の情報を記載する
Pusher に記載されている情報を.env
に追記します。
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=
また、ブロードキャストドライバもlog
→pusher
に変更しておきます。
BROADCAST_DRIVER=pusher
Event の作成
php artisan make:event MyEvent
でイベントクラスを作成します。
出来上がったapp/Events/MyEvent.php
を以下のように編集します。
<?php
namespace App\Events;
use Illuminate\Queue\SerializesModels;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class MyEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
public function __construct($message)
{
$this->message = $message;
}
public function broadcastOn()
{
return ['my-channel'];
}
public function broadcastAs()
{
return 'my-event';
}
}
イベントクラスではShouldBroadcast
インターフェースを実装し、broadcastOn
メソッドも定義する必要があります。
boradcastAs
では、ブロードキャストの名前をmy-event
という名前にしています。
デフォルトだと、ブロードキャスト名はクラス名になるようです。
クライアント側の設定
View の作成
今回はお試しなので、welcome.blade.php
を改造します。
<!DOCTYPE html>
<head>
<title>Pusher Test</title>
<script src="https://js.pusher.com/8.2.0/pusher.min.js"></script>
<script>
// Enable pusher logging - don't include this in production
Pusher.logToConsole = true;
var pusher = new Pusher("{{ $key }}", {
cluster: "{{ $cluster }}"
});
var channel = pusher.subscribe('my-channel');
channel.bind('my-event', function (data) {
console.log(JSON.stringify(data));
});
</script>
</head>
<body>
<h1>Pusher Test</h1>
<p>
Try publishing an event to channel <code>my-channel</code>
with event name <code>my-event</code>.
</p>
</body>
↓ だけのシンプルな画面です。
Hello Wolrd を送ってみるテスト
ルーティングを設定します。
今回は、http://localhost/test
にリクエストを送ったら、イベントがブロードキャストされるようにしています。
<?php
use Illuminate\Support\Facades\Route;
use \App\Events\MyEvent;
Route::get('/', function () {
return view('welcome', ['key' => env('PUSHER_APP_KEY'), 'cluster' => env('PUSHER_APP_CLUSTER')]);
});
Route::get('/test', function () {
event(new MyEvent('Hello World'));
});
↓
コンソールに「Hello World」が表示されました 😼
受け取ったものを画面に表示させる
ちょっと受け取ったものを画面に表示させてみましょう!
channel.bind('my-event', function (data) {
document.getElementById('message').innerHTML = data.message;
});
<body>
<h1>Pusher Test</h1>
<div id="message"></div>
</body>
表示されました!
DB が更新されたら UI も更新されるように
今度は、DB に変更があったときにリアルタイムに UI を更新してみます。
以下のようなテーブル・データを作成しました。
現在の在庫(stock_quantity
)を表示しておき、在庫数に変更があったら UI を更新するのが目標です 😺
モデルは ↓ です。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
use HasFactory;
}
Model の修正
Model の更新があったらブロードキャストするために、Illuminate\Database\Eloquent\BroadcastsEvents
トレイトのbroadcastOn()
を実装する必要があります。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
use HasFactory;
public function broadcastOn(string $event): array
{
return ['products'];
}
}
DB を更新する処理を追加
routes/web.php
のtest
を改造しました。
Product テーブルの id が 3 のレコードのstock_quantity
を+1 しています。
保存後、products
イベントを dispatch しています。
getEventDispatcher
は Model が利用しているHasEvents
トレイトで定義されているようです 🧐
Route::get('/test', function () {
$product = Product::find(3);
$product->increment('stock_quantity');
$product->save();
Product::getEventDispatcher()->dispatch('products');
});
クライアント側の修正
ひとまず、受け取った内容を console に出力してみます。
const channel = pusher.subscribe('products');
channel.bind('ProductUpdated', function (data) {
console.log("在庫" + data.model.stock_quantity);
});
ログに出力してみる
ID が 3 の店長の在庫数に+1 した値がログに表示されました!
(色々ためしていたらいつの間にかだいぶ増えてました)
受け取った値で UI を更新してみる
View を ↓ のような形にします。
<!DOCTYPE html>
<head>
<title>Pusher Test</title>
<script src="https://js.pusher.com/8.2.0/pusher.min.js"></script>
<script>
// Enable pusher logging - don't include this in production
Pusher.logToConsole = true;
const pusher = new Pusher("{{ $key }}", {
cluster: "{{ $cluster }}"
});
const channel = pusher.subscribe('products');
channel.bind('ProductUpdated', function (data) {
const productId = data.model.id;
const stockQuantity = data.model.stock_quantity;
document.getElementById(`stock_quantity_${productId}`).innerText = stockQuantity;
});
</script>
</head>
<body>
<h1>Pusher Test</h1>
<table>
<thead>
<tr>
<th>商品名</th>
<th>在庫数</th>
</tr>
</thead>
<tbody>
@foreach ($products as $product)
<tr>
<td>{{$product->product_name}}</td>
<td id="stock_quantity_{{ $product->id }}">{{$product->stock_quantity}}</td>
</tr>
@endforeach
</tbody>
</table>
</body>
店長の数が 109→110 に更新されました!
おわり
今回は、Laravel のブロードキャスト・Pusher を使用してリアルタイムの UI 更新をためしてみました。
まだまだ理解できていない部分も多いですが、↓ という認識です 🧐
- クライアント:Pusher ライブラリを使用して
products
チャンネルを購読、productUpdated
イベントがブロードキャストされると、指定した処理を実行する(今回だと受け取った在庫数で更新する) - Laravel:イベント(
productUpdated
)を発火させて、WebSocket サーバー(Pusher)にブロードキャストを依頼する - Pusher:Laravel から依頼があればブロードキャストして、チャンネルを購読しているクライアントにデータを送る
ドキュメントを読んでいると、認証が必要なプライベートチャンネルや、Pusher 以外のブロードキャストドライバ(Ably や Laravel WebSocket など)もあるようなので、色々ためしてみるのもいいかもしれません…!
Discussion