Laravel 12 双方向通信

Laravel12でWebソケットでのチャットアプリを作成した際の導入方法をメモしました。

### 1.ブロードキャストとreverbインストール
全てをYESとして必要なnpmライブラリーなどもインストールする
```
 php artisan install:broadcasting
```

### 2. pcntlをインストール
Dockerfileまたはコマンドでpcntlインストールする(今回はDockerfile)
```
&& docker-php-ext-install pdo pdo_mysql zip bcmath pcntl
```

### 3. 6001ポートを外部開放する
今回はdocker-compose.ymlのlaravelのコンテナにポートを追加します。
```
 ports:
      - 6001:6001
```

### 4. 現時点でreverbサーバー(Webソケット専用サーバー)の起動を確認する
エラーが起きていないことを確認する
```
 php artisan reverb:start --debug 
 ```


 # 実装
 双方向通信ができることを確認する
 ### 5. bladeファイルを作成 views/chat.blade.php
 ```
 <!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Simple Chat</title>
    <!-- ViteやMixを使っている場合は以下でapp.jsを読み込む -->
    @vite(['resources/js/app.js'])
</head>
<body>
    <h1>Chat Page</h1>

    <!-- メッセージ一覧表示 -->
    <ul id="messages"></ul>

    <!-- メッセージ送信フォーム -->
    <form id="chat-form">
        @csrf
        <input type="text" id="message-input" placeholder="Enter a message...">
        <button type="submit">Send</button>
    </form>

    <script>
        // ページが読み込まれた後に処理
        document.addEventListener('DOMContentLoaded', function() {

            // 1) Echo でメッセージを受信するリスナをセット
            //    ※ bootstrap.js で Echo をインポート済みなので window.Echo が使える
            window.Echo.channel('chat')
                .listen('MessageEvent', (event) => {
                    // event.message を画面に表示
                    const messagesList = document.getElementById('messages');
                    const li = document.createElement('li');
                    li.textContent = event.message;
                    messagesList.appendChild(li);
                });

            // 2) 送信フォームのsubmitハンドラ (POSTでメッセージを送る)
            const chatForm = document.getElementById('chat-form');
            const messageInput = document.getElementById('message-input');

            chatForm.addEventListener('submit', function(e) {
                e.preventDefault();

                // axios は resources/js/bootstrap.js ですでに window.axios として読み込まれている前提
                window.axios.post("{{ route('chat.send') }}", {
                    message: messageInput.value
                })
                .then(response => {
                    // 送信成功 → 入力欄をクリア
                    messageInput.value = '';
                })
                .catch(error => {
                    console.error(error);
                });
            });
        });
    </script>
</body>
</html>

 ```

 ### 6. イベントクラスを作成

 コマンド実行
 ```
 php artisan make:event MessageEvent
 ```

 実装
 ```
 <?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 MessageEvent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $message; // ←ここを追加
    /**
     * Create a new event instance.
     */
    public function __construct($message)
    {
        $this->message = $message; // ←ここを追加
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return array<int, \Illuminate\Broadcasting\Channel>
     */
    public function broadcastOn(): array
    {
        return [
            new Channel('chat'),
        ];
    }
}

 ```

 ### 7. ルーティングに追加
 web.php

 ```
 <?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 MessageEvent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $message; // ←ここを追加
    /**
     * Create a new event instance.
     */
    public function __construct($message)
    {
        $this->message = $message; // ←ここを追加
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return array<int, \Illuminate\Broadcasting\Channel>
     */
    public function broadcastOn(): array
    {
        return [
            new Channel('chat'),
        ];
    }
}

 ```


 ### 8. 実際にブラウザを2つ立ち上げて、双方向通信ができているか確認
 ( リロードなしで両方ともにメッセージが反映されていること)