COMMIT - Powerful Real time Web Applications Dengan Laravel + React + SocketIO + Redis

Powerful Real time Web Applications Dengan Laravel + React + SocketIO + Redis

Programming
Pada aplikasi web jaman now, banyak web menggunakan websockets untuk mengimplementasikan website yang dapat melakukan perubahan data secara langsung a.k.a live!!!!

karena biasanya ketika sebuah data dibuat ataupun dihapus, masing-masing pengguna website/client perlu me-refresh browsernya untuk melihat perubahan data yang terjadi. Tentu ini bukan hal yang terlalu signifikan untuk diperhitungkan, namun beda jadinya kalau website yang anda buat memang memerlukan perubahan data secara realtime seperti chat app, atau yang lainnya.

Dengan Fitur Broadcasting di laravel, membuat aplikasi seperti chat app akan dapat dibuat dengan singkat. Kira-kira saya mengestimasikan anda dapat membuat aplikasi simpel chat ini mungkin hanya beberapa menit saja (1k menit mungkin) haha.

Oke Langsung saja, apa saja yang anda butuhkan untuk membuat Aplikasi Realtime Sederhana ? 

Requirements

  • Laravel ( 5.5+ ), saya pakai 5.7.
  • Redis Server ( Wajib ), saya pakai v4.0.1, kalau belum install coba Tutorial Ini.
  • Node.js ( Yang LTS deh ), saya pakai v8.9.3.
  • Laravel Echo + Laravel Echo Server + Socket IO.
  • React + ReactDOM v16.2.0+
  • 2 Browser Berbeda ( Rekomendasi Firefox + Chrome )
  • Kopi
  • Rokok Cemilan

Jika anda Ingin Mengaplikasikannya di Production, Pastikan server yang anda miliki dapat menjalankan Laravel Echo(node js ) + Redis

Mari Mulai

Saya mengasumsikan bahwa anda mengetahui pengetahuan umum tentang React js dan tentu saja Laravel.

 

Pertama buat Projek Laravel baru atau anda bisa gunakan proyek anda yang sekarang, jika Redis sudah terinstall, ubah .env file dan ubah BROADCAST_DRIVER dan QUEUE_DRIVER = redis. kurang lebih bagian .env nya seperti ini

BROADCAST_DRIVER=redis
CACHE_DRIVER=file
SESSION_DRIVER=file
SESSION_LIFETIME=120
QUEUE_DRIVER=redis

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

lalu langkah selanjutnya kita membuat contoh migrasi dan model untuk menyimpan pesan kita namakan model ini dengan nama Message, mari kita buat beserta Controllernya jadi pakai 2 flag -mc

php artisan make:model Message -mc

dan kita ubah isi create_messages_table.php ini

public function up()
    {
        Schema::create('messages', function (Blueprint $table) {
            $table->increments('id');
            $table->uuid('uuid');
            $table->text('content');
            $table->timestamps();
        });
    }

lalu jalankan

php artisan migrate

selanjutnya kita isikan model Message.php seperti ini

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Message extends Model
{
    protected $guarded = [];//$fillable pun tidak masalah :), skip karena saya yakin anda sudah bisa
}

Redis

Karena kita menggunakan redis kita install Predis Library terlebih dahulu:

composer require predis/predis

Fungsi Library ini agar fitur Pub / Sub yang terdapat pada redis dapat diterima oleh server Websocket kita. Ketika sebuah event yang ditangkap melalui Redis Broadcast, server websocket dapat menerima data payload dari Redis, ya kurang lebih seperti itu lah.

Socket.IO + Laravel Echo

Nah kita akan menggunakan Socket io + Laravel Echo  sebagai penangkap Sinyal dari Redis Broadcast, untuk itu kita install dulu socket io + laravel echo:

npm install --save socket.io-client laravel-echo

lalu  untuk Laravel Echo Servernya nya kita install secara global saja ya kaya di dokumentasi

