laravel11 + reverb によるリアルタイムチャットをします。
ネット上に素晴らしいサイトがありました。
「Web屋さんの覚書」この方がいなければ私はいつまでも迷路でもがいていたことでしょう。
でもこのサイトはトップの方にはないので随分と苦労しました。他のサイトはほとんど役に立ちません。
さすがの chatGPT もこの件に関しては全然だめで、延々と同じ過ちを繰り返し最後には chatGPT そのものが反応しなくなりました。 さんざんタダでこき使ってきたので AI といえどもちょっとすねたのかもしれません。
linux mint 22Laravel Framework 11.39.1PHP 8.3.6 (cli)
参考サイトのほとんどコピペですが、私は docker は嫌いなので使いません。
composer create-project laravel/laravel:^11.0 larachatcd larachatphp artisan serve
一旦、ctl + c でストップ。
npm 設定
npm installnpm install -D sass
reverb をインストール
php artisan install:broadcasting
message テーブル作成のためのマイグレーションテーブル作成
php artisan make:migration create_message_table
編集
<?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'); }};
マイグレーション
php artisan migrate
データ挿入のための seeder 作成。
php artisan make:seeder MessagesTableSeeder
編集
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;use Illuminate\Support\Facades\DB;use Carbon\Carbon;
class MessagesTableSeeder extends Seeder{ /** * Run the database seeds. */ public function run(): void { DB::table('messages')->insert([ 'message' => 'hello', 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); }}
シーディング。
php artisan db:seed --class=MessagesTableSeeder
php artisan make:model Message
chat.blade.php を新規作成します。
<!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>
mkdir resources/scss
resources/scss/chat.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; }
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, }), ],});
php artisan make:controller ChatController
編集
<?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']);
別々のウィンドウで。
cd ~/larachatnpm run dev
と
cd ~/larachatphp artisan serve
で起動して、http://127.0.0.1:8000/で確認。
php artisan make:event ChatEvent
編集
<?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'), ]; }}
/** * 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); });
app.js の修正
import './bootstrap';
// Import the chat.js fileimport './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);});
それぞれ別のウィンドウで、
cd ~/larachatnpm run dev
と
cd ~/larachatphp artisan reverb:start
と
cd ~/larachatphp artisan serve
と
cd ~/larachatphp artisan queue:work
で起動して、http://127.0.0.1:8000/で確認。
firefox と chrome など別のブラウザでアクセスして確認します。
いちいちいろいろ起動するのはとても面倒なのでシェルスクリプトを作成しました。
#!/bin/bash
# プロセス終了時に一括で停止する設定trap 'kill 0' EXIT
cd ~/larachat
# NPMの開発サーバーをバックグラウンドで実行npm run dev &echo "Started npm run dev with PID $!"
# PHP Artisanのreverb:startをバックグラウンドで実行php artisan reverb:start &echo "Started php artisan reverb:start with PID $!"
# PHP Artisanのサーバーをバックグラウンドで実行php artisan serve &echo "Started php artisan serve with PID $!"
# PHP Artisanのキューをバックグラウンドで実行php artisan queue:work &echo "Started php artisan queue:work with PID $!"
# 全プロセスが終了するまで待機wait
これは chatGPT さんに作ってもらいました。一瞬でこんなものを作ってくれました。
こういうことをやらせると無敵ですね。