Xây dựng một ứng dụng chat realtime với Laravel, Vuejs và Pusher
Bài đăng này đã không được cập nhật trong 5 năm
Lời mở đầu
-
Với Laravel chúng ta có thể dễ dàng xây dựng những ứng dụng realtime một cách dễ dàng bằng việc sử dụng event broadcasting cho phép chúng ta phát và nghe event giữa server-side và phía Javascript client-side.
-
Bên cạnh đó, Laravel cũng hỗ trợ Pusher, một platform đơn giản và đáng tin cậy cho việc xây dựng một ứng dụng realtime linh hoạt dễ mở rộng, được đông đảo cộng đồng Laravel sử dụng.
-
Trong bài viết này, mình sẽ trình bày các bước chi tiết để xây dựng lên một ứng dụng chat với Laravel và Pusher. Phía Front end mình sử dụng framwork JavaScript là Vue.js
-
Trước khi bắt đầu chúng ta hãy xem qua ứng dụng mà chúng ta sẽ xây dựng:
Bắt đầu
Setting Up Laravel
- Tạo một project Laravel với tên là
laravel-chat
bằng composer:composer create-project --prefer-dist laravel/laravel laravel-chat
- Muốn sử dụng laravel event broadcasting, trước tiên chúng ta phải đăng kí
App\Providers\BroadcastServiceProvider
. Mở fileconfig/app.php
và bỏ comment dòng sau trongproviders
// App\Providers\BroadcastServiceProvider
- Tiếp theo chúng ta phải nói cho Laravel biết rằng chúng ta sử dụng Pusher driver. Trong file
.env
:// .env BROADCAST_DRIVER=pusher
- Mặc dù Laravel đã hỗ trợ Pusher tuy nhiên chúng ta vẫn phải cài đặt Pusher PHP SDK. Chúng ta sẽ cài đặt thông qua composer:
composer require pusher/pusher-php-server
Setting Up Pusher
1. Phía backend
- Sau khi install hoàn tất, chúng ta cần config những thông tin xác thực cho ứng dụng Pusher ở trong file config/broadcasting.php.
- Để có được những thông tin xác thực cho ứng dụng Pusher, chúng ta phải có tài khoản Pusher.
- Chúng ta tạo một tài khoản Pusher free tại đây https://pusher.com/signup, sau khi login chúng ta tạo một ứng dụng với Pusher, sau khi tạo thành công chúng ta sẽ có thông tin xác thực cho ứng dụng của bạn (vào tab Overview, kéo xuống phần key)
- Lấy những thông tin xác thực cho ứng dụng và điền vào file .env
// .env
PUSHER_APP_ID=xxxxxx
PUSHER_APP_KEY=xxxxxxxxxxxxxxxxxxxx
PUSHER_APP_SECRET=xxxxxxxxxxxxxxxxxxxx
PUSHER_CLUSTER=xx
- Ở file
config/broadcasting.php
, Laravel sẽ lấy những thông tin xác thực trên từ file .env:
// Don't add your credentials here!
// config/broadcasting.php
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [],
],
Chúng ta sẽ thêm một chút vào đoạn code trên:
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_CLUSTER'),
'encrypted' => true,
],
],
2. Phía Frondend
- Trước tiên chúng ta tải về các dependencies thông qua npm
npm install
- Để lắng nghe events, Laravel cung cấp cho chúng ta Laravel Echo, là một thư viện Javascript giúp dễ dàng đăng kí channels và lắng nghe những event được broadcast bởi Laravel. Chúng ta cần tải về nó cùng với thư viện Pusher JavaScript
npm install --save laravel-echo pusher-js
- Sau khi đã install, chúng ta cần nói cho Laravel Echo rằng chúng ta sử dụng Pusher.
Mở file
resources/assets/js/bootstrap.js
, ta sẽ thấy Laravel đã tích hợp Echo. Chúng ta bỏ comment phần Laravel Echo và thêm vào một số thông tin của Pusher:
Key ở đây là Pusher app key còn cluster bạn sử dụng cluster đã khai báo phía trên trong file// resources/assets/js/bootstrap.js import Echo from "laravel-echo" window.Echo = new Echo({ broadcaster: 'pusher', key: 'xxxxxxxxxxxxxxxxxxxx', cluster: 'eu', encrypted: true });
config/broadcasting.php
. - Đến đây chúng ta đã xong xuôi phần setting Laravel và Pusher. Hãy bắt đầu xây dựng ứng dụng chat realtime nào.
Authenticating Users
- Ứng dụng của chúng ta sẽ yêu cầu người dùng đăng nhập trước khi bắt đầu chat. Do đó ta cần authentication. Với Laravel chỉ cần chạy câu lệnh đơn giản này để tạo ra những routes, view, controllers cần thiết cho việc authen.
php artisan make:auth
- Mở file .env và kết nối database:
// .env DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=laravel-chat DB_USERNAME=root DB_PASSWORD=root
- Chạy migration
php artisan migrate
Message Model and Migration
-
Tạo model
Message
cùng với file migration:php artisan make:model Message -m
-
Trong file model Message.php:
// app/Message.php /** * Fields that are mass assignable * * @var array */ protected $fillable = ['message'];
-
Trong file migration (nằm trong thư mục database/migrations) cho bảng message, trong phương thức
up
:Schema::create('messages', function (Blueprint $table) { $table->increments('id'); $table->integer('user_id')->unsigned(); $table->text('message'); $table->timestamps(); });
Bảng message sẽ có 5 cột: id, user_id lưu Id của user gửi message, cột message lưu nội dung message, created_at và updated_at.
- Chạy migration để tạo bảng
php artisan migrate
User To Message Relationship
- Một user có thể gửi nhiều message và một message chỉ được gửi bởi một user. Mối quan hệ giữa user và message là mối quan hệ một - nhiều.
- Trong model User:
// app/User.php /** * A user can have many messages * * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function messages() { return $this->hasMany(Message::class); }
- Trong model Message:
// app/Message.php /** * A message belong to a user * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function user() { return $this->belongsTo(User::class); }
Defining App Routes
- Mở file
route/web.php
và định nghĩa những route như sau:// routes/web.php Auth::routes(); Route::get('/', 'ChatsController@index'); Route::get('messages', 'ChatsController@fetchMessages'); Route::post('messages', 'ChatsController@sendMessage');
- Route GET messages sẽ lấy ra tất cả các message chat và route POST messages sẽ sử dụng để gửi một message mới.
ChatsController
- Bây giờ chúng ta sẽ tạo các controller để xử lý logic cho ứng dụng của chúng ta. Tạo một
ChatsController
với câu lệnh:php artisan make:controller ChatsController
- Mở file
app/Http/Controllers/ChatsController.php
vừa mới tạo ra, và viết đoạn code sau:// app/Http/Controllers/ChatsController.php use App\Message; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; public function __construct() { $this->middleware('auth'); } /** * Show chats * * @return \Illuminate\Http\Response */ public function index() { return view('chat'); } /** * Fetch all messages * * @return Message */ public function fetchMessages() { return Message::with('user')->get(); } /** * Persist message to database * * @param Request $request * @return Response */ public function sendMessage(Request $request) { $user = Auth::user(); $message = $user->messages()->create([ 'message' => $request->input('message') ]); return ['status' => 'Message Sent!']; }
- Trong hàm
contruct()
sử dụng middlewareauth
để chỉ định rằng tất cả các phương thức trong controller chỉ được access bởi user đã đăng nhập. - Phương thức
fetchMessages()
trả về một JSON tất cả các mesage cùng user sở hữu message đó. - Phương thức
sendMessage()
sẽ lưu message vào database và trả về trạng thái gửi message.
Creating The Chat App View
-
Về phía view cho ứng dụng, chúng ta sẽ sử dụng Bootsnipp chat snippet với một vài tùy chỉnh.
-
Tạo một file
resources/views/chat.blade.php
với nội dung:<!-- resources/views/chat.blade.php --> @extends('layouts.app') @section('content') <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <div class="panel panel-default"> <div class="panel-heading">Chats</div> <div class="panel-body"> <chat-messages :messages="messages"></chat-messages> </div> <div class="panel-footer"> <chat-form v-on:messagesent="addMessage" :user="{{ Auth::user() }}" ></chat-form> </div> </div> </div> </div> </div> @endsection
-
ở đây chúng ta include 2 Vue component:
chat-message
sẽ dùng để hiển thị tất cả message vàchat-form
sẽ cung cấp một trường input và một button để gửi message. -
Tạo component
ChatMessages.vue
trong thư mụcresources/assets/js/components
với nội dung:// resources/assets/js/components/ChatMessages.vue <template> <ul class="chat"> <li class="left clearfix" v-for="message in messages"> <div class="chat-body clearfix"> <div class="header"> <strong class="primary-font"> {{ message.user.name }} </strong> </div> <p> {{ message.message }} </p> </div> </li> </ul> </template> <script> export default { props: ['messages'] }; </script>
Component này nhận vào một
props
là array messages. -
Tiếp theo chúng ta tạo một component
ChatForm.vue
, cũng trong thư mụcresources/assets/js/components
:// resources/assets/js/components/ChatForm.vue <template> <div class="input-group"> <input id="btn-input" type="text" name="message" class="form-control input-sm" placeholder="Type your message here..." v-model="newMessage" @keyup.enter="sendMessage"> <span class="input-group-btn"> <button class="btn btn-primary btn-sm" id="btn-chat" @click="sendMessage"> Send </button> </span> </div> </template> <script> export default { props: ['user'], data() { return { newMessage: '' } }, methods: { sendMessage() { this.$emit('messagesent', { user: this.user, message: this.newMessage }); this.newMessage = '' } } } </script>
ChatForm component nhận vào props là user đã đc authen. Khi nút send được click, component này sẽ emit một sự kiện
sendMessgage
tới root Vue instance. -
Tiếp theo chúng ta cần khai báo các component con này ở root Vue instance. Mở file
resources/assets/js/app.js
và viết đoạn code sau:// resources/assets/js/app.js require('./bootstrap'); Vue.component('chat-messages', require('./components/ChatMessages.vue')); Vue.component('chat-form', require('./components/ChatForm.vue')); const app = new Vue({ el: '#app', data: { messages: [] }, created() { this.fetchMessages(); }, methods: { fetchMessages() { axios.get('/messages').then(response => { this.messages = response.data; }); }, addMessage(message) { this.messages.push(message); axios.post('/messages', message).then(response => { console.log(response.data); }); } } });
-
Khi Vue instance
created
, chúng ta dùng axios tạo một request GET tớiroute messages
để lấy ra tất cả các messages rồi gán nó vào mảng messages hiển thị trên view. -
Method
addMessage
nhận vào message mà đc emit từChatForm
component, thưc hiệnpush
message đó vào mảngmessages
và thực hiện một request POST tới route mesages.
Broadcasting Message Sent Event
- Để realtime cho ứng dụng, chúng ta cần broadcast (phát sóng) những event (sự kiện). Ở đây chúng ta phát ra sự kiện
MessageSent
khi một người dùng gửi một Message. - Trước tiên ta tạo event MessageSent (trong thư mục app/Events)
php artisan make:event MessageSent
- Class MessageSent phải
implement
interfaceShouldBroadCast
:// app/Events/MessageSent.php 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 MessageSent implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels; /** * User that sent the message * * @var User */ public $user; /** * Message details * * @var Message */ public $message; /** * Create a new event instance. * * @return void */ public function __construct(User $user, Message $message) { $this->user = $user; $this->message = $message; } /** * Get the channels the event should broadcast on. * * @return Channel|array */ public function broadcastOn() { return new PrivateChannel('chat'); } }
- Ở đây chúng ta định nghĩa 2 property pubic là
$user
và$message
, chính là dữ liệu chúng ta sẽ truyền tớichanel
mà chúng ta phát sóng tới.
Note: Những property này phải để là public
- Ta chú ý vào hàm
broadcastOn()
khai báo trong event. Bởi vì ứng dụng của chúng ta là một authenticated-only app, chúng ta sẽ tạo một private channel với tên làChat
mà chỉ những user đã đăng nhập mới có thể kết nối đến. - Cuối cùng, ta phải có chỗ thực hiện hành động phát sóng sự kiện, đó là ở trong hàm
sendMessage()
của controllerChatsController
:// app/Http/Controllers/ChatsController.php //remember to use use App\Events\MessageSent; /** * Persist message to database * * @param Request $request * @return Response */ public function sendMessage(Request $request) { $user = Auth::user(); $message = $user->messages()->create([ 'message' => $request->input('message') ]); broadcast(new MessageSent($user, $message))->toOthers(); return ['status' => 'Message Sent!']; }
- Cũng bởi vì ta tạo một private channel, chỉ những user đăng nhập mới có thể lắng nghe channel
Chat
. Do đó chúng ta cần một cách để authorize rằng chỉ những user hiện tại đang đăng nhập mới có thể lắng nghe kênh. Ta thực hiện ở trong fileroutes/channels.php
// routes/channels.php Broadcast::channel('chat', function ($user) { return Auth::check(); });
- Chúng ta truyền vào hàm channel() tên của kênh và một hàm callback mà trả về true hay false dựa trên trạng thái đăng nhập hiện tại của user.
- Như vậy bây giờ khi message được gửi đi, Sự kiện
MessageSent
sẽ được phát sóng (broadcast) tớiPusher
. Chúng ta sử dụngtoOthers()
để cho phép loại trừ người dùng hiện tại ra khỏi danh sách người nhận phát sóng.
Listening For Message Sent Event
- Một khi sự kiện MessageSent được phát sóng, chúng ta cần lắng nghe sự kiện này để chúng ta cập nhật mảng messages với message mới vừa đc gửi.
- Trong file
resources/assets/js/app.js
chúng ta thêm vào hàmcreated()
ngay sauthis.fetchMessages()
đoạn code sau:// resources/assets/js/app.js Echo.private('chat') .listen('MessageSent', (e) => { this.messages.push({ message: e.message.message, user: e.user }); });
- Vì channel ở đây là
private channel
, chúng tasubscribe
(đăng kí) kênhChat
sử dụng phương thứcprivate()
củaEcho
. Khi đã subscribe kênh, chúng ta sẽ lắng nghe đc sựMessageSent
và mỗi lần có message mới sẽ thực hiện push nó vào mảngmessages
. - Compile các file JavaScript sử dụng Laravel Mix:
npm run dev
- Start ứng dụng:
php artisan serve
- Đến đây ứng dụng của chúng ta đã hoàn tất và giờ đây chúng ta có thể gửi và nhận message với thời gian thực.
Kết luận
Bạn có thể thấy việc xây dựng một ứng dụng realtime với Laravel và Pusher đơn giản như thế nào. Qua bài viết này mình mong các bạn biết được được luồng của việc xây dựng ứng dụng trên, từ việc phát event phía server, phát sóng bằng Pusher đến bên client lắng nghe event với Echo ...Và với Pusher bạn có thể xây dựng bất kì ứng dụng tương tác thời gian thực nào. Vậy thì còn ngại ngần gì mà không tạo một tài khoản Pusher free và tạo nên một ứng dụng tuyệt vời của bạn nào. Tài liệu tham khảo:
All rights reserved