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などと合わせたりログインの仕組みなども実装していけばもっと利用しやすいものになると思います。
とりあえず通信部分はこのような形で実装できることがわかったので、今後取り入れていこうと思います。
コメント