+3

RealTime Chat Application using Laravel & Vuejs [P2]

Chat real-time với 1 user

Đầu tiên để ta cần khai báo 1 route để xem ta muốn chat với user nào

    Route::get('/chat/{id}', 'ChatController@show')->name('chat.show');
    // ChatController
    public function show($userId)
    {
        $friend = User::find($userId);
        return view('chat-room.show', compact('friend'));
    }
    
    // show.blade.php
    <meta name="userId" content="{{ Auth::check() ? Auth::user()->id : 'null' }}">
    <meta name="friendId" content="{{ $friend->id }}">
    <div class="col-md-12" style="padding: 0" id="box-private">
        <div class="panel panel-primary">
            <div class="panel-heading friend-name">
                <span class="panel-title">{{$friend->name}}</span>
            </div>
        </div>
    </div>

Get history

Trên đây chỉ là giao diện bên ngoài thôi. Ta sẽ cần phải lấy lịch sử chat & làm form chat nữa

    Route::post('/history/{chatRoomId}', 'ChatRoomController@history')->name('chat-room.history');
    // ChatController
    public function history(Request $request, $chatRoomId)
    {
        return ChatRoom::where(function ($query) use ($chatRoomId) {
            $query->where('user_id', Auth::user()->id)
                ->where('friend_id', $chatRoomId);
        })->orWhere(function ($query) use ($chatRoomId) {
            $query->where('friend_id', Auth::user()->id)
                ->where('user_id', $chatRoomId);
        })->orderBy('created_at', 'asc')
            ->with(['user'])
            ->get();
    }

Ở đây, hàm history mình sẽ lấy tất cả record trong bảng chat_room của A gửi cho B hoặc B gửi cho A. Vậy nên mới có chỗ condition orWhere Tất nhiên hàm này là 1 API sẽ được gọi qua axios của Vue.

    // app.js
    const app = new Vue({
        el: '#app',
        data: {
            chats: '',
        },
        created() {
            const friendId = $('meta[name="friendId"]').attr('content');

            if (friendId != undefined) {
                axios.post('/chat-room/history/' + friendId).then((response) => {
                    this.chats = response.data;
                });
            }
        }
    });

Ở đoạn code trên, khi app được khởi tạo ta sẽ gọi đến api history để lấy lịch sử chat của user đang login và người bạn đang select & được lưu vào biến chats. Giở ta sẽ tạo component để hiển thị form chat

Create Form chat

    <template>
        <div class="box box-primary direct-chat direct-chat-primary">
            <div class="direct-chat-messager">
                <div class="direct-chat-msg clearfix" v-for="msg in chats" :class="[msg.user_id == user_id ? 'right' : '']">
                    <div class="direct-chat-info clearfix"></div>
                    <img class="direct-chat-img" :src="msg.user.avatar" alt="Message User Image">
                    <div class="direct-chat-text">{{msg.chat}}</div>
                </div>
            </div>
            <div class="box-footer">
                <chat-room-composer :user_id="user_id" :friend_id="friend_id" :history="chats"></chat-room-composer>
            </div>
        </div>
    </template>

    <script>
        export default {
            props: ['chats', 'user_id', 'friend_id'],
        }
    </script>

Ở trên ta đã hiển thị lịch sử chat của 2 user và ở dưới ta sẽ cần 1 form để nhập đoạn chat mới. Bạn có thể để í là ta sẽ dùng thêm 1 component nữa chat-room-composer để làm việc này

<template>
    <div class="input-group">
        <input type="text" class="form-control" :disabled="is_disabled" v-model="chat" v-on:keyup.enter="sendChat" autofocus>
        <!--<div class="input-group-append">-->
        <!--<button class="btn btn-primary" type="button" v-on:click="sendChat">Send</button>-->
        <!--</div>-->
    </div>
</template>
<script>
    export default {
        props: ['user_id', 'friend_id', 'history'],
        data() {
            return {
                chat: '',
                is_disabled: false,
            }
        },
        methods: {
            sendChat: function (e) {
                let data = {
                    chat: this.chat,
                    user_id: this.user_id,
                    friend_id: this.friend_id,
                    user: {
                        avatar: $('meta[name=user_avatar]').attr('content'),
                    },
                    is_sendmail: !(_.find(this.onlineusers, {id: this.friend_id})),
                };

                axios.post('/chat-room/sendChat', data).then((response) => {
                    this.history.push(data);
                    this.chat = '';
                    this.is_disabled = false;
                    $("html, body").animate({scrollTop: $(document).height()}, 100);
                }).catch((error) => {
                    this.is_disabled = false;
                });
            },
        }
    }
</script>

Ở component chat-composer ta có nhận 3 biến từ bên ngoài truyền vào. mục đích là để tạo data khi user submit form chat, sau đó sẽ thêm nội dung đoạn chat đó vào phần lịch sử của 2 user

Và api lưu đoạn chat mới của user sẽ là /chat-room/sendChat

// ChatRoomController.php
public function sendChat(Request $request)
{
    $chatRoom = ChatRoom::create([
        'user_id' => $request->user_id,
        'friend_id' => $request->friend_id,
        'chat' => $request->chat
    ]);

    $chatRoom->load('user');

    broadcast(new ChatRoomBroadCast($chatRoom))->toOthers();
}

Khi user nhập đoạn chat của họ vào form & ấn nút submit. Ta sẽ gọi 1 api lên server & data là nội dung đoạn chat, user id của 2 user. Sau đó trên server ta sẽ lưu toàn bộ data đó vào database

broadcast(new ChatRoomBroadCast($chatRoom))->toOthers();

Hàm này sẽ bắn nội dung chat đến cho những user khác thông qua broadcast của laravel

Phần tiếp theo mình sẽ hướng dẫn các bạn config broadcast để có thể chat real-time giữa các user với nhau cũng như ta sẽ làm tính năng thông báo khi 1 user nào đó online


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.