Laravel Reverb リアルタイム通信の新機能

laravel
この記事は約19分で読めます。
記事内に広告が含まれます。

Laravel Reverb(ララベル リバーブ)は、2024年3月にリリースされたLaravel 11に追加された新機能でLaravelアプリケーションに超高速でスケーラブルなリアルタイムWebSocket通信を提供します。Laravel Reverbを利用するとリアルタイムチャットなどの機能を実装できます。
今回作るチャットは簡易な物ですが、とりあえずReverbを使ってみたいという方に向けての記事です。基本的な動作を確認できるサンプルにしています。

リアルタイムチャットのイメージは以下のようなものです。
2つ以上のブラウザで、いずれかのブラウザでメッセージを送信すると、他のブラウザにもメッセージが表示されます。メッセージの表示はアルタイムで更新されるためLineのようなチャット機能を作るようなイメージです。

では作成開始!!

まずはプロジェクトの準備と必要なモジュールのインストールを行います。
laravel-sailを使って基本的なLaravel機能をサクッとインストールしています。
この手順では事前にDockerの導入が完了しているものとします。またこちらのコードはWindows+Docker環境で動作を確認していますがMacでも同じ手順で動作すると思います。
※laravel-sailを使わない場合は適宜正しいコマンドに置き換えてください。ほとんど問題なく動作すると思います。

スポンサーリンク

「laravel-chat」プロジェクトを作成

curlでLaravel-sailを導入します。※数分かかります。

$ curl -s https://laravel.build/laravel-chat?with=mysql,redis | bash
....
[sudo] password for [ユーザー名]: パスワード
Thank you! We hope you build something incredible. Dive in with: cd laravel-chat && ./vendor/bin/sail up
$ 

# プロジェクトのインストールが終わったらプロジェクトへ移動します
$ cd laravel-chat

Reverbで利用するポートの解放

Dockerで環境を作成する場合はdocker-compose.ymlファイルにReverbで利用する8080ポートを開放する必要があります。
service/laravel.test/portsに8080ポート記述を追加してください。

services:
    laravel.test:
        build:
            context: ./vendor/laravel/sail/runtimes/8.3
            dockerfile: Dockerfile
            args:
                WWWGROUP: '${WWWGROUP}'
        image: sail-8.3/app
        extra_hosts:
            - 'host.docker.internal:host-gateway'
        ports:
            - '${APP_PORT:-80}:80'
            - '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
            - '8080:8080'                                        ←この行を追加 Reverb用ポート
        environment:
            WWWUSER: '${WWWUSER}'
            LARAVEL_SAIL: 1
            XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
            XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
            IGNITION_LOCAL_SITES_PATH: '${PWD}'
        volumes:
            - '.:/var/www/html'
        networks:
            - sail
        depends_on:
            - mysql
            - redis
.
. 続く...
.

コンテナの起動

laravel-sailの起動コマンド

$ sail up -d

Laravel環境設定

.envファイルの以下のエレメントを編集します

APP_TIMEZONE=Asia/Tokyo
APP_LOCALE=jp

nodeのインストール

※数分かかります

$ sail npm install

npmコマンドでsassを導入します

$ sail npm install -D sass

Laravel Broadcastingのインストール

インストール中にLaravel Reverbをインストールするか聞かれるのでYesを選びます。
再度ブロードキャストに必要なノードの依存関係をインストールするか尋ねられるのでYesを選びます。

$ sail artisan install:broadcasting

   INFO  Published 'broadcasting' configuration file.

   INFO  Published 'channels' route file.

 ┌ Would you like to install Laravel Reverb? ───────────────────────────────────────────┐
 │ Yes                                                                                  │
 └──────────────────────────────────────────────────────────────────────────────────────┘
.
.
.
 ┌ Would you like to install and build the Node dependencies required for broadcasting? ┐
 │ Yes                                                                                  │
 └──────────────────────────────────────────────────────────────────────────────────────┘
.
.
.
   INFO  Node dependencies installed successfully.

Databaseの作成

メッセージを格納するためのデータベーステーブルを作成します。

$  sail artisan make:migration create_message_table

   INFO  Migration [database/migrations/2024_04_01_000000_create_message_table.php] created successfully.

migrationファイルを編集

ファイル名に作成日時が含まれますので正しいファイルに読み替えてください。

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('messages', function (Blueprint $table) {
            $table->id();
            $table->string('message',255);  // メッセージ格納用項目
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('messages');
    }
};

マイグレーションの実行

$ sail artisan migrate

  INFO  Running migrations.
  2024_04_01_000000_create_message_table  .............................. 14.80ms DONE
$

モデルの作成

$ sail artisan make:model Message

   INFO  Model [app/Models/Message.php] created successfully.
$

デザイン確認のためtinkerでテスト用メッセージ1件登録します。

直接SQLでDatabaseに登録しても構いません。やりやすい方法でどうぞ。

$ sail tinker
Psy Shell v0.12.3 (PHP 8.3.4 — cli) by Justin Hileman
> $message = new \App\Models\Message();
> $message->message = 'Test Message';
> $message->save();
> $message = \App\Models\Message::find(1);
= App\Models\Message {#5110
    id: 1,
    message: "Test Message",
    created_at: "2024-04-14 19:24:51",
    updated_at: "2024-04-14 19:24:51",
  }
   INFO  Ctrl+D.

