👏
【随時更新】Laravel6を使ってリアルタイムチャットを作る
参考にしたサイト
↓だからブラウザ更新しなくてリアルタイムで通知が来るのか〜と納得できたqiita記事です。 ありがとうございます😀laravelのバージョンは6系
完成系
gifなんで動作が遅く見えますが、もっと動作は早いです。
リロードしなくても相手のブラウザにメッセージが更新されていることがわかります。
動作のイメージはこんな感じ
0. pusherの登録
pusherを使うのに必要な環境をcomposerでインストール
"~4.0"をつけないとlaravel6ではうまく動かなかったです
composer require pusher/pusher-php-server "~4.0"
1. configディレクトリのapp.phpのBroadcastServiceProviderのコメントを外す
app.php
App\Providers\BroadcastServiceProvider::class,
2. .envファイルのBROADCAST_DRIVERの値をpusherとする
.env
BROADCAST_DRIVER=pusher
また.envファイルでapiキーを登録します。
pusherのGetting StartedのStep2でServerをLaravelと選択すると以下のようなキーを取得することができるので.envファイルに設定します。
PUSHER_APP_ID=英数字列が入ってます
PUSHER_APP_KEY=英数字列が入ってます
PUSHER_APP_SECRET=英数字列が入ってます
3. イベントの作成
ShouldBroadcastインターフェースを継承して、broadcastOnでreturn new Channel(‘チャンネル名’)とする。
イベント名をMessageAddedに、チャンネル名をmessage-added-channelにしました。
この時点でイベントを発火させると、pusherの方にデータを送ることができます。
MessageAdded.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 MessageAdded implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($message)
{
$this->message = $message;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('message-added-channel',[$this->message]);
}
}
4. laravel-echoとpusher-jsライブラリをインストール
ブラウザでpusherから情報を受け取るためのライブラリーをインストールします。
npm install --save laravel-echo pusher-js
resource\js\bootstrap.jsの以下のコードのコメントを外す
bootstrap.js
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,
encrypted: true
});
5. 今回使用するbladeを作成
chatroom.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">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Todos</title>
</head>
<body>
<div id="message_area">
</div>
<div>
<textarea id = "message" rows="5" cols="100"></textarea>
<button id="submit">送信</button>
</div>
<script src="/js/app.js"></script>
<script src="{{asset('js/chat.js')}}"></script>
</body>
</html>
6. コントローラー
ChatroomController.php
<?php
namespace App\Http\Controllers;
use App\Events\MessageAdded;
use Illuminate\Http\Request;
use App\Message;
use PHPUnit\Util\Test;
use Illuminate\Support\Facades\Auth;
class ChatroomController extends Controller
{
//チャット画面を表示
public function index()
{
return view('chatroom');
}
//新しいメッセージが来たとき
public function newmessage(Request $request)
{
$message = new Message();
$message->message = $request->message;
$message->user_id = Auth::id();
$message->save();
//イベント発動
//新しいメッセージをpusherに
event(new MessageAdded([$message,Auth::user()]));
}
//最初にアクセスした時、全メッセージを返す
public function allmessage()
{
return Message::with('user')->get();
}
}
7. 非同期通信のためのJavaScriptを書く(XmlHttpRequestを使用)
ajax通信する時、いつもjQueryに頼っていたので今回は以下の記事を参考にしました。
chat.js
window.Echo.channel('message-added-channel')
.listen('MessageAdded',function(data){
console.log('received a message');
console.log(data);
let newmessage = data.message[0].message;
let name = data.message[1].name;
let message = document.querySelector('#message_area');
message.insertAdjacentHTML('beforeend',name+" "+newmessage +" "+data.message[0].created_at+"<br>");
});
//一度だけ実行される処理
var xhr = new XMLHttpRequest();
xhr.open('GET', '/allmessage');
xhr.send();
//最初にアクセスした時全てのメッセージを取得
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
const json = xhr.responseText;
const obj = JSON.parse(json);
console.log(obj);
const message = document.querySelector('#message_area');
for(let i = 0; i < obj.length; i++){
message.insertAdjacentHTML('beforeend' , obj[i].user.name+" "+obj[i].message +" "+obj[i].created_at+"<br>");
}
}
}
const submitbutton = document.querySelector('#submit');
//送信ボタンをクリックした時
submitbutton.addEventListener('click',()=>{
const message = document.querySelector('#message');
console.log(message.value);
const xhr = new XMLHttpRequest();
let token = document.getElementsByName('csrf-token').item(0).content;
xhr.open('POST','/newmessage')
xhr.setRequestHeader('X-CSRF-Token', token);
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
console.log(message.value);
xhr.send("message="+message.value);
message.value = '';
});
8. ルーティング
web.php
<?php
use App\Events\MessageAdded;
use Symfony\Component\EventDispatcher\Event;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');
// Route::get('/test', function () {
// event(new MessageAdded('message!!!'));
// });
//ajax通信のルーティング
Route::post('/newmessage', 'ChatroomController@newmessage');
Route::get('/allmessage','ChatroomController@allmessage');
//チャットルームを表示
Route::get('/chatroom', 'ChatroomController@index')->middleware('auth');
Discussion