Membuat Aplikasi Chatting Menggunakan Laravel 6 & Vue.js

Membuat Aplikasi Chatting Menggunakan Laravel 6 & Vue.js

Pendahuluan

Fasilitas komunikasi atau chatting antar pengguna menjadi fitur yang menarik dari sebuah aplikasi, terlebih aplikasi yang sifatnya transaksi jual beli guna menunjang kecepatan dan ketepatan informasi antar kedua belah pihak. Artikel kali ini akan membahas bagaimana membuat aplikasi chatting menggunakan Laravel 6 dan Vue.js secara realtime.

Beruntungnya Laravel memudahkan kita untuk membuat aplikasi chatting atau aplikasi apapun itu yang sifatnya relatime karena telah disediakannya fitur broadcasting event yang memungkinkan developer untuk mengirimkan event antara server side dan client side.

Skenarionya adalah user yang mengirimkan pesan melalui kotak chat yang disediakan akan disimpan ke database kemudian di-broadcast ke semua pengguna selain dirinya sendiri. Adapun data yang tersimpan di database digunakan sebagai log apabila user ingin melihat riwayat percakapannya yang telah lampau.

Baca Juga: Aplikasi E-Commerce Laravel 6 #11: Dashboard & Management Orders

Persiapan Aplikasi Chatting Laravel

Ada beberapa hal yang harus disiapkan, diantaranya adalah Laravel 6 fresh install agar kita benar-benar memulainya dari awal dan yang kedua adalah API dari pihak ketiga sebagai sarana untuk men-delivery setiap event secara realtime. Layanan yang akan digunakan kali ini adalah Pusher karena sudah bundle dengan Laravel secara default, tapi kita juga bisa menggunakan opsi lainnya seperti Socket.io (red: next time saya akan menulis versi Socket.io).

Install Laravel dengan command

composer create-project --prefer-dist laravel/laravel dw-aplikasi-chatting

Buka file .env dan modifikasi beberapa bagian berikut

BROADCAST_DRIVER=pusher

DB_DATABASE=NAMA DATABASE ANDA
DB_USERNAME=USERNAME DATABASE ANDA
DB_PASSWORD=PASSWORD DATABASE ANDA

PUSHER_APP_ID=ID PUSHER ANDA
PUSHER_APP_KEY=KEY PUSHER ANDA
PUSHER_APP_SECRET=SECRET PUSHER ANDA
PUSHER_APP_CLUSTER=CLUSER PUSHER ANDA

Note: 8 line di atas sudah tersedia, kamu hanya perlu menyesuaikan value-nya kecuali BROADCAST_DRIVER.

Lalu sebuah pertanyaan membenak, bagaimana cara mendapatkan akun atau key dari Pusher? Jawabannya adalah dengan membaca artikel Aplikasi Laundry (Laravel 5.8 - Vue.js - SPA) #8: Push Notification Expenses, tepatnya pada sub-bab Register an Account In Pusher karena di materi tersebut sudah ada panduan cara membuat account sampai membuat channel.

Kemudian install package dari Pusher dengan command.

composer require pusher/pusher-php-server

Persiapan terakhir adalah buka file config/app.php dan cari code berikut untuk di-uncomment agar kita bisa menggunakan route channels.

App\Providers\BroadcastServiceProvider

Generate Database With Migration

Sebelum melangkah pada fungsi apa saja yang dibutuhkan, maka database sebagai media penyimpanan log pesan maupun data user akan kita kerjakan terlebih dahulu, pada command line jalankan command berikut.

php artisan make:model Message -m 

Buka file migration yang baru saja di-generate, kemudian modifikasi menjadi.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateMessagesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('messages', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->integer('user_id')->unsigned();
            $table->text('message');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('messages');
    }
}

Adapun table users sudah secara default disediakan oleh Laravel, jadi execute semua migration yang ada dengan command

php artisan migrate

Kemudian buat jenis relasinya menggunakan Eloquent, buka file app/Message.php dan ubah menjadi

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Message extends Model
{
    protected $fillable = ['message']; //MENGIZINKAN MENYIMPAN DATA KE COLUMN MESSAGE

    //RELASI KE TABLE USER UNTUK MENGAMBIL SIAPA PEMILIK PESAN TERSEBUT
    public function user()
    {
        //KITA GUNAKAN BELONGSTO KARENA USER BERTINDAK SEBAGAI DATA INDUK
        return $this->belongsTo(User::class);
    }
}

