Chat room với laravel 5.5 và Vue.js trong 15 phút
Bài đăng này đã không được cập nhật trong 3 năm
- Ở bài trước mình đã giới thiệu đến các bạn cách kết hợp Laravel với Vue.js trong bài viết: Simple CRUD Project. Bài này ta sẽ thử làm một ứng dụng chat room sử dụng Laravel Broadcasting, Pusher kết hợp với Vue.js. Mục tiêu của bài viết là như sau:
I. Setup
- Tương tự như bài hướng dẫn trước ta cũng cần tạo project laravel, tạo liên kết database và test thử vue component.
- Bước 1: Tạo project laravel chatdemo lệnh sau:
laravel new chatdemo
- Bước 2: Chạy lệnh
npm install
và lệnhnpm run watch
- Bước 3: Tạo view chat như sau: resources/views/chat.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="row"> <div class="panel panel-default"> <div class="panel-heading"> Chatroom </div> <div class="panel-body"> <example></example> </div> </div> </div> </div> @endsection
- Và thêm vào routes/web.php:
Route::get('/chat', function () { return view('chat'); });
- Và thêm vào routes/web.php:
- Bước 4: Chạy lệnh
php artisan serve
và truy cập vào đường link https://localhost:8000/chat check Example component
II. Vue Components
- Phần này ta sẽ tạo ra các vue components: ChatLog.vue, ChatMessage.vue và ChatComposer.vue tương tự như resources/assets/js/components/Example.vue mặc định của laravel (tham khảo VueJS component docs)
- Trước tiên ta cần thêm các components vào resources/js/app.js
Vue.component('chat-log', require('./components/ChatLog.vue')); Vue.component('chat-message', require('./components/ChatMessage.vue')); Vue.component('chat-composer', require('./components/ChatComposer.vue')); const app = new Vue({ el: '#app', data: { messages: [ { message: 'Hey!', user: "Van Giang" }, { message: 'Hello!', user: "Hong Quan" } ] }, methods: { addMessage(message) { // Add to existing messages this.messages.push(message); // Persist to the database etc } } });
- 2 message được tạo sẵn bởi Giang và Quân (phần sau các message này sẽ được load ra từ DB) dùng cho ChatLog component, còn method
addMessage
sẽ được gọi trong ChatComposer component - resources/assets/js/components/ChatLog.vue : Hiển thị danh sách các messages
<template lang="html"> <div class="chat-log"> <chat-message v-for="message in messages" :message="message"></chat-message> </div> </template> <script> export default { props: ['messages'] } </script> <style lang="css"> .chat-log .chat-message:nth-child(even) { background-color: #ccc; } </style>
- resources/assets/js/components/ChatMessage.vue : hiển thị 1 message, được gọi từ ChatLog.vue thông qua thẻ
chat-message
<template lang="html"> <div class="chat-message"> <p>{{ message.message }}</p> <small>{{ message.user }}</small> </div> </template> <script> export default { props: ['message'] } </script> <style lang="css"> .chat-message { padding: 1rem; } .chat-message > p { margin-bottom: .5rem; } </style>
- resources/assets/js/components/ChatComposer.vue : tạo 1 message mới
<template lang="html"> <div class="chat-composer"> <input type="text" placeholder="Start typing your message..." v-model="messageText" @keyup.enter="sendMessage"> <button class="btn btn-primary" @click="sendMessage">Send</button> </div> </template> <script> export default { data() { return { messageText: '' } }, methods: { sendMessage() { this.$emit('messagesent', { message: this.messageText, user: "Van Giang" }); this.messageText = ''; } } } </script> <style lang="css"> .chat-composer { display: flex; } .chat-composer input { flex: 1 auto; } .chat-composer button { border-radius: 0; } </style>
- Giờ ta chỉ việc xóa bỏ example component trong chat.blade.php và gọi đến ChatLog, ChatComposer component
<chat-log :messages="messages"></chat-log> <chat-composer v-on:messagesent="addMessage"></chat-composer>
III. Laravel Backend
- Chạy lệnh
php artisan make:auth
- Tạo model Message cùng file migrate với lệnh:
php artisan make:model Message -m
- Bảng messages sẽ có trường
message
vàuser_id
Schema::create('messages', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); $table->text('message'); $table->integer('user_id')->unsigned(); });
- Tạo quan hệ 1-N giữa bảng users và bảng messages:
class Message extends Model { protected $fillable = ['message']; public function user() { return $this->belongsTo(User::class); } }
//app/User.php public function messages() { return $this->hasMany(Message::class); }
- Tạo database và chạy command tạo bảng:
php artisan migrate
- Thêm các routes vào routes/web.php
Route::get('/chat', function () { return view('chat'); })->middleware('auth'); Route::get('/messages', function () { return App\Message::with('user')->get(); })->middleware('auth'); Route::post('/messages', function () { // Store the new message $user = Auth::user(); $user->messages()->create([ 'message' => request()->get('message') ]); return ['status' => 'OK']; })->middleware('auth'); Auth::routes(); Route::get('/home', 'HomeController@index');
- Thay vì hardcode message như phần II thì tới đây ta sẽ load message từ DB và insert message mới vào DB, update file app.js:
const app = new Vue({ el: '#app', data: { messages: [] }, methods: { addMessage(message) { // Add to existing messages this.messages.push(message); // Persist to the database etc axios.post('/messages', message).then(response => { // Do whatever; }) } }, created() { axios.get('/messages').then(response => { this.messages = response.data; }); } });
- Biến
messages
giờ đây sẽ có dạng như sau:messages = [ message: "Hello", user: { name: "Van Giang" } ];
- Nên ta cần update lại ChatComposer.vue: tên của người gửi được lấy từ dropdown góc cao bên phải:
methods: { sendMessage() { this.$emit('messagesent', { message: this.messageText, user: { name: $('.navbar-right .dropdown-toggle').text() } }); this.messageText = ''; } } ... ... .chat-composer input { flex: 1 auto; padding: .5rem 1rem; }
- Thêm thiện thị cho trường hợp chưa có message nào trong DB: ChatLog.vue
<chat-message v-for="message in messages" :message="message"></chat-message> <div class="empty" v-show="messages.length === 0"> Nothing here yet! </div> ... .empty { padding: 1rem; text-align: center; }
- update ChatMessage.vue
<p>{{ message.message }}</p> <small>{{ message.user.name }}</small>
IV. Laravel Echo
-
Bước này sẽ làm cho ứng dụng trở nên realtime, bạn cần nắm được được một vài kiến thức cơ bản về Event Broadcasting và tạo ra 1 app tại Pusher
-
Tạo app Pusher, truy cập vào https://pusher.com và tạo 1 app tương tự như sau: ta cần lưu ý những thông tin sau để dùng trong ứng dụng chatroom:
app_id = "446989" key = "xxxxxxxxxxxxxx" secret = "xxxxxxxxxxxxxx" cluster = "ap1"
-
Tạo event MessagePosted.php với lệnh:
php artisan make:event MessagePosted
namespace App\Events; use App\User; 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 MessagePosted implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels; public $message; public $user; /** * Create a new event instance. * * @return void */ public function __construct(Message $message, User $user) { $this->message = $message; $this->user = $user; } /** * Get the channels the event should broadcast on. * * @return \Illuminate\Broadcasting\Channel|array */ public function broadcastOn() { return new PresenceChannel('chatroom'); } }
-
Đăng ký channel trong routes/channel.php:
Broadcast::channel('chatroom', function ($user) { return $user; });
-
Event này được gọi khi 1 message mới được tạo trong routes/web.php
use App\Events\MessagePosted; Route::post('/messages', function () { $user = Auth::user(); $message = $user->messages()->create([ 'message' => request()->get('message') ]); broadcast(new MessagePosted($message, $user))->toOthers(); return ['status' => 'OK']; })->middleware('auth');
-
Cài đặt Pusher PHP SDK:
composer require pusher/pusher-php-server "~3.0"
-
Cài đặt Laravel Echo:
npm install --save laravel-echo pusher-js
-
Uncomment và truyền key ở trên vào resources/assets/js/bootstrap.js
import Echo from 'laravel-echo' window.Pusher = require('pusher-js'); window.Echo = new Echo({ broadcaster: 'pusher', key: 'your-pusher-key', cluster: 'ap1', encrypted: true });
-
Tất cả cấu hình broadcasting được đặt trong file config/broadcasting.php
'pusher' => [ 'driver' => 'pusher', 'key' => env('PUSHER_APP_KEY'), 'secret' => env('PUSHER_APP_SECRET'), 'app_id' => env('PUSHER_APP_ID'), 'options' => [ 'cluster' => 'ap1', 'encrypted' => true, ], ],
- ta chỉ cần truyền app_key, app_secret, app_id, cluster vào file .env là xong (nhớ set BROADCAST_DRIVER=pusher).
-
Để thêm hiển thị số lượng người đang trong Room, ta cần chỉnh lại một chút file chat.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="row"> <div class="panel panel-default"> <div class="panel-heading"> Chatroom <span class="badge pull-right">@{{ usersInRoom.length }}</span> </div> <div class="panel-body"> <chat-log :messages="messages"></chat-log> <chat-composer v-on:messagesent="addMessage"></chat-composer> </div> </div> </div> </div> @endsection
-
Và update file app.js để thực hiện việc update real-time
... data: { messages: [], usersInRoom: [] }, ... created() { axios.get('/messages').then(response => { this.messages = response.data; }); Echo.join('chatroom') .here((users) => { this.usersInRoom = users; }) .joining((user) => { this.usersInRoom.push(user); }) .leaving((user) => { this.usersInRoom = this.usersInRoom.filter(u => u != user) }) .listen('MessagePosted', (e) => { this.messages.push({ message: e.message.message, user: e.user }); }); }
-
Pusher cũng cung cấp công cụ test request Debug Console:
-
Hy vọng bài viết sẽ giúp ích được bạn, nếu bạn có gặp khó khăn gì trong lúc thực hiện hãy liên hệ với mình hoặc tài liệu tham khảo bên dưới
Tài liệu tham khảo
Laravel Broadcasting Vue.js Components Building a Realtime Chat App with Laravel 5.4 and VueJS Source code
All rights reserved