- Published on
Xây dựng chatbox với Laravel Livewire phần 2
Mục lục
- Giới thiệu
- Cài đặt Broadcasting để gửi đi sự kiện
- Cấu hình Pusher
- Tạo sự kiện gửi tin nhắn
- Bắt sự kiện tin nhắn mới bằng Laravel Echo
Giới thiệu
Trong phần trước chúng ta đã làm quen với Livewire và cũng biết cách nó tạo ra tương tác gia front-end và back-end như thế nào, trong bài viết này, mình sẽ giới thiệu đến các bạn cách sử dụng Broadcasting trong Laravel và Pusher - một dịch vụ socket miễn phí mà bạn có thể sử dụng cho các dự án của mình để thực hiện gửi thông báo realtime đến các client hiện tại:
Để thực hiện được hướng dẫn dưới đây, bạn phải thực hiện hoàn tất những gì ở Phần 1 - Cài đặt livewire và tạo các model.
Cài đặt Broadcasting để gửi đi sự kiện
Để Laravel có thể phát đi các sự kiện đến người dùng khác (broadcasting) thì bạn cần đăng ký service của nó bên trong config/app.php
bạn hãy vào file này và bỏ //
đầu dòng đi (uncomment)
// App\Providers\BroadcastServiceProvider::class,
👇
App\Providers\BroadcastServiceProvider::class,
Sau đó, bạn cần thay đổi BROADCAST_DRIVER
mặc định trong .env
thành pusher
BROADCAST_DRIVER=pusher
Tuy rằng Laravel đã hỗ trợ sẵn các cấu hình cho Pusher, tuy nhiên ta vẫn cần cài đặt Pusher SDK cho dự án của mình, hãy chạy lệnh sau:
$ composer require pusher/pusher-php-server
Cấu hình Pusher
Trước khi cấu hình Pusher, bạn hãy tạ một tài khoản miễn phí tại https://dashboard.pusher.com/accounts/sign_up sau đó đăng nhập vào bảng điều khiển.
Sau khi đăng nhập thành công, bạn hãy ấn vào Channel và tạo một App mới (gói sandbox miễn phí). Bạn chỉ cần đặt tên cho ứng dụng và cấu hình máy chủ sử dụng (nên chọn Singapore)
- Front-end engine: Laravel Echo
- Back-end engine: Laravel
Sau khi tạo thành công, bạn sẽ có được các thông tin bao gồm APP_ID, APP_Key, ... Hãy sao chép nó vào bên trong file .env
của bạn:
PUSHER_APP_ID=12839xx
PUSHER_APP_KEY=2dfa7dcc3c2b509axxxx
PUSHER_APP_SECRET=04c798031ade6381xxxx
PUSHER_APP_CLUSTER=ap1
Tạo sự kiện gửi tin nhắn
Để thực hiện gửi tin nhắn thời gian thực, bạn cần tạo ra một sự kiện, ví dụ ở đây là SendMessage
mỗi khi một tin nhắn gửi đến hệ thống, nó sẽ tự động chạy sự kiện này và thông báo cho các kênh (channel) đảm nhận việc xử lý nó.
Để tạo một Event trên Larave ta dùng lệnh:
$ php artisan make:event MessageSent
Một class sự kiện sẽ được tạo ra bên trong thư mục app\Events
hãy mở thư mục này ra và bạn sẽ thấy file MessageSent
bạn cần thông báo cho Laravel biết rằng sự kiện này sẽ thực hiện Broadcast bằng cách implements ShouldBroadcast
<?php
namespace App\Events;
...
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class MessageSent implements ShouldBroadcast
{
...
}
Nội dung đầy đủ của Event như sau:
<?php
namespace App\Events;
use App\Models\Message;
use App\Models\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class MessageSent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $user;
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 \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('chat');
}
}
Bây giờ bạn cần cho Laravel biết, khi nào thì sự kiện này diễn ra, hãy mở file Chat.php
lên và thêm vào hàm send
public function send()
{
$user = Auth::user();
$message = $user->messages()->create([
'message' => $this->message
]);
$this->reset();
broadcast(new MessageSent($user, $message))->toOthers();
}
Bây giờ hãy thử đăng nhập vào Dashboard của Pusher và thực hiện gửi tin nhắn thử. Nếu tin nhắn của bạn được gửi thành công đến Pusher, Total messages sent today của bạn sẽ tăng lên:
Bắt sự kiện tin nhắn mới bằng Laravel Echo
Laravel Echo là một thư viện JS hỗ trợ việc bắt sự kiện socket. Để cài đặt Laravel Echo các bạn cần chạy lệnh sau:
$ npm install --save laravel-echo pusher-js
Mở file resources/js/bootstrap.js
lên và uncomment (bỏ //
) cấu hình cho Echo:
/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/
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,
})
Thực hiện lại lệnh build JS:
$ npm run dev
Chèn JS đã được build vào views/layouts/app.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() }}" />
<script src="{{ asset('js/app.js') }}" defer></script>
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap"
/>
<!-- Styles -->
@livewireStyles
</head>
<body class="font-sans antialiased">
{{ $slot }} @livewireScripts
</body>
</html>
Bỏ wire:poll
ở view resources/views/livewire/app.blade.php
đi nhé:
<div>
@foreach($messages as $message)
<div class="mb-1">{{ $message->user->name }}: {{ $message->message }}</div>
@endforeach
<form wire:submit.prevent="send">
<input type="text" wire:model="message" />
<button type="submit">Send</button>
</form>
</div>
Cấu hình sự kiện echo trong Livewire controller app/Http/Livewire/App.php
<?php
namespace App\Http\Livewire;
use App\Events\MessageSent;
use App\Models\Message;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;
class App extends Component
{
public $message;
public $messages;
protected $listeners = ['echo:chat,MessageSent' => 'reloadMessages'];
public function render()
{
$this->messages = Message::all();
return view('livewire.app');
}
public function send()
{
$user = Auth::user();
$message = $user->messages()->create([
'message' => $this->message
]);
$this->reset();
broadcast(new MessageSent($user, $message))->toOthers();
}
public function reloadMessages($event)
{
$this->messages->concat($event['message']);
}
}
Như bạn thấy ở đây, khi nhận được sự kiện MessageSent
từ kênh chat
Livewire sẽ cần thực hiện hàm reloadMessages
Trong hàm reloadMessages
mình sẽ lấy tin nhắn vừa nhận được (trong $event
có hai phần tử là user và message mình chỉ cần message) và thêm nó vào mảng $this->messages
của component.
Khi dữ liệu của component được cập nhât, view cũng sẽ tự động được reload lại mà không cần wire:poll
nữa
Nếu bạn có thắc mắc gì trong quá trình làm, đừng ngại comment bên dưới để mình hỗ trợ nhé.