Relasi dari sisi user, buka file app/User.php dan tambahkan method

//KARENA USER BISA MEMILIKI LEBIH DARI 1 PESAN, MAKA KITA GUNAKAN hasMany
public function messages()
{
    return $this->hasMany(Message::class);
}

Membuat Halaman Login di Laravel 6

Sebagaimana yang kita ketahui bersama bahwa sejak Laravel 6 diluncurkan, artisan command untuk men-generate fitur authentication tidak lagi dibawa oleh Laravel secara default, maka kita perlu untuk menambahkannya secara manual dengan meng-install package berikut

composer require laravel/ui --dev

Adapun fitur auto generate-nya berubah menjadi.

php artisan ui vue --auth

Seperti biasa secara otomatis fungsi authentication dan registration akan dibuat dan bisa digunakan secara sempurna. Sentuhan terakhir adalah buka file app/Providers/RouteServiceProvider.php dan ganti value dari public const HOME = '/home'; menjadi public const HOME = '/'; agar user yang berhasil login otomatis di-direct ke halaman /.

Baca Juga: Mengenal Widget Flutter #5: Membuat Form Login

Aplikasi Chatting Laravel 6 & Vue.js

Tibalah saatnya kita untuk mengerjakan halaman serta fungsi untuk broadcast event secara realtime-nya, buat controller baru dengan command.

php artisan make:controller ChatController

Buka controller baru tersebut dan tambahkan method index()

public function index()
{
    return view('chat');
}

Kemudian buat file chat.blade.php di dalam folder resources/views dan tambahkan kerangka HTML berikut

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row">
            <div class="col-md-12 col-md-offset-2">
                <div class="card">
                    <div class="card-header">
                        <h3 class="card-title">Pesan Antar Pengguna</h3>
                    </div>

                    <div class="card-body" ref="scrollParent">
                        <dw-messages :messages="messages"></dw-messages>
                    </div>
                    <div class="card-footer">
                        <dw-form
                            v-on:sent="addMessage"
                            :user="{{ Auth::user() }}"
                        ></dw-form>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

Penjelasan: Jadi terdapat dua buah custom tag yakni <dw-messages></dw-messages> dimana tag ini adalah component dari Vue.js yang nantinya akan dibuat, dimana tag ini berfungsi untuk menampilkan semua pesan yang ada. Tag lainnya adalah <dw-form></dw-form> yang bertindak sebagai form untuk mengetikkan dan mengirimkan pesan.

Disebabkan kedua tag tersebut adalah component dari Vue.js, maka buat file Messages.vue di dalam folder resources/js/components dan tambahkan code berikut

<template>
    <ul class="chat">
        <!-- DATA TERSEBUT BERUPA ARRAY YANG BERISI PESAN -->
        <!-- KITA LOOPING DATA TERSEBUT UNTUK MENAMPILKAN SEMUA ISI PESAN DAN PENGIRIM -->
        <li class="left clearfix" v-for="(message, index) in messages" :key="message + '-' + index">
            <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 {
        //INGAT TAG :message="" DI CODE SEBELUMNYA? CODE TERSEBUT DISEBUT SEBAGIA PROPS
        //GUNANYA UNTUK MENGIRIMKAN DATA KE COMPONENT YANG DITUJU
        //DALAM HAL INI KITA MENGIRIMKAN DATA DENGAN PROPS MESSAGE KE COMPONENT MESSAGES.VUE
        props: ['messages']
    }
</script>

Component selanjutnya adalah Form.vue di dalam folder yang sama dan tambahkan code berikut

<template>
    <div class="input-group mb-3">
      
      	<!-- INPUTAN INI AKAN KETRIGGER JIKA TOMBOL ENTER DITEKAN -->
        <input type="text" name="message" class="form-control" placeholder="Kirim Pesan..." v-model="message" @keyup.enter="submit">
        <div class="input-group-append">
          
          	<!-- ATAU JIKA TOMBOL SEND DI KLIK -->
            <button class="btn btn-primary btn-sm" id="btn-chat" @click="submit">
                Send
            </button>
        </div>
    </div>
