How to create a Real-Time Chat using Laravel and Firebase

A comprehensive guide to make a real-time chat using Laravel 6 with firebase cloud messaging and laravel-fcm library.

Real-time Laravel FCM chat

Getting Started — Configuring Environment

Step : 1

Install composer

Step : 2

Create a Laravel 6 project named Chatsi

composer create-project — prefer-dist laravel/laravel Tweety “6.*”

Step : 3

Require laravel-ui for authentication

composer require laravel/ui=”1.*” — dev

Generate authentication with vue

php artisan ui vue — auth

Install dependancies and build

npm install && npm run dev

Step : 4

Start Development Server

php artisan serve

Step : 5

Create a database and and add it to the .env file. Then, migrate authentication tables

php artisan migrate

Make sure that authentications work properly

Installing FCM

Step : 6

Login to your google account and go to ‘console.firbase.google.com’

Create a project and give it a name. Here it is chatsi-realtime-laravel-chat

No need to tic google analytics option (not checked recommended)

Step : 7

Navigate to project settings and create a new web app from the icon below. Give it a name.

new web app icon

Step : 8

Copy the firebase link script tag in the line 2 and paste it inside resources>views>layouts> app.blade.php header tag.

Step : 9

Copy the remaining part of the script of firebase to the app.blade.php script section.

Step : 10

Navigate to Project Overview>Project Settings, register your app there and copy the generated code as publicVapidKey

Step : 11

Inside home.blade.php add below lines as a new script section.

@section('scripts')
<script>
// Retrieve Firebase Messaging object.
const messaging = firebase.messaging();
// Add the public key generated from the console here.
messaging.usePublicVapidKey("your publicVapidKey");
</script>
@endsection

Add FCM token to user table

Step : 12

Create a new controller called FCMController

php artisan make:controller FCMController

Step : 13

Make the FCMController looks like below

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\User;
class FCMController extends Controller
{
//
public function index(Request $req){
return "testing";
}
}
?>

Step : 14

Change the user table migration to add a fcm_token as below

Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->longText('fcm_token')->nullable();
$table->rememberToken();
$table->timestamps();
});

Migrate the changes using below command

php artisan migrate:fresh

Don’t forget to add the new filed to fillable variable inside User model.

Try to create a test user. It should be created with a null fcm_token.

Send FCM token to the server

Step : 15

Change your FCMController as below

public function index(Request $req){
$input = $req->all();

return response()->json($input);
}

Return a dummy response again.

Don’t forget to create a route in Api.php as below.

Route::post('/save-token', 'FCMController@index');

We will try to hit the end point with axios library.

Step : 16

Just add CDN script(jsDeliver CDN) inside out head tag in app.blade.php.

Step : 17

Add following lines to the home.blade.php.

@section('scripts')
<script>
// Retrieve Firebase Messaging object.
const messaging = firebase.messaging();
// Add the public key generated from the console here.
messaging.usePublicVapidKey("BNi8WFY0HVBE2YvPXCfj7wDGbURGAh6rD57klttmetVL-PDYCnPNOgvUfC_RZquDF6uXjS5f78PXAcnFI9KuNTE");


function sendTokenToServer(fcm_token) {
const user_id = '{{auth()->user()->id}}';
//console.log($user_id);
axios.post('/api/save-token', {
fcm_token, user_id
})
.then(res => {
console.log(res);
})

}

function retreiveToken(){
messaging.getToken().then((currentToken) => {
if (currentToken) {
sendTokenToServer(currentToken);
// updateUIForPushEnabled(currentToken);
} else {
// Show permission request.
//console.log('No Instance ID token available. Request permission to generate one.');
// Show permission UI.
//updateUIForPushPermissionRequired();
//etTokenSentToServer(false);
alert('You should allow notification!');
}
}).catch((err) => {
console.log(err.message);
// showToken('Error retrieving Instance ID token. ', err);
// setTokenSentToServer(false);
});
}
retreiveToken();
messaging.onTokenRefresh(()=>{
retreiveToken();


});

messaging.onMessage((payload)=>{
console.log('Message received');
console.log(payload);

location.reload();
});

</script>
@endsection

Step : 18

Change FCMController as below.

public function index(Request $req){
$input = $req->all();
$fcm_token = $input['fcm_token'];
$user_id = $input['user_id'];


$user = User::findOrFail($user_id);

$user->fcm_token = $fcm_token;
$user->save();
return response()->json([
'success'=>true,
'message'=>'User token updated successfully.'
]);
}

Try to go to homepage and see whether you can see the token in console. if not check your firebase keys and script links.

Installing Laravel FCM Library

Step : 19

Install laravel fcm library

composer require brozot/laravel-fcm

Step : 20

Register the Laravel-FCM provider to your app configuration

congif/app.php

'providers' => [
// ...

LaravelFCM\FCMServiceProvider::class,
]
'aliases' => [
// ...
'FCM' => LaravelFCM\Facades\FCM::class,
'FCMGroup' => LaravelFCM\Facades\FCMGroup::class, // Optional
]

