HomeOur Team
Realtime Chat App Using Laravel & Pusher
Solutions
Realtime Chat App Using Laravel & Pusher
chung.nguyen1
chung.nguyen1
October 26, 2020
4 min

Cài đặt Laravel

Chúng ta sẽ bắt đầu bằng việc cài đặt dự án Laravel. Mở terminal và chạy dòng lệnh bên dưới

git clone https://github.com/laravel/laravel chat-app-laravel-pusher

Câu lệnh trên sẽ clone 1 project có tên chat-app-laravel-pusher về máy của bạn

Trước khi chúng ta sử dụng Laravel event broadcasting, chúng ta cần đăng ký App\Providers\BroadcastServiceProvider. Mở file config/app.php và bỏ comment dòng App\Providers\BroadcastServiceProvider trong mảng providers

// App\Providers\BroadcastServiceProvider

Để sử dụng Pusher chúng ta cần update BROADCAST_DRIVER trong file .env

// .env

BROADCAST_DRIVER=pusher

Mặc định Laravel đã hỗ trợ Pusher nhưng chúng ta vẫn cần cài đặt Pusher PHP SDK. Để cài đặt Pusher PHP SDK thông qua composer

composer require pusher/pusher-php-server

Cài đặt Pusher

Tạo tài khoản Pusher tại trang https://pusher.com/signup sau đó đăng nhập vào tạo Pusher App

Tiếp theo là điền thông tin Pusher App vào file config/broadcasting.php. Nếu để ý bạn sẽ thấy rằng config/broadcasting.php sẽ sử dụng các biến ở trong 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 cần chỉnh sửa 1 chút để ứng dụng có thể hoạt động

'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,
    ],
],

Sau đó update file .env bằng các thông tin bên trong Pusher App

// .env

PUSHER_APP_ID=xxxxxx
PUSHER_APP_KEY=xxxxxxxxxxxxxxxxxxxx
PUSHER_APP_SECRET=xxxxxxxxxxxxxxxxxxxx
PUSHER_CLUSTER=xx

Giờ chúng ta đã cài đặt xong phần back-end, giờ sẽ chuyển qua cài đặt bên phía front-end. Ở front-end chúng ta cần 1 số frameworkslibraries như Bootstrap, VuejsAxios.

Chúng ta sẽ sử dụng Laravel Mix để biên dịch CSSJavascript. Nhưng trước tiên chúng ta phải cài các dependencies thông qua npm

npm install

Để subscribelisten các events, chúng ta sẽ sử dụng Laravel Echo. Đây là 1 thư viện Javascript để subscribe channellisten events được phát đi bởi Laravel. Để cài đặt, chúng ta chạy lệnh

npm install --save laravel-echo pusher-js

Sau khi cài đặt xong, chúng ta cần config Pusher cho Laravel Echo ở file resources/assets/js/bootstrap.js

// resources/assets/js/bootstrap.js

import Echo from "laravel-echo"

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'xxxxxxxxxxxxxxxxxxxx',
    cluster: 'eu',
    encrypted: true
});

Authenticating Users

Ứng dụng chat của chúng ta yêu cầu người dùng phải login trước khi họ bắt đầu chat. Vì thế chúng ta cần 1 hệ thống xác thực

composer require laravel/ui
php artisan ui vue --auth

Câu lệnh này sẽ tạo cho chúng ta các route, viewcontroller cần thiết cho 1 hệ thống xác thực người dùng.

Trước khi chúng ta tạo user, chúng ta cần migration bảng users. Để làm được việc này chúng ta cần setup database. Mở file .env và điền thông tin database

// .env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel-chat
DB_USERNAME=root
DB_PASSWORD=root

Giờ bạn có thể tạo bảng cho database của mình bằng câu lệnh

php artisan migrate

Message Model and Migration

Tạo 1 Message model với migration file bằng cách chạy câu lệnh:

php artisan make:model Message -m

Mở Message model và thêm đoạn code sau:

// app/Message.php

/**
 * Fields that are mass assignable
 *
 * @var array
 */
protected $fillable = ['message'];