</template>

<script>
    export default {
        //COMPONENT INI JUGA MEMINTA DATA USER YANG AKAN MENGIRIMKAN PESAN
        //DARI CODE SEBELUMNYA KITA JUGA MENGIRIMKAN PROPS BERUPA DATA USER YANG SEDANG LOGIN
        props: ['user'],
        data() {
            return {
                //VALUE DEFAULT DARI INPUTAN KOSONG
                message: ''
            }
        },

        methods: {
            //JIKA ENTER ATAU TOMBOL DIKLIK MAKA FUNGSI INI AKAN DIJALANKAN
            submit() {
                //MENGIRIMKAN EMIT KE PENGGUNA COMPONENT INI UNTUK KEMUDIAN DATANYA DIPROSES PADA FILE LAINNYA
                //EMITNYA BERNAMA SENT, DAN DATA YANG DIKIRIM ADALAH PESAN DAN USER PEMILIKNYA
                this.$emit('sent', {
                    user: this.user,
                    message: this.message
                });

                //INPUTAN UNTUK MENGIRIM PESAN DIKOSONGKAN LAGI
                this.message = ''
            }
        }    
    }
</script>

Kedua component di atas tidak akan berfungsi jika tidak didaftarkan sebagai sebuah components, maka buka file resources/js/app.js dan modifikasi menjadi

require('./bootstrap');

import Vue from 'vue';

//IMPORT KEDUA COMPONENT TERSEBUT
import Messages from './components/Messages.vue'
import Form from './components/Form.vue'

//REGISTER COMPONENT TERSEBUT SECARA GLOBAL
//PARAMETER PERTAMA ADALAH CUSTOM TAG YANG DIINGINKAN, PARAMETER KEDUA ADALAH NAMA COMPONENTNYA
Vue.component('dw-messages', Messages);
Vue.component('dw-form', Form);

const app = new Vue({
    el: '#app',

    data: {
        //VARIABLE UNTUK MENAMPUNG DATA PESAN
        messages: []
    },

    //KETIKA FILE INI DI-LOAD ATAU AKAN DI-RENDER OLEH BROWSER
    created() {
        //MAKA AKAN MENJALANKAN FUNGSI fetchMessage()
        this.fetchMessages();
      
        //DAN MENGGUNAKAN LARAVEL ECHO, KITA AKSES PRIVATE CHANNEL BERNAMA CHAT YANG NNTINYA AKAN DIBUAT
        //KEMUDIAN EVENTNYA KITA LISTEN ATAU PANTAU JIKA ADA DATA YANG DIKIRIM 
        Echo.private('chat')
        .listen('MessageSent', (e) => {
            //DATA YANG DITERIMA AKAN DITAMBAHKAN KE DALAM VARIABLE MESSAGES SEBELUMNYA
            this.messages.push({
                message: e.message.message,
                user: e.user
            })
        })
    },
    methods: {
        //FUNGSI FETCH MESSAGE UNTUK MEMINTA DATA DARI DATABASE TERKAIT PESAN YANG SUDAH LAMPAU
        fetchMessages() {
            //MENGGUNAKAN AXIOS UNTUK MELAKUKAN AJAX REQUEST
            axios.get('/messages').then(response => {
                //SETIAP DATA YANG DITERIMA AKAN DITAMBAHKAN KE VARIABLE MESSAGES
                this.messages = response.data;
            });
        },

        //INGAT EMIT YANG DIKIRIM? AKAN DI-HANDLE DISINI
        //CARA TRACE-NYA GIMANA? PERHATIKAN FILE CHAT.BLADE.PHP, TERDAPAT ATTRIBUTE v-on:sent="addMessage" DI DALAM TAG DW-FORM
        //YANG BERARTI KETIKA EMIT BERNAMA SENT DITERIMA, MAKA AKAN MEMICU FUNGIS addMessage
        addMessage(message) {
            //PESAN YANG DITERIMA AKAN DITAMBAHKAN KE VARIABLE MESSAGE
            this.messages.push(message);

            //KEMUDIAN AKAN DISIMPAN KE DATABASE SEBAGAI LOG
            axios.post('/messages', message).then(response => {
              console.log(response.data);
            });
        }
    }
});

