対象読者
・phpの基礎は理解している
・Laravelを使って簡単なアプリを実装したい
・Vue.jsを使った開発について知りたい
・Pusherの使い方を理解したい
始めに
udemyの講座を参考にしています。(無料ですが、講師がインドの方で全て英語です)
講座のlaravelのバージョンが5.4と少し古いので、8.xでも実装できるようにしています。
ログインの機能については解説されていなかったので、この記事でログイン部分の解説を入れています。
Chatプロジェクトの作成
laravelプロジェクトの作成
↓
表示するページの作成
↓
bootstrapインストール
↓
Vue.jsインストール
を行います。
laravelプロジェクトの作成
laravel new Chat
cd Chat
↓
php artisan serve
npm install
↓
npm run dev
ファイルの作成
resources/views/chat.blade.php
※以下のhtmlコードをphpファイルで作成
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <link rel="stylesheet" href="{{ asset('css/app.css') }}"> </head> <body> <div class="container"> <div class="row"> <h1>Chat room</h1> </div> </div> <script src="{{ asset('js/app.js') }}"></script> </body> </html>
routes/web.php
<?php use Illuminate\Support\Facades\Route; Route::get('/', function () { return view('welcome'); }); Route::get('chat', function(){ return view('chat'); });
bootstrapインストール
npm install bootstrap
↓
npm install
↓
npm run dev
ERROR in ./node_modules/bootstrap/dist/js/bootstrap.esm.js 6:0-41
Module not found: Error: Can’t resolve ‘@popperjs/core’ in ‘D:\GitHub\chat\node_modules\bootstrap\dist\js’
以下を実行
Vue.jsのインストール
composerコマンドでフロントエンド開発のベースを簡単にしてくれます。
以下のコマンドでvue.jsを使えるようにします。
※ –authオプションを付けると認証機能も同時にインストールされます。
※講座では上記2つコマンドを実行していないですが、こちらを実行しておくと楽です。
再度、npm run devを実行
Chat(メッセージ)送信機能の実装
resources/views/chat.blade.php
入力したメッセージを表示する<message>タグとメッセージを入力する<input>タグを追加。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <meta name="csrf-token" content="{{csrf_token()}}"> <link rel="stylesheet" href="{{ asset('css/app.css') }}"> <style> .list-group{ overflow-y: scroll; height: 200px; } </style> </head> <body> <div class="container"> <div class="row" id="app"> <h1>Chat room</h1> <div class="offset-4 col-md-4"> <li class="list-group-item active">Chat</li> <ul class="list-group"> <message v-for="value in chat.message"> @{{value}} </message> </ul> <input type="text" class="form-control" placeholder="Type your message here.." v-model='message' @keyup.enter='send'> </div> </div> </div> <script src="{{ asset('js/app.js') }}"></script> </body> </html>
resources/js/app.js
app.jsファイルをjsフォルダ内に作成する。
require('./bootstrap'); window.Vue = require('vue').default; Vue.component('message', require('./components/Message.vue').default); const app = new Vue({ el: '#app', data:{ message:'', chat:{ message:[] } }, methods:{ send(){ if(this.message.length !=0){ this.chat.message.push(this.message); this.message = ''; } }, } });
resources/components/Message.vue
<template> <li class="list-group-item "><slot></slot></li> </template> <script> export default { mounted() { console.log('Component mounted.') } } </script>
php artisan serve
http://127.0.0.1:8000/chat
テキスト入力欄とスクロールバーが表示されていればOKです。
テキストを何回か入力すると以下のような感じになります。
スクール追従機能の実装
このサイトに書いてある通りに以下を実行していきます。
https://www.npmjs.com/package/vue-chat-scroll
resources/js/app.js
以下のコードを追加する。
import Vue from 'vue' import VueChatScroll from 'vue-chat-scroll' Vue.use(VueChatScroll)
chat.blade.php
v-chat-scrollを<ul>タグの中に記載する。
<ul class="list-group" v-chat-scroll>
Event to Broadcast
laravel Echo(JavaScriptライブラリ)が、ブラウザ内でeventを受信できるように、laravel event をbroadcastするサーバ側ドライバーによって実行されます。
laravelですぐに使用できるbroadcastするドライバーにPusherチャンネルとRedisがあり、今回Pusherを利用します。(※Pusherについては後述)
参考ドキュメント
※これ以降もlaravel公式ドキュメントに従い、実装していきます。
以下のコマンドを実行
config/app.php内で以下のコメントアウトを外す。
App\Providers\BroadcastServiceProvider::class,
App/Providers/EventServiceProvider.php
<?php namespace App\Providers; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Event; class EventServiceProvider extends ServiceProvider { /** * The event listener mappings for the application. * * @var array */ protected $listen = [ // Registered::class => [ // SendEmailVerificationNotification::class, // ], 'App\Events\ChatEvent' => [ 'App\Listener\ChatListener', ], ]; /** * Register any events for your application. * * @return void */ public function boot(){ // } }
以下のコマンドを実行
EventsとListenerフォルダがapp内に作成される。
Eventsフォルダに作成されたChatEvent.phpを開いて、ShoudBroadcastを実装。
↓
class ChatEvent implements ShouldBroadcast
app/Events/ChatEvent.php
※以下を追加。
use App\Models\User;
use Illuminate\Queue\SerializesModels; use App\Models\User; class ChatEvent implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels; public $message; public $user; public function __construct($message, User $user) { return $this->message = $message; return $this->user = $user; } public function broadcastOn() { return new PrivateChannel('chat'); } }
コントローラの作成
app/Http/Controllers/ChatController.php
chatとsend関数を作る。
<?php namespace App\Http\Controllers; use App\Events\ChatEvent; use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class ChatController extends Controller { public function chat(){ return view('chat'); } public function send(Request $request){ $user=User::find(Auth::id()); event(new ChatEvent($request->message,$user)); } }
ログイン機能
ログインページはあるものの、テーブルがないため、DBを作る。
phpmyadminを使用想定。(使い方はここでは省略)
chatというDBを作る。
.envを確認。デフォルトのポート番号が3306なら以下のように設定。
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=chat DB_USERNAME=root DB_PASSWORD=
以下のコマンドを実行するとdatabase\migrationsの中にあるファイルの分テーブルが作成される。
phpmyadminのchatにusersテーブルが作成されていることを確認。
http://127.0.0.1:8000/register
ユーザーを登録する。
以下のように表示されればOK
routes/web.php
以下のように記載することで、/chatにアクセスしたときに認証するようになります。
Route::group(['prefix'=> '/', 'middleware'=>'auth'],function(){ Route::get('chat', [App\Http\Controllers\ChatController::class,'chat']); });
※講座ではChatController内で認証チェックをするようにしています。
Pusherを使ったリアルタイム通知機能の実装
Pusherという無料サービスを利用して、eventをbroadcastするドライバー使えるようにしていきます。
googleアカウントで無料登録する。
Channels の「Get Started」をクリック
以下のようにapp名を入力、frontにvue.js、backendにlaravelを選択。
「Create app」後、以下のような画面が表示される。
左側にある「App Keys」をクリック。
app_id,key,secretの値を.envファイルの以下にそれぞれ設定。
PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET=
clusterの値は
config/broadcasting.php
内のclusterに設定する。
'cluster' => 'ap3',
laravel echoのインストール
resources/js/bootstrap.js
コメントアウトを外す。
keyとclusterの値を.envと同じようにpusherのApp keysで確認した値に変更する。
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 });
※講座ではforceTLSがencryptedとなっていますが8.xの公式ドキュメントではforceTLSとなっているので、そのままでOKかと思います。
ChatControllerのsend関数を一旦以下のような関数にする。
public function send(){ $message = 'Hello'; $user=User::find(Auth::id()); event(new ChatEvent($message,$user)); }
web.phpのルーティングに以下を追加。
Route::get('send', [App\Http\Controllers\ChatController::class,'send']);
npm run dev またはnpm run watchを実行
↓
php artisan serve
http://127.0.0.1:8000/send
(画面はないので、白画面が表示)
PusherにてDebug consoleをクリック
ChatController内に記載した固定messageの”Hello”が表示されていることを確認する。
ChatEvent.phpのコンスタント内のreturnを消す。
public function __construct($message, User $user) { $this->message = $message; $this->user = $user; }
pusher側のconsoleにてuser情報が全て表示される。
userのnameだけをpusherのconsoleに表示したい場合、
app/Events/ChatEvent.phpのコンスタント内を以下にする。
public function __construct($message, User $user) { $this->message = $message; $this->user =$user->name; }
/sendでpusherに送られたメッセージをコンソールに表示
pusher側で新しいchannelを作っておく。
channel:chat
Event:ChatEvent
routes/channels.php
に以下を追加する。
Broadcast::channel('chat', function(){ return true; });
resources/js/app.js
に以下をmounted()を追加
mounted(){ window.Echo.private('chat') .listen('ChatEvent', (e) => { console.log(e); }); }
※講座ではwindowがついてないですが、ないとおそらくエラーになります。
http://127.0.0.1:8000/send
(画面はないので、白画面が表示)
http://127.0.0.1:8000/chat
側に固定メッセージがコンソール上に表示される
axiosを使ってメッセージを送信する
resources/js/bootstrap.jsにaxiosが記載されていれば、インストールコマンドを実行する必要はないです。
※ない場合
npm install axios
resources/js/app.js
axios.postを追加。
methods:{ send(){ if(this.message.length !=0){ //console.log(this.message); this.chat.message.push(this.message); axios.post('/send', { message: this.message }) .then(response => { console.log(response); this.message = ''; }) .catch(error => { console.log(error); }); } } }, mounted(){ window.Echo.private('chat') .listen('ChatEvent', (e) => { this.chat.message.push(e.message); console.log(e); }); }
app/Http/Controllers/ChatController.php
send関数を以下のようにする。
public function send(Request $request){ $user=User::find(Auth::id()); event(new ChatEvent($request->message,$user)); }
この状態で異なるブラウザを2つ並べて、片方にメッセージを入力すると、入力した方(右側)は、2つメッセージが表示され、もう片方(左側)は、1つメッセージが表示されます。
右側は、自分が入力したメッセージとbroadcastされたものが表示されてます。
左側は、broadcastされたもののみが表示されてます。
自分が入力したものが重複して表示されないようにします。
app/Events/ChatEvent.php
public function __construct($message, User $user) { $this->message = $message; $this->user = $user->name; $this->dontBroadcastToCurrentUser(); }
$this->dontBroadcastToCurrentUser();
この関数を使うことで、入力したときにメッセージが2回表示されなくなります。
resources/js/app.js
自分が入力したメッセージと相手方でユーザー名が識別されるようにしていきます。
const app = new Vue({ el: '#app', data:{ message:'', chat:{ message:[], user:[], color:[] } }, methods:{ send(){ if(this.message.length !=0){ //console.log(this.message); this.chat.message.push(this.message); this.chat.color.push('success'); this.chat.user.push('you'); axios.post('/send', { message: this.message }) .then(response => { console.log(response); this.message = ''; }) .catch(error => { console.log(error); }); } } }, mounted(){ window.Echo.private('chat') .listen('ChatEvent', (e) => { this.chat.message.push(e.message); this.chat.user.push(e.user); this.chat.color.push('warning'); console.log(e); }); } });
dataにuserとcolorを追加、send()内について
this.chat.color.push('success'); //success(緑色)で表示 this.chat.user.push('you'); //youは自分が送る場合、常にyouを表示
mounted()内にも同様に追加
this.chat.user.push(e.user); //メッセージを受信した方にユーザーの名前を表示 this.chat.color.push('warning'); //黄色で表示
resources/components/Message.vue
propsに’user’を追加
<template> <div> <li class="list-group-item" :class="className"><slot></slot></li> <small class="float-right" :class="className">{{user}}</small> </div> </template> <script> export default { props:{ 'color':{}, 'user':{} },
youを{{user}}に変更
<small class="float-right" :class="className">{{user}}</small>
resources/views/chat.blade.php
<body> <div class="container"> <div class="row" id="app"> <h1>Chat room</h1> <div class="offset-4 col-md-4"> <li class="list-group-item active">Chat</li> <ul class="list-group" v-chat-scroll> <message v-for="value,index in chat.message" :key=value.index :color=chat.color[index] :user=chat.user[index]> @{{value}} </message> </ul> <input type="text" class="form-control" placeholder="Type your message here.." v-model='message' @keyup.enter='send'> </div> </div> </div> <script src="{{ asset('js/app.js') }}"></script> </body>
<message>タグ内にcolorとuserを追加
ブラウザを2つようにしてそれぞれ違うユーザーでログインした状態で、それぞれメッセージを送信すると、自分のメッセージは緑(success)、名前はyou、受信した側のメッセージは黄色(warning)、名前はユーザー名(送信したログインユーザー)になったことを確認。
※名前が左に表示されていますが、bootstrapがうまくいってなかったので、上の方に記載したコマンドを後で実行しなおしています。(本来なら右側に表示されます)