チャット用Bladeを作成

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>簡易チャット</title>
        @vite(['resources/js/app.js','resources/scss/chat.scss'])
    </head>
    <body>
        <div>
            <input type="text" id="message" name="message" placeholder="メッセージを書く">
            <button id="send-button">送信</button>
        </div>
        <ul id="message-list">
            @foreach ($messages as $message)
                <li class="message">{{ $message->message }}</li>
            @endforeach
        </ul>
    </body>
</html>

チャット用スタイルシートを作成

SCSS用ディレクトリ作成

$ mkdir resources/scss
#message {
  width: 500px;
  padding: 10px;
  margin: 10px 0;
  border: 1px solid #ccc;
  border-radius: 5px;
  font-size: 1em;
}

#send-button {
  padding: 10px;
  margin: 10px 0;
  border: 1px solid #ccc;
  border-radius: 5px;
  font-size: 1em;
  background-color: #f0f0f0;
  cursor: pointer;
}

#send-button:hover {
  background-color: #e0e0e0;
}

#message-list {
  list-style-type: none;
  padding: 0;
  margin: 0;
}

.message {
  font-size: 1em;
  color: #333;
  list-style-type: none;
  padding: 2px 10px;
}

vite.config.jsファイルの修正

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
  plugins: [
    laravel({
      input: ['resources/css/chat.css', 'resources/js/app.js'],
      refresh: true,
    }),
  ],
});

チャットコントローラの作成

$ sail artisan make:controller ChatController

  INFO  Controller [app/Http/Controllers/ChatController.php] created successfully.

チャットコントローラの編集

<?php

namespace App\Http\Controllers;
use App\Models\Message;

use Illuminate\Http\Request;

class ChatController extends Controller
{
    //
    public function index()
    {
        // 最後の20件を取得
        $messages = Message::orderBy('created_at', 'desc')->take(20)->get();
        $response = [
            'messages' => $messages
        ];
        return view('chat', $response);
    }
}

ルーティングの修正

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ChatController;

Route::get('/', [ChatController::class, 'index']);

チャット画面の表示テスト

$ sail npm run dev
  VITE v5.2.8  ready in 115 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: http://172.18.0.4:5173/
  ➜  press h + enter to show help

  LARAVEL v11.3.1  plugin v1.0.2

  ➜  APP_URL: http://localhost

http://localhost をブラウザで開いて確認します。
このように表示されれば正しく動いています。「TestMessage」と表示されているのは先ほどTinkerで登録した情報です。こちらも正しく参照して表示しているのがわかります。

さて、いよいよLaravel Reverbを使ってチャットができるように編集していきます。

イベントの作成

まずChatEventイベントを作成します。

$ sail artisan make:event ChatEvent

  INFO  Event [app/Events/ChatEvent.php] created successfully.

イベントの編集

ShouldBroadcastを継承しchannel名は「channel-chat」としました。

<?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 App\Models\Message;

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

    //この値をChat.jsで取得する
    public $message;

    /**
     * Create a new event instance.
     */
    public function __construct($message = null)
    {
        $this->message = $message;
        $messages = new Message();
        $messages->message = $message;
        $messages->save();
    }


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

Chatのメッセージ受信と画面に常時するためのJSを作成します。

このJavascriptではメッセージの送信とイベントのリッスンし送られてきたメッセージを画面を書き換えることなく表示する部分になっています。

/**
 * Send a message to the server
 */
document.getElementById('send-button').addEventListener('click', () => {
  const message = document.getElementById('message').value;
  if (message) {
    axios.post('/', { message: message }).then(() => {
      document.getElementById('message').value = '';
    });
  }
});

/**
 * Listen for events on the channel-chat channel
 */
Echo.channel('channel-chat').listen('ChatEvent', (e) => {
  const newMessage = document.createElement('li');
  newMessage.classList.add('message');
  newMessage.textContent = e.message;
  const ul = document.getElementById('message-list');
  ul.prepend(newMessage);
});

chat.jsをVITEに読み込ませるためapp.jsを修正します。

import './bootstrap';

// Import the chat.js file
import './chat';

チャットメッセージを受信するためのルーティング追加

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ChatController;
use App\Events\ChatEvent;

♯ トップページルーティング
Route::get('/', [ChatController::class, 'index']);

# ポスト用ルーティング
Route::post('/', function (Request $request) {
    ChatEvent::dispatch($request->message);
});

完成と動作テスト

以下のコマンドをそれぞれターミナルを起動して実行します
それぞれ以下のように表示されれば正しく動いています。

LaravelReverbの起動

$ sail artisan reverb:start

   INFO  Starting server on 0.0.0.0:8080 (localhost).

Queueの起動

$ sail artisan queue:work

   INFO  Processing jobs from the [default] queue.

npmのDev起動

$ sail npm run dev

 VITE v5.2.8  ready in 117 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: http://172.18.0.4:5173/
  ➜  press h + enter to show help

  LARAVEL v11.3.1  plugin v1.0.2

  ➜  APP_URL: http://localhost

2つのブラウザで動作テスト

2つのブラウザでそれぞれのメッセージが表示されれば正しい動きになります。
どちらのブラウザに入力してもほぼ同時に両方のブラウザでメッセージが表示されます。

以上で簡単なチャットの作成は終了です。
かなり簡単にリアルタイムチャット可能なサイトができることがわかると思います。このサンプルではまだまだ機能が足りていません、Livewireなどと合わせたりログインの仕組みなども実装していけばもっと利用しやすいものになると思います。

とりあえず通信部分はこのような形で実装できることがわかったので、今後取り入れていこうと思います。

コメント

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