Langkah terakhir dari sisi Vue.js adalah membuat kofigurasi untuk Laravel Echo, buka file bootstrap.js dan modifikasi menjadi

window._ = require('lodash');

/**
 * We'll load jQuery and the Bootstrap jQuery plugin which provides support
 * for JavaScript based Bootstrap features such as modals and tabs. This
 * code may be modified to fit the specific needs of your application.
 */

try {
    window.Popper = require('popper.js').default;
    window.$ = window.jQuery = require('jquery');

    require('bootstrap');
} catch (e) {}

/**
 * We'll load the axios HTTP library which allows us to easily issue requests
 * to our Laravel back-end. This library automatically handles sending the
 * CSRF token as a header based on the value of the "XSRF" token cookie.
 */

window.axios = require('axios');

window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

/**
 * 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,
    encrypted: true
});

Note: Sebenarnya hanya uncomment line paling bawah, dimana config Laravel Echo-nya kita ambil dari file .env menggunakan perantara process.env.MIX_BLA_BLA. Utuk mengambil data dari .env menggunakan Laravel mix, maka kita dapat mengaksesnya dengan MIX_KEYNYA.

Bagian handling untuk fungsi broadcasting dan simpan data ke-database akan dikerjakan dengan menambahkan kedua method berikut di dalam file ChatController.php.

public function getMessages()
{
    //MENGAMBIL SEMUA LOG PESAN BESERTA USER YANG MENJADI PEMILIKNYA MENGGUNAKAN EAGER LOADING
    //PEMBAHASAN MENGENAI EAGER LOADING BISA DI CARI DI DAENGWEB.ID
    return Message::with('user')->get();
}

//FUNGSI UNTUK BROADCAST SERTA MENYIMPAN KE DATABASE
public function broadcastMessage(Request $request)
{
    $user = Auth::user(); //AMBIL USER YANG SEDANG LOGIN
    //SIMPAN DATA KE TABLE MESSAGES MELALUI USER
    $message = $user->messages()->create([
        'message' => $request->message
    ]);

    //BROADCAST EVENTNYA 
    broadcast(new MessageSent($user, $message))->toOthers();
    return response()->json(['status' => 'Message Sent!']);
}

Jangan lupa untuk menambahkan use statement.

use App\Events\MessageSent;
use App\Message;
use Auth;

Selanjutnya adalah membuat class MessageSent, pada command line jalankan command

php artisan make:event MessageSent

Buka file app/Events/MessageSent.php dan modifikasi menjadi

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

//TAMBAHKAN USE STATEMENT INI
use App\User;
use App\Message;

//PASTIKAN ShouldBroadcast DI IMPLEMENTASIKAN
class MessageSent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;
    public $user;
    public $message;

    /**
     * Create a new event instance.
     *
     * @return void
     */
  
    //CLASS INI MEMINTA DATA BERUPA USER DAN MESSAGE YANG AKAN DIBROADCAST
    public function __construct(User $user, Message $message)
    {
        $this->user = $user;
        $this->message = $message;
    }

    //KEMUDIAN BROADCASTNYA MENGGUNAKAN PRIVATECHANNEL DENGAN NAMA CHAT
    public function broadcastOn()
    {
        return new PrivateChannel('chat');
    }
}

Buat routing untuk private channel tersebut, buka file routes/channel.php dan tambahkan code

Broadcast::channel('chat', function ($user) {
    return Auth::check(); //YANG DI-RETURN ADALAH HANYA TRUE / FALSE
});

Bagian terakhir adalah mendefinisikan routing untuk aplikasinya, buka file routes/web.php dan modifikasi menjadi.

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::group(['middleware' => 'auth'], function() {
    Route::get('/', 'ChatController@index');
    Route::get('messages', 'ChatController@getMessages');
    Route::post('messages', 'ChatController@broadcastMessage');
});

Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');

Untuk membuat fungsi scrollable di Card chat-nya, tambahkan css berikut di dalam file resources/views/layouts/app.blade.php dan sebelum tag </head>

<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;
    }

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

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

 

Category:
Share:

Comments