Trong thư mục database/migrations, mở messages table migration và update nó

Schema::create('messages', function (Blueprint $table) {
    $table->id();
    $table->integer('user_id')->unsigned();
    $table->text('message');
    $table->timestamps();
});

Sau đó chúng ta chạy migration

php artisan migrate

User To Message Relationship

Chúng ta cần cài đặt quan hệ giữa bảng users và bảng messages. Một user có thể gửi nhiều message trong khi 1 message được gửi bởi 1 user. Nên quan hệ giữa bảng usersmessage là quan hệ one to many. Để định nghĩa quan hệ, thêm đoạn code sau vào User model:

// app/User.php

/**
 * A user can have many messages
 *
 * @return \Illuminate\Database\Eloquent\Relations\HasMany
 */
public function messages()
{
  return $this->hasMany(Message::class);
}

Tiếp theo, chúng ta định nghĩa quan hệ cho Message model:

// 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

Để tạo routes cho dự án, mở file routes/web.php và cập nhật như sau:

// routes/web.php

Auth::routes();

Route::get('/', 'ChatsController@index');
Route::get('messages', 'ChatsController@fetchMessages');
Route::post('messages', 'ChatsController@sendMessage');

Trang chủ sẽ hiển thị chat messages and 1 input để nhập new messages. Một GET messages route sẽ lấy tất cả các chat messages và POST messages route sẽ được sử dụng để gửi message mới.

Chú ý: Khi chúng ta bỏ /home route, bạn phải update thuộc tính redirectTo ở cả 2 controller app/Http/Controllers/Auth/LoginController.phpapp/Http/Controllers/Auth/RegisterController.php thành

protected $redirectTo = '/';

ChatsController

Để xử lý logic chat của người dùng chúng ta cần tạo ChatsController với câu lệnh sau:

php artisan make:controller ChatsController

Mở file app/Http/Controllers/ChatsController.php và update code như 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!'];
}

Sử dụng auth middleware trong ChatsController's __contruct() để tất cả các method bên trong controller sẽ chỉ được truy cập với user đã đăng nhập. Sau đó method index() sẽ trả về 1 file view. Method fetchMessages() trả về một JSON của tất cả các messages của người dùng. Cuối cùng method sendMessage() sẽ lưu message tới database và trả về trạng thái của message.

Creating The Chat App View

Tạo 1 file mới resources/views/chat.blade.php và copy đoạn code sau:

@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

Chú ý chúng ta có 1 vài thuộc tính của Vue components bên trong view. chat-messages sẽ hiển thị khung chat và chat-form sẽ cung cấp 1 input và 1 button để gửi message

Trước khi chúng ta tạo Vue component thì chúng ta sẽ thêm 1 chút styles cho giao diện chat. Mở file resources/views/layouts/app.blade.php và thêm các đoạn code bên dưới:

<style>
  .chat {
    list-style: none;
    margin: 0;
    padding: 0;
  }

  .chat li {
    margin-bottom: 10px;
    padding-bottom: 5px;
    border-bottom: 1px dotted #B3A9A9;
  }

  .chat li .chat-body p {
    margin: 0;
    color: #777777;
  }

  .panel-body {
    overflow-y: scroll;
    height: 350px;
  }

  ::-webkit-scrollbar-track {
    -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
    background-color: #F5F5F5;
  }

  ::-webkit-scrollbar {
    width: 12px;
    background-color: #F5F5F5;
  }

  ::-webkit-scrollbar-thumb {
    -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
    background-color: #555;
  }
</style>

Để ý file resources/assets/js/bootstrap.js, bạn sẽ nhận ra Laravel có cài đặt 1 số front-end dependencies (jQuery, Bootstrap, Lodash, Vue, Axios, Echo) vì thế chúng ta có thể sử dụng Vue mà không cần cài đặt thêm bất cứ thứ gì.

Tạo 1 file ChatMessages.vue bên trong thư mục resources/assets/js/components và chỉnh sửa nó:

// 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 chấp nhận 1 mảng thuộc tính messages, lặp mảng messages và hiển thị tên của user và tin nhắn của họ đã gửi.