Creating the chat UI

Step : 21

I will use on document css for this. Let’s modify our home.blade.php file.

@section('content')
<style>
.chat-container {
display: flex;
flex-direction: column;
}

.chat {
border: 1px solid gray;
border-radius: 3px;
width: 50%;
padding: 0.5em;
}

.chat-left {
background-color: white;
align-self: flex-start;
}

.chat-right {
background-color: #3f9ae5;
align-self: flex-end;
}

.message-input-container {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background-color: white;
border: 1px solid gray;
padding: 1em;


}
</style>
<div class="container" style="margin-bottom: 480px" >
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Dashboard</div>

<div class="card-body">
<div class="chat-container">

<p class="chat chat-right">
<b>A :</b><br>
message1 </p>
<p class="chat chat-left">
<b>B :</b><br>
message 2
</p>



</div>
</div>
</div>
</div>
</div>
</div>
<div class="message-input-container">
<form action="" method="POST">
@csrf
<div class="form-group">
<label>Message</label>
<input type="text" name="message" class="form-control">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">SEND MESSAGE</button>
</div>
</form>
</div>
@endsection
chats section
new message section

Chat migration and creation

Step : 22

Create a table named Chats and Chats model

php artisan make:migration create_chats_table

php artisan make:model Chat

Step : 23

Change your migration file as below.

Schema::create('chats', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('sender_id');
$table->string('sender_name');
$table->longText('message');
$table->timestamps();
});

php artisan migrate:fresh

Step : 24

Add fillable fields to Chat model.

protected $fillable=[
'sender_id',
'sender_name',
'message'
];

Step : 25

Add following lines to the HomeController to display the chats.

public function createChat(Request $request)
{
$input = $request->all();
$message = $input['message'];
$chat = new Chat([
'sender_id' => auth()->user()->id,
'sender_name' => auth()->user()->name,
'message' => $message
]);

$chat->save();
return redirect()->back();

}

Step : 26

Add these lines to display the chat in home.blade.php.

<div class="chat-container">
@if(count($chats)==0)
<p>There is no chat yet.</p>
@endif
@foreach($chats as $chat )
@if($chat->sender_id === auth()->user()->id)
<p class="chat chat-right">
<b>{{$chat->sender_name}} :</b><br>
{{$chat->message}} </p>
@else
<p class="chat chat-left">
<b>{{$chat->sender_name}} :</b><br>
{{$chat->message}}
</p>
@endif
@endforeach


</div>

Step : 27

Make following routes in web.php

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

Route::post('/home','HomeController@createChat')->name('home.createChat');

Step : 28

Make users and check whether messages are displaying.

Using Laravel FCM

Step : 29

Update the HomeController with the new methods below.

private function broadcastMessage($senderName, $message)
{
$optionBuilder = new OptionsBuilder();
$optionBuilder->setTimeToLive(60 * 20);

$notificationBuilder = new PayloadNotificationBuilder('New message from : ' . $senderName);
$notificationBuilder->setBody($message)
->setSound('default')
->setClickAction('http://localhost:8000/home');

$dataBuilder = new PayloadDataBuilder();
$dataBuilder->addData([
'sender_name' => $senderName,
'mesage' => $message
]);

$option = $optionBuilder->build();
$notification = $notificationBuilder->build();
$data = $dataBuilder->build();

$tokens = User::all()->pluck('fcm_token')->toArray();

$downstreamResponse = FCM::sendTo($tokens, $option, $notification, $data);

return $downstreamResponse->numberSuccess();

}

Update the createChat function as below.

public function createChat(Request $request)
{
$input = $request->all();
$message = $input['message'];
$chat = new Chat([
'sender_id' => auth()->user()->id,
'sender_name' => auth()->user()->name,
'message' => $message
]);

$this->broadcastMessage(auth()->user()->name,$message);

$chat->save();
return redirect()->back();

}

Don’t forget to Check the imports.

Step : 30

Go to cloud messaging section in the firebase console of google and add those keys to the .env file with your keys

FCM_SERVER_KEY = your key
FCM_SENDER_ID = your id

Check by logging with two users and messages will update now.

Send and Receive Messages with notification

Step : 31

Create a file named firbase-messaging-sw.js in public>js folder and add these codes and update it with your credentials.

// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/7.23.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/7.23.0/firebase-messaging.js');

// Initialize the Firebase app in the service worker by passing in
// your app's Firebase config object.
// https://firebase.google.com/docs/web/setup#config-object
var firebaseConfig = {
apiKey: "your key",
authDomain: "your app url",
databaseURL: "your app db url",
projectId: "your project id",
storageBucket: "your storage bucket",
messagingSenderId: "your message senderid",
appId: "your app id"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);

// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();

messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
const {title, body} = payload.notification;
const notificationOptions = {
body,
};

return self.registration.showNotification(title,
notificationOptions);
});

Try to send messages and you will see json objects of messages in console and receive notifications as well.

Thanks.

WHMLowe