さくらVPS(Rocky Linux) + laravel + Inetria(vue3)でbroadcastの実装メモ

## laravel broadcastの使いどころ

一般的なクライアントサーバの仕組みは
クライアントからの要求に対してサーバが応答を返す流れとなる。

サーバ側からクライアント側への逆方向のイベントがないので
サーバ内で発生した変更を知るにはクライントがサーバに対して
定期ポーリングするなどの仕組みが必要だった。

laravelフレームワークではbroadcastという仕組みが用意されており。
websocketサーバを介してサーバからのリアルタイムの変更通知を受信することができる。

前提となる知識としては
backend(laravel)側はLaravelのイベント、リスナー、キューワーカーの知識
frontend(jsvascript)ではWebsocketの知識が前提となる。

Websocketサーバによる外部サービスとしてPusher、ablyなどの無料プランが使えるが
laravel-echoやlaravel-websocketsなどのlaravelフレームワークが用意した
websocketサーバを自前で用意してもよい。その場合はwebscoketサーバの構築の知識が必要になる。

### まずは外部サービスpusherjsで実装してみる

pusherjsにてアカウント作成してAPP_KEYを発行する
```bash
app_id = "XXXXXX"
key = "yyyyyyyyyyyyyyyyyyyyyy"
secret = "zzzzzzzzzzzzzzzzzz"
cluster = "ap3" // pusherサーバのtimezoneらしい
```
この値をコピーして.envファイルのPUSHER関連の値を設定する

```bash
# BROADCAST_DRIVER=log
BROADCAST_DRIVER=pusherjs
(略)
PUSHER_APP_ID=XXXXXX
PUSHER_APP_KEY=yyyyyyyyyyyyyyyyyyyyyy
PUSHER_APP_SECRET=zzzzzzzzzzzzzzzzzz
PUSHER_APP_CLUSTER=ap3
```
## ※以下、websocketサーバ(socketi,laravel-websockets)を自前で用意する場合
[WSサーバ構築](https://qiita.com/morohoshi/items/92358a4211838eded915)

### laravel(backendとfront)側の設定
laravel(backend)からfrontend(javascript)への橋渡しのライブラリをインストールする。
```bash
$ composer require inertiajs/inertia-laravel
```
front側にinertiaとvueライブラリをインストールする。
```bash
$ npm install @inertiajs/inertia @inertiajs/inertia-vue3
```

front側の大本のファイルは唯一のbladeファイルである`resources/views/app.blade.php`となる。
vueファイルの大本は`resources/js/app.js`で
各画面のvueコンポーネントは`resources/js/Pages/`配下にvueファイルを格納する構成となる

一覧画面とチャット送信のアクションだけのシンプルなIFのみとする。
一覧画面のURLは`/chat`。メッセージの一覧を取得し、画面側に表示する。
送信ボタンでメッセージを登録する。
ここでイベントを使い、同じ画面を見ている別の端末に通知を受信するようにする。

ルーティングは以下の２つ。
```php:routes\web.php
Route::get('/chat', 'ChatController@index');
Route::post('/chat', 'ChatController@store');
```
コントローラでInetriaライブラリつかってページにルーティングする。
```php:app\Http\Controller\ChatController.php
(略)
class ChatController extends Controller
{
    public function index()
    {
        return Inertia::render('Chat/Index', [
           'messages' => Message::all([
               'body', 'created_at'
           ]);
        ]);
    }
    public function store(Request $request)
    {
        $message = Message::create([
            'body' => $request->message,
        ]);
        /* 通知用のイベントをキューに積む */
        event(new MessagePostAppended($message));
        return redirect()->route('chat.index');
    }
}
(略)
```

MessagePostAppendedがないのでイベントを作成
```bash
$ php artisan make:event MessagePostAppended
```
broadcast機能を使うので`ShouldBroadcast`を継承させる。
```php:app\Events\MessagePostAppended
(略)
class MessagePostAppended implements ShouldBroadcast
{
    public $message;
    public function __construct(Message $message)
    {
        $this->message = $message;
    }
    public function broadcastOn()
    {
        return new Channel('my-channel');
    }
    public function broadcastAs()
    {
        return 'message.appended';
    }
}
```
`broadcastOn`が初期値だとPrivateChannelになっているのでChannelクラスに変更。
`broadcastAs`でイベント名を任意に設定できる。ない場合はクラス名`MessagePostAppended`
となる。

この`my-channel`と`message.appended`(`MessagePostAppended`)をfront側で使用する。

front側のjsパッケージをインストールする。
```bash
$ npm install --save laravel-echo pusher-js
```
`pusher-js`はpusherjsが提供しているwebsocketライブラリ、
`laravel-echo`はlaravelが提供しているWebSocketライブラリで
自前でwebsocket(laravel-echo-serverとかsocket-io)を構築する場合はこちらを利用する。
broadcastの指定でpusherjsもサポートしています。要`pusher-js`。

bootsrrap.jsでlaravel-echoインスタンスを生成する。
pusherライブラリもグローバルの`Pusher`でロードし使えるようにしておく。
```typescript:resources\js\bootstrap.js
(略)
import Echo from 'laravel-echo';

window.Pusher = require('pusher-js');

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    forceTLS: true,
});
```
front側は`resources\js\Pages\Chat\Index.vue`を作成
broadcastのメッセージを受信したらreloadするようにする
`listen`メソッドのイベントの名称は
クラス名の場合はそのままクラス名だが`broadcastAs`で別名を指定した場合は
先頭にピリオドをつけて`.message.appended`で待ち受ける。

```vue:resources\js\Pages\Chat\Index.vue
<script setup>
import { ref } from 'vue';
import BreezeAuthenticatedLayout from '@/Layouts/Authenticated.vue';
import BreezeButton from '@/Components/Button.vue';
import { Inertia } from '@inertiajs/inertia';
import { Head, Link, useForm } from '@inertiajs/inertia-vue3';

defineProps({
    messages: Array,
});

const submit = () => {
    form.post(route('chat.store'), {
        onFinish: () => {
          form.reset();
          form.message = '';          
        },
    });
};

Echo.channel('my-channel')
.listen('.message.appended', (e) => {
  console.log(e);
  Inertia.reload();
});
</script>
<template>
  <h1>タイトル</h1>
  <div v-for="msg in messages">
    <span v-text="msg.created_at"></span>：&nbsp;
    <span v-text="msg.body"></span>
  </div>
  <form @submit.prevent="submit">
  　 <textarea v-model="form.message"></textarea><br>
     <BreezeButton class="ml-4" :disabled="form.processing">送信</BreezeButton>
  </form>
</template>
```
ここまででWebscoketを外部サービスであるPusherで実装した。
次はこちらをベースに代替フリーウェアの`laravel-websockets`を使用して
ブロードキャスト機能を実装する。