Create a Chat with Laravel and Pusher

Create a Chat with Laravel and Pusher

In the digital age, real-time web applications have transformed how we interact with technology and each other. From live chat systems to instant content updates, users now expect seamless, instantaneous communication at their fingertips. This tutorial will introduce you to the exciting world of real-time web applications using Laravel and Pusher, two powerful tools that make implementing real-time features a breeze.

Install and run migrations

First let us install the laravel.

composer create-project laravel/laravel

For the ease of use let’s use SQLite database. In .env change nysql to sqlite here. And remove DB_DATABASE=laravel in .env

DB_CONNECTION=sqlite

Now let’s write migrations.

php artisan make:migration create_chat_messages_table –create=chat_messages

Now add this to migration table

public function up(): void
{
Schema::create(‘chat_messages’, function (Blueprint $table) {
$table->id();
$table->string(‘user’);
$table->text(‘message_text’);
$table->timestamps();
});
}

now run

php artisan migrate

Pusher

First log into pusher and create an app.

https://dashboard.pusher.com/apps

After creating the app get the these fields and save in side .env

BROADCAST_DRIVER=pusher

PUSHER_APP_ID=””
PUSHER_APP_KEY=””
PUSHER_APP_SECRET=””
PUSHER_APP_CLUSTER=

install pusher add this inside command line

composer require pusher/pusher-php-server

Vue JS

Now let’s build the UI with VusJS.

npm install
npm install vue@latest vue-loader@latest
npm i @vitejs/plugin-vue

Edit File vite.config.js

import { defineConfig } from ‘vite’;
import laravel from ‘laravel-vite-plugin’;
import vue from ‘@vitejs/plugin-vue’

export default defineConfig({
plugins: [
vue(),
laravel({
input: [‘resources/css/app.css’, ‘resources/js/app.js’],
refresh: true,
}),
],
});

Now got to resourcesjsapp.js and add these lines.

import { createApp } from ‘vue’;
import Chat from ‘./components/Chat.vue’;
import App from ‘./App.vue’
const app = createApp(App);
app.component(‘Chat’, Chat);