Tiếp theo, tạo file ChatForm.vue bên trong thư mục resources/assets/js/components và update code vào file đó:

// 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 hiển thị 1 inputsend button. Nó nhận 1 thuộc tính user, nó cũng chứa dữ liệu newMesssage được liên kết tới ô input. Khi send button được click hoặc được submit bằng phím Enter trên ô input, method sendMessage() được gọi. Method sendMessage() sẽ phát ra 1 messagesent event và sau đó xoá ô input.

Tiếp theo, chúng ta cần đăng ký component cho Vue instance. Mở file resources/assets/js/app.js và update code bên dưới:

// 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 được tạo, sử dụng Axios, chúng ta sẽ tạo 1 GET request tới messsages routes và lấy tất cả dữ liệu của messages sau đó trả về 1 mảng messages để hiển thị dưới chat view. Method addMessage() nhận vào 1 message và được phát đi từ ChatForm component, đẩy message đó vào mảng messages và tạo 1 POST request tới messages routes.

Broadcasting Message Sent Event

Để ứng dụng có thể chạy realtime, chúng ta cần broadcast 1 vài event dựa trên 1 số hoạt động. Trong trường hợp của chúng ta, chúng ta sẽ broadcast MessageSent event khi người dùng gửi 1 tin nhắn. Đâù tiên, chúng ta cần tạo 1 event với tên MessageSent.

php artisan make:event MessageSent

Câu lệnh này sẽ tạo 1 MessageSent event class bên trong thư mục app/Events. Class này phải implement ShouldBroadcast interface. Class MessageSent sẽ trông như sau:

// 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');
    }
}

Khi ứng dụng chat của chúng ta chỉ dành cho người dùng đã đăng nhập, chúng ta tạo 1 private channel gọi là Chat, để cho người dùng đã đăng nhập sẽ có thể kết nối. Sử dụng PrivateChannel, Laravel sẽ đủ thông minh để biết chúng ta tạo 1 private channel, nên không cần prefix cho channel name với private- (giống như Pusher). Laravel sẽ thêm private- prefix.

Tiếp theo, chúng ta cần update sendMessage() của ChatsController để broadcast MessageSent event.

// 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!'];
}

Khi chúng ta tạo 1 private channel, chỉ user đã đăng nhập mới có thể lắng nghe chat channel. Nên chúng ta cần 1 cách để xác thực user đang đăng nhập có thể thực sự lắng nghe channel. Để làm được điều này chúng ta cần update file routes/channels.php:

// routes/channels.php

Broadcast::channel('chat', function ($user) {
  return Auth::check();
});

Chúng ta truyền tới channel(), tên channel của chúng ta và 1 callback function. Function này sẽ trả về true hoặc false tuỳ thuộc vào việc user hiện tại có đang đăng nhập hay không.

Khi 1 message được gửi đi, event MessageSent sẽ được broadcast tới Pusher. Chúng ta sử dụng toOthers() cho phép chúng ta loại trừ người dùng hiện tại khỏi người nhận của chương trình phát sóng.

Listening For Message Sent Event

Khi event MessageSent được broadcast, chúng ta cần lắng nghe event này, nên chúng ta có thể update chat box khi có tin nhắn mới. Chúng ta có thể làm điều này bằng cách update đ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
    });
  });

Chúng ta subscribe chat channel sử dụng Echo's private() khi channel là private channel. Khi subscribe, chúng ta lắng nghe MessageSent và update mảng chat messages khi có tin nhắn mới.

Trước khi chạy ứng dụng, chúng ta cần compile Javascript file sử dụng Laravel Mix:

npm run dev

Bây giờ chúng ta bắt đầu chạy ứng dụng bằng cách chạy lệnh:

php artisan serve

Tags

#laravel#realtime#chat#102020
chung.nguyen1

chung.nguyen1

Developer

Related Posts

Sign In with Apple - Backend (Java)
Sign In with Apple - Backend (Java)
October 29, 2020
3 min
© 2021, All Rights Reserved.

Quick Links

HomeOur Team

Social Media