npm install -g laravel-echo-server

inisiasi laravel echo + socket io di bootstrap.js ( resources/js/bootstrap.js untuk 5.7++ atau resources/assets/js/bootstrap.js untuk 5.6 kebawah )

import Echo from 'laravel-echo'
window.io = require('socket.io-client');

window.Echo = new Echo({
    broadcaster: 'socket.io',
    host: window.location.hostname + ':6001'
});

kembali kepada prasayarat di atas bahwa kita memerlukan setidaknya server tambahan , karena laravel tidak mengimplementasikan node js ( socket io ) didalam servernya, maka dari itu tadi kita install laravel-echo-server untuk membuat server yang dapat menerima penyiaran/broadcast dari Redis

Message Event

Dalam contoh ini sangat sederhana, dalam sebuah halaman yang nantinya kita tentukan, akan memuat sebuah kolom input untuk memasukkan "pesan" yang dapat diterima juga oleh client/pengguna lain, yang tentu saja kita tidak perlu repot-repot me-refresh halaman yang sedang kita lihat pesannya. oke kita sekarang buat event dengan nama MessageSent

php artisan make:event MessageSent

kita ubah sedikit MessageSent.php ini, buka app/Events/MessageSent.php

<?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 MessageSent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public $message;
    public function __construct($message)
    {
        $this->message = $message;
        $this->dontBroadcastToCurrentUser();//gaperlu login :)
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new Channel('public');
    }
}
lalu ubah MessageController yang sudah kita buat sejak awal. kita buat dua method seperti ini
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Message;
use App\Events\MessageSent;

class MessageController extends Controller
{
    public function index()
    {
        $messages = Message::get();
        if(request()->wantsJson()){
            return $messages;
        }
        return view('message');
    }

    public function store(Request $request)
    {
        $message = new Message;
        $content = $request->content;
        $message->content = $content;
        $message->uuid = $request->uuid;
        $message->save();
        broadcast(new MessageSent($message));
        return $message;
    }
}
lalu daftarkan di route
<?php

//...route route anda

Route::get('/message','MessageController@index');
Route::post('/message','MessageController@store');

setelah itu kita init terlebih dahulu laravel-echo-server dengan menjalankan perintah

laravel-echo-server init

isikan seperti ini

? Do you want to run this server in development mode? Yes
? Which port would you like to serve from? 6001
? Which database would you like to use to store presence channel members? redis
? Enter the host of your Laravel authentication server. http://localhost:8000
? Will you be serving on http or https? http
? Do you want to generate a client ID/Key for HTTP API? Yes
? Do you want to setup cross domain access to the API? No
appId: idappanda
key: k3y4nd413R0
Configuration file saved. Run laravel-echo-server start to run server.

jangan dulu kita jalankan ya sabar. Sekarang kita buat dulu file reactnya dan kita beri saja namanya Message.js, sebagai tambahan kita install package uuid dengan menjalankan

npm install --save uuid

lalu kita buat component Reactnya yang diberi nama Message.js seperti ini:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import uuid from 'uuid';
import axios from 'axios';

export default class Message extends Component {
    constructor(props){
        super(props);
        this.state = {
            content:'',
            uuid:null,
            messages:[],
        }
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }
    componentDidMount() {
        axios.get('/message').then(res => this.setState({
            messages:res.data
        }));
        window.Echo.channel('public').listen('MessageSent',({message}) => {
            this.setState(state => ({
                messages:[...state.messages,message]
            }))
        })
    }
    handleChange(e){
        this.setState({
            [e.target.name]:e.target.value
        })
    }
    handleSubmit(e){
        e.preventDefault();
        const data = {
            content:this.state.content,
            uuid:uuid.v4(),
        }
        axios.post('/message',data).then(() => {
            this.setState(state => ({
                messages:[...state.messages,data],
                content:''
            }))
        }).catch(err => console.log(err.response))
    }
    
