Broadcasting with laravel echo

Giới thiệu

Như các bạn đã biết, ngày nay với việc ứng dụng websocket vào website sẽ khiến trang web trở nên linh động hơn, cải thiện các chức năng cũng như tương tác với người dùng tốt hơn. Vì vậy websocket ngày càng trở nên quang trọng đối với việc phát triển một website. Và sau đây mình xin giới thiệu về boardcasting trong laravel hỗ trợ cho việc tích hợp websocket vào trang web của các bạn

Cài đặt

Đầu tiên bạn phải cài đặt các gói thư viện thông qua npm

"redis": "^2.7.1",
"socket.io": "^2.0.3",
"socket.io-client": "^1.7.1",
"dotenv": "^2.0.0",
"laravel-echo": "^1.3.0",
"laravel-echo-server": "^1.1.0"

Sau đó các bạn chạy npm install để cài đặt các gói này. Cài đặt redis thông qua composer composer require predis/predis

Config

config file .env

BROADCAST_DRIVER=redis
...
ECHO_KEY=9te930q989itok355krkfrs95j92ais01iim3i5sjs1k93g87kt8qdd0ur91
ECHO_HOST=mt.dev
ECHO_AUTH_HOST=http:\\mt.dev
ECHO_AUTH_ENDPOINT="/broadcasting/auth"
ECHO_DATABASE=redis
ECHO_REDIS_HOST=127.0.0.1
ECHO_SQLITE_PATH="/database/laravel-echo-server.sqlite"
ECHO_DEV_MODE=true
ECHO_PROTOCOL=http
ECHO_PORT=9090
ECHO_SSL_CERT_PATH=
ECHO_SSL_KEY_PATH=

config config/app.php các bạn mở comment dòng App\Providers\BroadcastServiceProvider::class, ở mục providers và thêm vào mục aliases

'LRedis' => Illuminate\Support\Facades\Redis::class,

Tạo server

Các bạn chạy lệnh php artisan make:auth để laravel tự sinh các phần để login và authentication nhé. Ở đây, mình sẽ giải thích các luồng dữ liệu. Đầu tiên mình sẽ tạo 1 event chat để lắng nghe sự kiện. Event phải implements ShouldBroadcast

<?php

namespace App\Events;
 
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 ChatEvent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    private $message;
    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct($message)
    {
        $this->message = $message;
    }
 
    /**
     * Get the channels the event should broadcast on.
     *
     * @return Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('chat-room');
    }
    
    // phương thức này định nghĩa dữ liệu sẽ được gửi qua sự kiện của event chat
    public function broadcastWith()
    {
        return [
            'message' => $this->message,
            'user' => auth()->user(),
        ];
    }
}

Sau đó mình tạo một controller để gọi event chat php artisan make:controller ChatController

<?php
 
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Events\ChatEvent;
use LRedis;

class ChatController extends Controller
{
    private $redis;

    public function __construct()
    {
        $this->redis = LRedis::connection();
    }

    protected function chat(Request $request)
    {
        if (!$request->ajax()) {
            return ['status' => false];
        }
        
        event(new ChatEvent($request->get('message')));

        return ['status' => true];
    }
}

Tạo các router tương ứng

Route::post('/chat', 'Common\[email protected]')->name('chat');
Route::get('/get-chat', function () {
    return view('chat.chat');
});

Các bạn thêm vào file channels.php một như sau

Broadcast::channel('chat-room', function ($user) {
    // athorization for user can listener event
    return true; // all user can chat
});

Ở đây, file channels.php này sẽ xác thực những user nào được phép join vào kênh chat-room cũng như được lắng nghe hay tạo sự kiện trên đó. Tạo file server.js theo đường dẫn Node/server.js

require("dotenv").config({ path: '../.env' })

var Echo = require("laravel-echo-server")

/**
 * The Laravel Echo Server options.
 */

var options = {
    "appKey": process.env.ECHO_KEY,
    "authHost": process.env.ECHO_AUTH_HOST,
    "authEndpoint": process.env.ECHO_AUTH_ENDPOINT,
    "database": process.env.ECHO_DATABASE,
    "databaseConfig": {
        "redis": {
            "port": 6379,
            "host": process.env.ECHO_REDIS_HOST
        },
        "sqlite": {
            "databasePath": process.env.ECHO_SQLITE_PATH
        }
    },
    "devMode": process.env.ECHO_DEV_MODE,
    "host": process.env.ECHO_HOST,
    "protocol": process.env.ECHO_PROTOCOL,
    "port": process.env.ECHO_PORT,
    "referrers": [],
    "socketio": {},
    "sslCertPath": process.env.ECHO_SSL_CERT_PATH,
    "sslKeyPath": process.env.ECHO_SSL_KEY_PATH,
    "verifyAuthPath": true,
    "verifyAuthServer": true
}

/**
 * Run the Laravel Echo Server.
 */
Echo.run(options)

tiếp theo mình sẽ tạo view chat box chat/chat.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-10 col-md-offset-1">
            <div class="panel panel-primary">
                <div class="panel-heading">Chat Box</div>
                <div class="panel-body" style="height: 350px; overflow-y: auto" id="content">
                </div>
                <div class="panel-footer">
                    <div class="input-group">
                        <input type="text" class="form-control" id="input" placeholder="Enter your message...">
                        <span class="input-group-btn">
                            <button class="btn btn-default" type="button" id="send">Send</button>
                        </span>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Sữa lại file bootstrap.js trong assests

window.$ = window.jQuery = require('jquery')

require('bootstrap-sass')
let token = document.head.querySelector('meta[name="csrf-token"]')
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': token.content
    }
})

import Echo from 'laravel-echo'

window.io = require('socket.io-client')

window.Echo = new Echo({
    namespace: 'App.Events',
    broadcaster: 'socket.io',
    host: `${window.location.hostname}:9090`
})

Tạo 1 file js có tên chat.js

import Echo from 'laravel-echo'
let echo = new Echo({
    broadcaster: 'socket.io',
    host: window.location.hostname + ':9090'
})
 
$('#send').on('click', function () {
    let message = $('#input').val()
    $('#input').val('')

    if (message != '') {
        $.post('chat' ,  { message: message })
    }
})

echo.private('chat-room')
    .listen('ChatEvent', (e) => {
        $('#content').append(`<div class="well">${ e.user.name + ': ' + e.message }</div>`)
})

cuối cùng là biên dịch file js

 mix.js('resources/assets/js/app.js', 'public/js')
    .js('resources/assets/js/chat.js', 'public/js')
    .sass('resources/assets/sass/app.scss', 'public/css');

Và đừng quên khai báo <script src="{{ asset('js/chat.js') }}"></script> vào file layout/app.blade.php Cuối cùng bạn chạy lệnh node Node/server.js

Giải thích

Khi bạn nhấn nút send thì chúng ta sẽ thực hiện một phương thức post lên server với controller chatevent. Sau khi gọi phương thức chat chúng ta sẽ khởi tạo một event với dữ liệu truyền qua là message chúng ta gửi đến. Và trong event Chat với phương thức broadcastWith sẽ định nghĩa dữ liệu được truyền qua server node. Đến đây, việc còn lại là ở client, laravel echo sẽ lắng nghe sự kiện ở channel với class name tương ứng . Chúc các bạn code vui vẻ . nguồn tham khảo : https://laravel.com/docs/5.5/broadcasting


All Rights Reserved