app.mount(“#app”);

Let’s start with UI

And create this file resourcesjscomponentsChat.vue

Add these lines

<template>
<div class=”chat-box”>
<div class=”chat-box-header”>Chat</div>
<div class=”chat-box-messages” id=”chat-messages”>
<div class=”message other-user”>Hi, how are you?</div>
<div class=”message current-user”>I’m good, thanks! And you?</div>
</div>
<div class=”chat-box-input”>
<input type=”text” class=”input_border” placeholder=”Type a message…” />
<button type=”button”>Send</button>
</div>
</div>
</template>

<style scoped>

.chat-box-input {
padding: 10px;
background-color: #fff;
border-top: 1px solid #eee;
display: flex; /* Aligns input and button side by side */
}

.chat-box-input input {
flex-grow: 1; /* Allows input to take up available space */
margin-right: 8px; /* Adds spacing between input and button */
padding: 8px 10px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}

.chat-box-input button {
padding: 10px 15px;
background-color: #007bff;
color: #ffffff;
border: none;
border-radius: 4px;
cursor: pointer; /* Changes cursor to pointer on hover */
white-space: nowrap; /* Prevents wrapping of text in the button */
}

.chat-box-input button:hover {
background-color: #0056b3; /* Darker shade on hover for visual feedback */
}

.chat-box {
display: flex;
flex-direction: column;
max-width: 320px;
min-width: 300px;
height: 500px;
border: 1px solid #ccc;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
font-family: Arial, sans-serif;
background-color: #fff;
}

.chat-box-header {
background-color: #007bff;
color: #ffffff;
padding: 10px;
text-align: center;
font-size: 16px;
}

.chat-box-messages {
flex: 1;
padding: 10px;
overflow-y: auto;
background-color: #f9f9f9;
display: flex;
flex-direction: column;
}

.message {
margin-bottom: 12px;
padding: 8px 10px;
border-radius: 20px;
display: inline-block;
max-width: 70%;
}

.current-user {
background-color: #007bff;
color: #ffffff;
margin-left: auto;
text-align: right;
align-self: flex-end;
}

.other-user {
background-color: #e9ecef;
color: #333;
}

.chat-box-input {
padding: 10px;
background-color: #fff;
border-top: 1px solid #eee;
}

.chat-box-input input {
width: 100%;
padding: 8px 10px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}

.input_border{
border: 1px solid #ccc;
}
</style>

Now go to this file resourcesviewswelcome.blade.php delete everything and add these,

<!DOCTYPE html>
<html lang=”{{ str_replace(‘_’, ‘-‘, app()->getLocale()) }}”>
<head>
<meta charset=”utf-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1″>
<meta name=”csrf-token” content=”{{ csrf_token() }}”>

<title>Laravel</title>
@vite(‘resources/js/app.js’)
</head>
<body class=”antialiased”>
<div id=”app”></div>
</body>
</html>

Add this file resourcesjsApp.vue

Fill with these content,

<template>
<chat></chat>
</template>

Run this command in one console (in your project directory)

php artisan serve

Run this command in another console (in your project directory)

npm run dev

Now you will see this fine UI in localhost:8000

Now let’s create a model and event.

php artisan make:model ChatMessage
php artisan make:event MessageCreated

This is ChatMessage Model

class ChatMessage extends Model
{
use HasFactory;

protected $fillable = [‘user’, ‘message’];
}

Then ‘MessageCreated’ event.

<?php

namespace AppEvents;

use IlluminateBroadcastingChannel;
use IlluminateBroadcastingInteractsWithSockets;
use IlluminateBroadcastingPresenceChannel;
use IlluminateBroadcastingPrivateChannel;
use IlluminateContractsBroadcastingShouldBroadcast;
use IlluminateFoundationEventsDispatchable;
use IlluminateQueueSerializesModels;

class MessageCreated implements ShouldBroadcast
{

use Dispatchable, InteractsWithSockets, SerializesModels;

public $chat;

/**
* Create a new event instance.
*/
public function __construct($chat)
{
$this->chat = $chat;
}

/**
* Get the channels the event should broadcast on.
*
* @return array<int, IlluminateBroadcastingChannel>
*/
public function broadcastOn(): array
{
return [
new Channel(‘chats’),
];
}
}

Check here we have added ShouldBroadcast broadcasting on a private channel. Broadcasting on public channel means when use echo anyone will see that.

And need to enable providers too

configapp.php

And now the routes.

Route::post(‘/chat’, function () {
$chat = ChatMessage::create([
‘user’ => request(‘user’),
‘message_text’ => request(‘message_text’)
]);

event((new MessageCreated($chat))->dontBroadcastToCurrentUser());
});

What this does is saving ChatMessage inside table and send it to pusher. Let’s connect to UI and check.

Now if you check in ui by clicking the button

You will see this in pusher dashboard.

But you may notice that after pressing the button it takes long time to populate the message in screen. That happens it takes long time to send pusher by backend. We can make this call asynchronous.

This is how we does that,

Step 1: Configure the Queue Driver

First, update your .env file to use the database queue driver:

QUEUE_CONNECTION=database

This setting tells Laravel to use the database for queueing jobs.

Step 2: Create the Queue Table

Laravel needs a table in your database to store queued jobs. You can create this table by running the queue table migration that comes with Laravel. In your terminal, execute:

php artisan queue:table

Then, apply the migration to create the table:

php artisan migrate

Step 4: Ensure Event Listeners Implement ShouldQueue

use IlluminateBroadcastingPrivateChannel;
use IlluminateContractsBroadcastingShouldBroadcast;
use IlluminateContractsQueueShouldQueue;
use IlluminateFoundationEventsDispatchable;
use IlluminateQueueSerializesModels;

class MessageCreated implements ShouldBroadcast, ShouldQueue
{

Step 5: Running the Queue Worker

php artisan queue:work

This command starts a queue worker that listens for new jobs on the database queue and processes them. Keep this worker running to ensure your queued jobs are processed.

Now you see significant speed increase in API call

I have added few changes to chat interface. Yu can access full example in this git repo..

Run this in cli

npm install –save laravel-echo pusher-js

And you need to add this to resourcesjsbootstrap.js

import Echo from ‘laravel-echo’;
import Pusher from ‘pusher-js’;
window.Pusher = Pusher;

window.Echo = new Echo({
broadcaster: ‘pusher’,
key: ‘pubkey’,
cluster: ‘mt1’,
forceTLS: true
});

https://github.com/vimuths123/chatapp

Leave a Reply

Your email address will not be published. Required fields are marked *