はじめに
今回は、Pusherを使ったリアルタイムチャット機能を実装していきます!!
この記事の目標は、ブラウザを2つ開いて片方からチャットを送るとリロードしなくても自動更新される簡単なリアルタイムチャット機能を目指します!
実行環境
Vue : 3.0.5
Laravel : 8.63.0
まず簡単なチャット機能を作る(リアルタイムではない)
今回の本題は、リアルタイムということなのでここはサクッと進みたいと思います。
テーブルとモデルの作成
まずチャットのメッセージを保存するためのテーブルを作ります。
php artisan make:model Message
php artisan make:migration create_messages_table --create=messages
public function up()
{
Schema::create('messages', function (Blueprint $table) {
table->bigIncrements('id');
$table->text('body');//追記
$table->timestamps();
});
}
class ChatMessage extends Model
{
protected $guarded = ['id'];//追記
protected $table = 'chat_messages';//追記
}
php artisan migrate
これでチャットのメッセージを保存するためのテーブルが完成です。
チャットページの作成
次に、チャットページを作っていきます。(ここも同じくサクッと進みます)
ルートを追加
Route::get('/chat', 'ChatController@index');
Route::post('/chat', 'ChatController@create');
コントローラーを追加
php artisan make:controller ChatController
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Message;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
class ChatController extends Controller
{
// 新着順にメッセージ一覧を取得
public function index() {
$messages = Message::orderBy('id', 'desc')->get();
return Inertia::render(
'Chat',
['messages' => $messages]
);
}
// メッセージを登録
public function create(Request $request) {
DB::beginTransaction();
try {
$message = new Message;
$message->text = $request->message;
$message->save();
DB::commit();
} catch (\PDOException $e) {
\Log::debug($e->getMessage());
DB::rollback();
}
}
}
ページを追加
<template>
<div>
<div>
<textarea v-model="message"></textarea><br>
<button type="button" v-on:click="send()">送信</button>
</div>
<div v-for="m in messages" :key="m.id">
<span v-text="m.text"></span>
</div>
</div>
</template>
<script>
export default {
props: {
messages: Array,
},
data: function() {
return {
message: '',
}
},
methods: {
send() {
const formData = new FormData()
formData.append('message',this.message)
this.$inertia.post('/chat', formData, {
onSuccess: () => (this.message = '';),
})
},
},
}
</script>
サクッとですがこれでリアルタイムではないチャット機能の完成です!!
チャット機能をリアルタイムにしていく
ここからが本題です!!
今作ったチャット機能をリロード無しで自動的に更新されるリアルタイムチャットにしていきます!!
今回は、Pusherを使うので、Pusherに登録して.envに必要なPusherのAppKeysを取得します。
https://pusher.com/
PusherのAppKeysを追加、編集
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=取得したKey
PUSHER_APP_KEY=取得したKey
PUSHER_APP_SECRET=取得したKey
PUSHER_APP_CLUSTER=取得したKey
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
App\Providers\BroadcastServiceProvider::class,//ここを解除
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\FortifyServiceProvider::class,
App\Providers\JetstreamServiceProvider::class,
NotificationChannels\WebPush\WebPushServiceProvider::class,
デフォルトではBroadcastServiceProviderがコメント化されているので、これを解除してあげます。
composer require pusher/pusher-php-server "~3.0"
npm install --save laravel-echo pusher-js
上記のコマンドPusher用のパッケージと、Pusherとのつなぎ役をしてくれるLaravelEchoをインストールします。
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
});
インストールが完了後、bootstrap.jsの上記の部分もデフォルトではコメント化されているので、これを解除してあげます。
npm run dev
ビルドして更新しておきます。
イベントの作成
php artisan make:event MessageCreated
上記のコマンドでイベント用のファイルを用意します。
<?php
namespace App\Events;
use App\Message;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class MessageCreated implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Message $message)
{
$this->message = $message;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('chat');
}
}
これでイベント用のファイルができたので、メッセージが送信された先のコントローラーにこのイベントが呼ばれるようにしていきましょう!
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Message;
use App\Events\MessageCreated;//追記
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
class ChatController extends Controller
{
// 新着順にメッセージ一覧を取得
public function index() {
$messages = Message::orderBy('id', 'desc')->get();
return Inertia::render(
'Chat',
['messages' => $messages]
);
}
// メッセージを登録
public function create(Request $request) {
DB::beginTransaction();
try {
$message = new Message;
$message->text = $request->message;
$message->save();
event(new MessageCreated($message));//追記
DB::commit();
} catch (\PDOException $e) {
\Log::debug($e->getMessage());
DB::rollback();
}
}
}
では、最後にvue側にもPusherからの通知を待機するLaravelEchoのコードをmounted()内に追加してビルドしたら作業は終了です。
<template>
<div>
<div>
<textarea v-model="message"></textarea><br>
<button type="button" v-on:click="send()">送信</button>
</div>
<div v-for="m in messages" :key="m.id">
<span v-text="m.text"></span>
</div>
</div>
</template>
<script>
export default {
props: {
messages: Array,
},
data: function() {
return {
message: '',
}
},
mounted() { //追記
Echo.channel('chat')//追記
.listen('MessageCreated', (e) => {//追記
this.getMessages();//追記
});//追記
},//追記
methods: {
getMessages() {//追記
this.$inertia.get('/chat');//追記
},//追記
send() {
const formData = new FormData()
formData.append('message',this.message)
this.$inertia.post('/chat', formData, {
onSuccess: () => (this.message = '';),
})
},
},
}
</script>
npm run dev
リアルタイムチャットの実装は以上となります!!
確認方法
確認方法は、2種類のブラウザ(chromeとfirefox)を用意して確認するか、
同じブラウザでも片方は、シークレットモードにすれば確認できるはずです!!
片方からメッセージを送信しリロードしなくても自動でもう片方のメッセージが更新されたら成功です!!
お疲れ様でした!!
最後まで読んでくれてありがとうございます!!
どこか途中でうまく行かなかった場合は、気軽にコメントorツイッター(@LC_Ono)のDMでも対応します!