Laravel, the web artisan's favorite PHP framework, has just got a whole new powerful tool in its arsenal: Reverb. Among the official packages of Laravel, this WebSocket server application would seamlessly let you integrate real-time features in your Laravel-based applications, thereby taking interaction to a whole new level.
What is Laravel Reverb?
Reverb acts as a mediator between your Laravel-based application and its users. It establishes two-way, real-time communication based on WebSockets technology that allows web pages to receive updates on the server without a complete page refresh. This means that your users experience your application more dynamically and responsively.
Key Features of Laravel Reverb
Blazing Speed: Provides outstanding performance for real-time information with no lags.
Scalability: Grow with your applications to handle increased user traffic.
Seamless Integration: It works with broadcasting features added to Laravel and Laravel Echo to make development simple.
Push Updates: Push updates, messages, or events to clients to share your information instantly.
Built-in Security: Data encryption and authentication assurance for security communication
Adding Laravel Reverb to Your Chat Project
With Laravel Reverb, you can build dynamic chat applications. The messages are posted instantly, making users involved comprehensively. Here's a breakdown of the steps involved:
Step 1: Setting Up Your Laravel Project:
-
Ensure you have a Laravel application set up (version 11 or above is recommended).
-
If you're starting fresh, use
composer create-project laravel/laravel your-chat-app-name
.
Step 2: Install and Configure Reverb:
Install Laravel Reverb by running the following command:
php artisan install:broadcasting
Once you’ve installed Reverb, you can now modify its configuration from the config/reverb.php
file. To establish a connection to Reverb, a set of Reverb “application” credentials must be exchanged between the client and server. These credentials are configured on the server and are used to verify the request from the client. You can define these credentials using the following environment variables:
BROADCAST_DRIVER=reverbREVERB_APP_ID=my-app-idREVERB_APP_KEY=my-app-keyREVERB_APP_SECRET=my-app-secret
It also automatically creates echo.js
in the resources/js
directory.
import Echo from 'laravel-echo'; import Pusher from 'pusher-js';window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'reverb', key: import.meta.env.VITE_REVERB_APP_KEY, wsHost: import.meta.env.VITE_REVERB_HOST, wsPort: import.meta.env.VITE_REVERB_PORT ?? 80, wssPort: import.meta.env.VITE_REVERB_PORT ?? 443, forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https', enabledTransports: ['ws', 'wss'],});
Follow the Laravel documentation for configuration steps specific to your application server https://laravel.com/docs/11.x/reverb
Step 3: Running a Server
You can launch the Reverb server by using the reverb:start
Artisan command:
php artisan reverb:start
By default, the Reverb server will be started at 0.0.0.0:8080
, which makes it accessible from all network interfaces.
If you want to set a specific host or port, you can use the –host
and –port
options when starting the server.
php artisan reverb:start --host=127.0.0.1 --port=9000
You can also define REVERB_SERVER_HOST
and REVERB_SERVER_PORT
environment variables in your application’s .env
configuration file.
Step 4: Setup Database
Open your .env
file and adjust the settings to set up your database. Here’s an example using SQLite for simplicity:
DB_CONNECTION=sqliteDB_DATABASE=/path/to/database.sqlite
You can create an SQLite database by simply running:
touch /path/to/database.sqlite
For this demo, we’ll create five predefined rooms. Let’s start by generating a model ChatMessage
with migration for a chat_messages
table.
php artisan make:model ChatMessage --migration
To make it simpler, only create name attributes for this model and migrate it.
Schema::create('chat_messages', function (Blueprint $table) { $table->id(); $table->foreignId('receiver_id'); $table->foreignId('sender_id'); $table->text('text'); $table->timestamps();});
php artisan migrate
Now, let's add the necessary relationships in the ChatMessage
model. Open the ChatMessage.php
file in the app/Models
directory and update it as follows:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Model; class ChatMessage extends Model{ use HasFactory; protected $fillable = [ 'sender_id', 'receiver_id', 'text' ]; public function sender() { return $this->belongsTo(User::class, 'sender_id'); } public function receiver() { return $this->belongsTo(User::class, 'receiver_id'); }}
Step 5: Create Event
To handle the broadcasting of messages, we'll create an event called MessageSent
. This event will implement Laravel's ShouldBroadcastNow
interface, which allows for immediate broadcasting over WebSockets without queuing. Follow these steps to create and set up the event:
-
Create a new PHP file in the
App\Events
directory and name itMessageSent.php
. -
Open the newly created file and add the following code:
<?php namespace App\Events; use App\Models\ChatMessage;use Illuminate\Broadcasting\InteractsWithSockets;use Illuminate\Broadcasting\PrivateChannel;use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;use Illuminate\Foundation\Events\Dispatchable;use Illuminate\Queue\SerializesModels; class MessageSent implements ShouldBroadcastNow{ use Dispatchable; use InteractsWithSockets; use SerializesModels; /** * Create a new event instance. */ public function __construct(public ChatMessage $message) { // } /** * Get the channels the event should broadcast on. * * @return array<int, \Illuminate\Broadcasting\Channel> */ public function broadcastOn(): array { return [ new PrivateChannel("chat.{$this->message->receiver_id}"), ]; }}
Step 6: Create a Private Channel Route
The channels.php
file in Laravel applications plays a crucial role in defining broadcast channels used for real-time communication features.
<?php use Illuminate\Support\Facades\Broadcast; Broadcast::channel('chat.{id}', function ($user, $id) { return (int) $user->id === (int) $id;});
This code defines a private channel named chat.{id}
using Laravel's Broadcast
facade. Private channels restrict access based on user authentication and authorization logic.
Step 7: Defining Routes
1. Chat Room Route:
Route::get('/chat/{friend}', function (User $friend) { return view('chat', [ 'friend' => $friend ]);})->middleware(['auth'])->name('chat');
This route is responsible for rendering the chat interface. It accepts a dynamic parameter {friend}
representing the user's chat partner.
3. Get Chat Messages Route:
Route::get('/messages/{friend}', function (User $friend) { return ChatMessage::query() ->where(function ($query) use ($friend) { $query->where('sender_id', auth()->id()) ->where('receiver_id', $friend->id); }) ->orWhere(function ($query) use ($friend) { $query->where('sender_id', $friend->id) ->where('receiver_id', auth()->id()); }) ->with(['sender', 'receiver']) ->orderBy('id', 'asc') ->get();})->middleware(['auth']);
This route retrieves chat messages exchanged between the authenticated user and the specified friend ({friend}
). The query ensures it retrieves messages where either the user is the sender or receiver, including both directions of the conversation.
4. Send Chat Message Route:
Route::post('/messages/{friend}', function (User $friend) { $message = ChatMessage::create([ 'sender_id' => auth()->id(), 'receiver_id' => $friend->id, 'text' => request()->input('message') ]); broadcast(new MessageSent($message)); return $message;});
After creating the message, it leverages Laravel's broadcasting functionality using broadcast(new MessageSent($message))
. This line broadcasts the newly created message to all connected users through Reverb, enabling real-time chat functionality.
Step 8: Creating the Blade View
To render the chat interface, you'll need to create a Blade view file. Create a new file named chat.blade.php
in the resources/views
directory and add the following code:
<x-app-layout> <x-slot name="header"> <h2 class="text-xl font-semibold leading-tight text-gray-800"> {{ $friend->name }} </h2> </x-slot> <div class="py-12"> <div class="mx-auto max-w-7xl sm:px-6 lg:px-8"> <div class="overflow-hidden bg-white shadow-sm sm:rounded-lg"> <div class="p-6 bg-white border-b border-gray-200"> <chat-component :friend="{{ $friend }}" :current-user="{{ auth()->user() }}" /> </div> </div> </div> </div></x-app-layout>
The key element here is <chat-component :friend="{{ $friend }}" :current-user="{{ auth()->user() }}" />
. This line renders a Vue.js component named chat-component
.
Step 8: Create a Chat Component
<template> <div> <div class="flex flex-col justify-end h-80"> <div ref="messagesContainer" class="p-4 overflow-y-auto max-h-fit"> <div v-for="message in messages" :key="message.id" class="flex items-center mb-2" > <div v-if="message.sender_id === currentUser.id" class="p-2 ml-auto text-white bg-blue-500 rounded-lg" > {{ message.text }} </div> <div v-else class="p-2 mr-auto bg-gray-200 rounded-lg"> {{ message.text }} </div> </div> </div> </div> <div class="flex items-center"> <input type="text" v-model="newMessage" @keydown="sendTypingEvent" @keyup.enter="sendMessage" placeholder="Type a message..." class="flex-1 px-2 py-1 border rounded-lg" /> <button @click="sendMessage" class="px-4 py-1 ml-2 text-white bg-blue-500 rounded-lg" > Send </button> </div> <small v-if="isFriendTyping" class="text-gray-700"> {{ friend.name }} is typing... </small> </div></template> <script setup>import axios from "axios";import { nextTick, onMounted, ref, watch } from "vue"; const props = defineProps({ friend: { type: Object, required: true, }, currentUser: { type: Object, required: true, },}); const messages = ref([]);const newMessage = ref("");const messagesContainer = ref(null);const isFriendTyping = ref(false);const isFriendTypingTimer = ref(null); watch( messages, () => { nextTick(() => { messagesContainer.value.scrollTo({ top: messagesContainer.value.scrollHeight, behavior: "smooth", }); }); }, { deep: true }); const sendMessage = () => { if (newMessage.value.trim() !== "") { axios .post(`/messages/${props.friend.id}`, { message: newMessage.value, }) .then((response) => { messages.value.push(response.data); newMessage.value = ""; }); }}; const sendTypingEvent = () => { Echo.private(`chat.${props.friend.id}`).whisper("typing", { userID: props.currentUser.id, });}; onMounted(() => { axios.get(`/messages/${props.friend.id}`).then((response) => { console.log(response.data); messages.value = response.data; }); Echo.private(`chat.${props.currentUser.id}`) .listen("MessageSent", (response) => { messages.value.push(response.message); }) .listenForWhisper("typing", (response) => { isFriendTyping.value = response.userID === props.friend.id; if (isFriendTypingTimer.value) { clearTimeout(isFriendTypingTimer.value); } isFriendTypingTimer.value = setTimeout(() => { isFriendTyping.value = false; }, 1000); });});</script>
This Vue.js component manages the chat interface's dynamic behaviour. It displays a scrollable message list, styled differently based on the sender (current user or friend). It provides an input field for composing new messages and a button to send them. It leverages axios
for making HTTP requests to fetch initial messages and send new ones.
Real-time functionality is achieved using Laravel Echo:
-
It listens for broadcasted
MessageSent
events to update the message list whenever a new message arrives. -
It utilizes whispers on private channels to notify the chat partner about the user's typing activity and receive similar notifications from the friend.
Step 9: Run Project
To run the Laravel project, we need to execute the following command:
php artisan serve
For starting front:
npm run dev
Run reverb:
php artisan reverb:start
Source Code
You can find the source code for this Laravel Reverb chat implementation in the following GitHub repository: https://github.com/qirolab/laravel-reverb-chat
By utilizing Laravel Reverb, developers can ensure their applications remain responsive and dynamic, providing real-time updates and interactions that modern users expect. Happy coding!
Hey, I'm Harish, a full-stack web developer. I teach web development tutorials with practical screencasts on Qirolab Youtube Channel.