    render() {
        return (
            <div className="container">
                <form onSubmit={this.handleSubmit}>
                    <div className="form-group">
                        <label htmlFor="exampleInputEmail1">Message</label>
                        <input value={this.state.content} onChange={this.handleChange} type="text" className="form-control" id="exampleInputEmail1" name="content" />
                        <small id="emailHelp" className="form-text text-muted">We'll Share your message!!</small>
                    </div>
                    <button type="submit" className="btn btn-primary">Send</button>
                </form>
                <div className="mt-4">
                    {
                    this.state.messages.length > 0 ?
                        this.state.messages.map(message => (
                            <div className="alert alert-light" key={message.uuid} role="alert">
                                {message.content}
                            </div>
                        ))
                    :
                    <div className="alert alert-primary" role="alert">
                    Pesan Akan Muncul, Silahkan buat pesan pertama :*
                    </div>
                }
                </div>
            </div>
        );
    }
}
if(document.getElementById('message')){
    ReactDOM.render(<Message/>,document.getElementById('message'));
}

mari kita review bagian ini pada Message.js

window.Echo.channel('public').listen('MessageSent',({message}) => {
   this.setState(state => ({
       messages:[...state.messages,message]
   }))
})

perintah ini digunakan untuk mendengarkan Broadcast dari MessageSent yang sudah kita buat di MessageController bagian ini

public function store(Request $request)
    {
        $message = new Message;
        $content = $request->content;
        $message->content = $content;
        $message->uuid = $request->uuid;
        $message->save();
        broadcast(new MessageSent($message));//broadcast -> laravel echo
        return $message;
    }

fungsi broadcast ini juga memerlukan queue jadi pada disaat yang sama anda mungkin akan membuka 3 terminal sekaligus, pertama server laravel, kedua server laravel-echo-server, yang ketiga adalah perintah queue php artisan queue:work , namun untuk queue ini anda juga bisa menggunakan cronjob ataupun supervisor pada linux untuk memprosesnya di latar belakang.

selanjutnya daftarkan Message.js di app.js (resources/js/app.js atau resources/js/assets/app.js).

require('./bootstrap');

require('./components/Message');//tergantung letaknya ya, pastikan dicocokin bro

Dan Yang Terakhir..

Terakhir kita jalankan perintah

php artisan queue:work

dan

laravel-echo-server start

terakhir kita buat file message.blade.php di folder utama views. dan isikan seperti ini

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Message</title>
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
        crossorigin="anonymous">
    <script src="//{{ Request::getHost() }}:6001/socket.io/socket.io.js"></script>
</head>
<body>
    <div id="app">
        <div class="jumbotron">
            <h1 class="display-4">Hello, world!</h1>
            <p class="lead">This is a simple message.</p>
            <hr class="my-4">
        </div>
    </div>
    <div id="message"></div>
    <script src="{{ asset('/js/app.js')}}"></script>
</body>
</html>

lalu silahkan buka 2 browser itu dan silahkan kirim chatnya. data akan otomatis terupdate tanpa perlu anda refresh di client lain :). tentu berbeda dengan ...state atau concat pada react tanpa menggunakan broadcast.

Kesimpulan

Sungguh mudah sekali menggunakan Fitur Broadcast Laravel ini, namun jika aplikasi pesan ini gagal dijalankan pada program anda , coba pastikan bahwa

  • Redis berjalan, coba anda jalankan di terminal `redis-cli ping` jika jawabannya pong berarti sudah jalan
  • pastikan konfigurasi saat menjalankan laravel-echo-server init sudah anda lakukan dengan benar
  • Jangan diclose queue:work kalau anda tidak pakai cronjob atau supervisor
  • Pastikan anda ganteng mengecek lagi kode-kode diatas, mungkin ada yang salah,typo, atau terlewatkan dan terlupakan :(

Enjoy this post? Share It!