Backend Development

Implementing A Chat App Using Laravel And Pusher

implementing a chat app with laravel and pusher

In this tutorial we will implement a real time chat application using laravel framework and laravel broadcasting using the pusher library as it supports a real time events and channels.

 

Laravel broadcasting based internally on Web Sockets but the advantages of using it is that it facilitates all the hard work of web sockets because implementing web sockets from scratch require a lot of work like special server to handle connections etc, you can read more about implementing a socket server in this article.

In fact laravel handles broadcasting using the event listener pattern so before starting you should have knowledge of laravel events and listeners you can more about them here.

There are a variety of drivers that can be used inside broadcasting some of them are pusher, redis, and log. In this tutorial we will be using Pusher.

 

Preparing The App

Let’s create a new laravel project, in this tutorial i will be using laravel 5.5 so initiate this command in the terminal:

composer create-project laravel/laravel chat  "5.5.*" --prefer-dist

After create a new database and update your .env file with the database credentials like that:

DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=<put your db name>
DB_USERNAME=<put your db username>
DB_PASSWORD=<put your db password>

 

Creating Tables

When thinking about the tables, we need two have a users table and another table for the messages, fortunately laravel provides the users migration out of the box, so all we need is a messages migration:

php artisan make:migration create_messages_table

 

Next open database/migrations/xxxxx_create_migrations_table.php and update it as shown below:

<?php

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

class CreateMessagesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('messages', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('from_user');
            $table->unsignedInteger('to_user');
            $table->text('content');

            $table->timestamps();

            $table->foreign('from_user')->references('id')->on('users');
            $table->foreign('to_user')->references('id')->on('users');
        });
    }

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

Now run

php artisan migrate

After creating the tables let’s proceed to prepare the models.

 

Creating Models

As we already have a User model we need only to create a new model for the messages table so initiate this command in the terminal:

php artisan make:model Message
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Message extends Model
{
    protected $table = "messages";
}

Note there is no need to create a model for users as laravel already created to us out of the box.

 

Generating Authentication

Run this command to generate authentication scaffolding:

php artisan make:auth

Now the auth routes be sure that the redirectTo in LoginController, RegisterController, and ResetPasswordController like this:

protected $redirectTo = '/';

 

Preparing Pusher

To get the pusher credentials go to https://pusher.com/ and create a new account, then create a new app and get the credentials as shown in this image and update your .env file.

implementing a chat app with laravel and pusher

 

PUSHER_APP_ID=<insert app id>
PUSHER_APP_KEY=<insert app key>
PUSHER_APP_SECRET=<insert app secret>
PUSHER_APP_CLUSTER=<insert cluster>

 

 

Preparing Chat UI

We will use bootstrap for that, a chat box contains the messages in the top and text input along with a button in the bottom area in much the same way the facebook chat looks like as shown in this image:

building a chat app

 

Project Structure

implementing a chat app with laravel and pusherimplementing a chat app with laravel and pusherimplementing a chat app with laravel and pusher

 

 

 

 

 

 

 

 

As you see in the image above this is our project file structure, so let’s first create

app/Lib/PusherFactory.php

<?php

namespace App\Lib;


use Pusher\Pusher;

class PusherFactory
{
    public static function make()
    {
        return new Pusher(
            env("PUSHER_APP_KEY"), // public key
            env("PUSHER_APP_SECRET"), // Secret
            env("PUSHER_APP_ID"), // App_id
            array(
                'cluster' => env("PUSHER_APP_CLUSTER"), // Cluster
                'encrypted' => true,
            )
        );
    }
}

This simple class just return a new instance of Pusher to be used by other classes to subscribe and fire events.

 

create a new css file public/css/chat.css and update it like this:

p {
    font-size: 13px;
    padding: 5px;
    border-radius: 3px;
}

.base_receive p {
    background: #4bdbe6;
}

.base_sent p {
    background: #e674a8;
}

time {
    font-size: 11px;
    font-style: italic;
}

#login-box {
    margin-top: 20px
}

#chat_box {
    position: fixed;
    top: 10%;
    right: 5%;
    width: 27%;
}

.close-chat {
    margin-top: -17px;
    cursor: pointer;
}

.chat_box {
    margin-right: 25px;
    width: 310px;
}

.chat-area {
    height: 400px;
    overflow-y: scroll;
}

#users li {
    margin-bottom: 5px;
}

#chat-overlay {
    position: fixed;
    right: 0%;
    bottom: 0%;
}

.glyphicon-ok {
    color: #42b7dd;
}

.loader {
    -webkit-animation: spin 1000ms infinite linear;
    animation: spin 1000ms infinite linear;
}
@-webkit-keyframes spin {
    0% {
        -webkit-transform: rotate(0deg);
        transform: rotate(0deg);
    }
    100% {
        -webkit-transform: rotate(359deg);
        transform: rotate(359deg);
    }
}

For the sound and avatar you can download them from here.

Updating main layout

Open resources/views/layouts/app.blade.php and update it like below:

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>Chat App</title>

    <link href="//netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
    <link rel="stylesheet" href="{{ asset('css/chat.css') }}" />
    <script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.1.0/js/bootstrap.min.js"></script>
    <script>
        var base_url = '{{ url("/") }}';
    </script>
</head>
<body>
<div class="container-fluid">
    @yield('content')
</div>

<div id="chat-overlay" class="row"></div>

    <audio id="chat-alert-sound" style="display: none">
        <source src="{{ asset('sound/facebook_chat.mp3') }}" />
    </audio>
    @yield('script')
</body>
</html>

This is a simple layout we just added a div with id=’chat-verlay‘ this div will contain the chat boxes and also added audio element that represent the chat alert sound.

 

Next create a new view resources/views/chat-box.blade.php

<div id="chat_box" class="chat_box pull-right" style="display: none">
    <div class="row">
        <div class="col-xs-12 col-md-12">
                <div class="panel panel-default">
                    <div class="panel-heading">
                        <h3 class="panel-title"><span class="glyphicon glyphicon-comment"></span> Chat with <i class="chat-user"></i> </h3>
                        <span class="glyphicon glyphicon-remove pull-right close-chat"></span>
                    </div>
                    <div class="panel-body chat-area">

                    </div>
                    <div class="panel-footer">
                        <div class="input-group form-controls">
                            <textarea class="form-control input-sm chat_input" placeholder="Write your message here..."></textarea>
                            <span class="input-group-btn">
                                    <button class="btn btn-primary btn-sm btn-chat" type="button" data-to-user="" disabled>
                                        <i class="glyphicon glyphicon-send"></i>
                                        Send</button>
                                </span>
                        </div>
                    </div>
                </div>
        </div>
    </div>
    <input type="hidden" id="to_user_id" value="" />
</div>

The code above represents a blueprint of the chat box template, this template will be cloned according to the selected user.

 

Displaying home users

As we enter into a chat application we first see a list of users to chat with so let’s display them now:

open app/Http/Controllers/HomeController.php and update it like this:

<?php

namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $users = User::where('id', '!=', Auth::user()->id)->get();

        return view('home', compact('users'));
    }
}

Then update the routes/web.php as follows:

Route::get('/', 'HomeController@index');

Auth::routes();

Finally update resources/views/home.blade.php with this code:

@extends('layouts.app')

@section('content')
    <div class="row">
        <div class="col-md-5">
            @if($users->count() > 0)
                <h3>Pick a user to chat with</h3>
                <ul id="users">
                    @foreach($users as $user)
                        <li><span class="label label-info">{{ $user->name }}</span> <a href="javascript:void(0);" class="chat-toggle" data-id="{{ $user->id }}" data-user="{{ $user->name }}">Open chat</a></li>
                    @endforeach
                </ul>
            @else
                <p>No users found! try to add a new user using another browser by going to <a href="{{ url('register') }}">Register page</a></p>
            @endif
        </div>
    </div>

    @include('chat-box')

    <input type="hidden" id="current_user" value="{{ \Auth::user()->id }}" />
    <input type="hidden" id="pusher_app_key" value="{{ env('PUSHER_APP_KEY') }}" />
    <input type="hidden" id="pusher_cluster" value="{{ env('PUSHER_APP_CLUSTER') }}" />
@stop

@section('script')
    <script src="https://js.pusher.com/4.1/pusher.min.js"></script>
    <script src="{{ asset('js/chat.js') }}"></script>

@stop

As shown above we looped over the users to display them a long with a link that will open the chat box, currently you will see no users but try to register new users using a another browser. We also included the chat-box template and pusher javascript api and some hidden fields to be used by our javascript code.

Opening the chat box

Create a new controller called MessagesController so run this command in the terminal:

php artisan make:controller MessagesController

 

Now open app/Http/Controllers/MessagesController.php and update it as shown below:

<?php

namespace App\Http\Controllers;

use App\Lib\PusherFactory;
use App\Message;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class MessagesController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }


    /**
     * getLoadLatestMessages
     *
     *
     * @param Request $request
     */
    public function getLoadLatestMessages(Request $request)
    {
        if(!$request->user_id) {
            return;
        }

        $messages = Message::where(function($query) use ($request) {
            $query->where('from_user', Auth::user()->id)->where('to_user', $request->user_id);
        })->orWhere(function ($query) use ($request) {
            $query->where('from_user', $request->user_id)->where('to_user', Auth::user()->id);
        })->orderBy('created_at', 'ASC')->limit(10)->get();

        $return = [];

        foreach ($messages as $message) {

            $return[] = view('message-line')->with('message', $message)->render();
        }


        return response()->json(['state' => 1, 'messages' => $return]);
    }
}

In the code above we first create a new action getLoadLatestMessages, this action will be called when we open the chat box for the first time to fetch the latest messages between the two users.

 

Open resources/views/message-line.blade.php and update it as follows:

@if($message->from_user == \Auth::user()->id)

    <div class="row msg_container base_sent" data-message-id="{{ $message->id }}">
        <div class="col-md-10 col-xs-10">
            <div class="messages msg_sent text-right">
                <p>{!! $message->content !!}</p>
                <time datetime="{{ date("Y-m-dTH:i", strtotime($message->created_at->toDateTimeString())) }}">{{ $message->fromUser->name }} • {{ $message->created_at->diffForHumans() }}</time>
            </div>
        </div>
        <div class="col-md-2 col-xs-2 avatar">
            <img src="{{ url('images/user-avatar.png') }}" width="50" height="50" class="img-responsive">
        </div>
    </div>

@else

    <div class="row msg_container base_receive" data-message-id="{{ $message->id }}">
        <div class="col-md-2 col-xs-2 avatar">
            <img src="{{ url('images/user-avatar.png') }}" width="50" height="50" class=" img-responsive ">
        </div>
        <div class="col-md-10 col-xs-10">
            <div class="messages msg_receive text-left">
                <p>{!! $message->content !!}</p>
                <time datetime="{{ date("Y-m-dTH:i", strtotime($message->created_at->toDateTimeString())) }}">{{ $message->fromUser->name }} • {{ $message->created_at->diffForHumans() }}</time>
            </div>
        </div>
    </div>

@endif

The above code represents a single message sent or received by the user.

 

update routes/web.php:

Route::get('/', 'HomeController@index');

Route::get('/load-latest-messages', 'MessagesController@getLoadLatestMessages');

Auth::routes();

 

Finally we need to update public/js/chat.js with this code:

$(function () {
   let pusher = new Pusher($("#pusher_app_key").val(), {
        cluster: $("#pusher_cluster").val(),
        encrypted: true
    });

    let channel = pusher.subscribe('chat');


    // on click on any chat btn render the chat box
   $(".chat-toggle").on("click", function (e) {
       e.preventDefault();

       let ele = $(this);

       let user_id = ele.attr("data-id");

       let username = ele.attr("data-user");

       cloneChatBox(user_id, username, function () {

           let chatBox = $("#chat_box_" + user_id);

           if(!chatBox.hasClass("chat-opened")) {

               chatBox.addClass("chat-opened").slideDown("fast");

               loadLatestMessages(chatBox, user_id);

               chatBox.find(".chat-area").animate({scrollTop: chatBox.find(".chat-area").offset().top + chatBox.find(".chat-area").outerHeight(true)}, 800, 'swing');
           }
       });
   });

   // on close chat close the chat box but don't remove it from the dom
   $(".close-chat").on("click", function (e) {

       $(this).parents("div.chat-opened").removeClass("chat-opened").slideUp("fast");
   });
});


/**
 * loaderHtml
 *
 * @returns {string}
 */
function loaderHtml() {
    return '<i class="glyphicon glyphicon-refresh loader"></i>';
}

/**
 * getMessageSenderHtml
 *
 * this is the message template for the sender
 *
 * @param message
 * @returns {string}
 */
function getMessageSenderHtml(message)
{
    return `
           <div class="row msg_container base_sent" data-message-id="${message.id}">
        <div class="col-md-10 col-xs-10">
            <div class="messages msg_sent text-right">
                <p>${message.content}</p>
                <time datetime="${message.dateTimeStr}"> ${message.fromUserName} • ${message.dateHumanReadable} </time>
            </div>
        </div>
        <div class="col-md-2 col-xs-2 avatar">
            <img src="` + base_url +  '/images/user-avatar.png' + `" width="50" height="50" class="img-responsive">
        </div>
    </div>
    `;
}

/**
 * getMessageReceiverHtml
 *
 * this is the message template for the receiver
 *
 * @param message
 * @returns {string}
 */
function getMessageReceiverHtml(message)
{
    return `
           <div class="row msg_container base_receive" data-message-id="${message.id}">
           <div class="col-md-2 col-xs-2 avatar">
             <img src="` + base_url +  '/images/user-avatar.png' + `" width="50" height="50" class="img-responsive">
           </div>
        <div class="col-md-10 col-xs-10">
            <div class="messages msg_receive text-left">
                <p>${message.content}</p>
                <time datetime="${message.dateTimeStr}"> ${message.fromUserName}  • ${message.dateHumanReadable} </time>
            </div>
        </div>
    </div>
    `;
}


/**
 * cloneChatBox
 *
 * this helper function make a copy of the html chat box depending on receiver user
 * then append it to 'chat-overlay' div
 *
 * @param user_id
 * @param username
 * @param callback
 */
function cloneChatBox(user_id, username, callback)
{
    if($("#chat_box_" + user_id).length == 0) {

        let cloned = $("#chat_box").clone(true);

        // change cloned box id
        cloned.attr("id", "chat_box_" + user_id);

        cloned.find(".chat-user").text(username);

        cloned.find(".btn-chat").attr("data-to-user", user_id);

        cloned.find("#to_user_id").val(user_id);

        $("#chat-overlay").append(cloned);
    }

    callback();
}

/**
 * loadLatestMessages
 *
 * this function called on load to fetch the latest messages
 *
 * @param container
 * @param user_id
 */
function loadLatestMessages(container, user_id)
{
    let chat_area = container.find(".chat-area");

    chat_area.html("");

    $.ajax({
        url: base_url + "/load-latest-messages",
        data: {user_id: user_id, _token: $("meta[name='csrf-token']").attr("content")},
        method: "GET",
        dataType: "json",
        beforeSend: function () {
            if(chat_area.find(".loader").length  == 0) {
                chat_area.html(loaderHtml());
            }
        },
        success: function (response) {
            if(response.state == 1) {
                response.messages.map(function (val, index) {
                    $(val).appendTo(chat_area);
                });
            }
        },
        complete: function () {
            chat_area.find(".loader").remove();
        }
    });
}

This code is strait forward first we created a new pusher instance and subscribed to the chat channel, we will supply this channel in the server code shortly then we added a handler for chat-toggle link. The process is to make a copy of the chatbox html template according to the selected user exactly like facebook chat when you click on any user a chat box opened, so we accomplished this by using this function:

function cloneChatBox(user_id, username, callback)
{
    if($("#chat_box_" + user_id).length == 0) {

        let cloned = $("#chat_box").clone(true);

        // change cloned box id
        cloned.attr("id", "chat_box_" + user_id);

        cloned.find(".chat-user").text(username);

        cloned.find(".btn-chat").attr("data-to-user", user_id);

        cloned.find("#to_user_id").val(user_id);

        $("#chat-overlay").append(cloned);
    }

    callback();
}

After we cloned and opened the chat box we loaded the latest messages between the currently logged in user and the selected user using loadLatestMessages function.

Sending and receiving messages:

Open app/Http/Controllers/MessagesController.php and add this method:

/**
     * postSendMessage
     *
     * @param Request $request
     */
    public function postSendMessage(Request $request)
    {
        if(!$request->to_user || !$request->message) {
            return;
        }

        $message = new Message();

        $message->from_user = Auth::user()->id;

        $message->to_user = $request->to_user;

        $message->content = $request->message;

        $message->save();


        // prepare some data to send with the response
        $message->dateTimeStr = date("Y-m-dTH:i", strtotime($message->created_at->toDateTimeString()));

        $message->dateHumanReadable = $message->created_at->diffForHumans();

        $message->fromUserName = $message->fromUser->name;

        $message->from_user_id = Auth::user()->id;

        $message->toUserName = $message->toUser->name;

        $message->to_user_id = $request->to_user;

        PusherFactory::make()->trigger('chat', 'send', ['data' => $message]);

        return response()->json(['state' => 1, 'data' => $message]);
    }

In the above method, the most important part is pusherFactory::make()->trigger(), the trigger fires a new event with data and it takes three parameters [channel name, event name, array of data].

Now update routes/web.php:

<?php


Route::get('/', 'HomeController@index');

Route::get('/load-latest-messages', 'MessagesController@getLoadLatestMessages');

Route::post('/send', 'MessagesController@postSendMessage');


Auth::routes();

Finally update public/js/chat.js:

$(function () {
   
    ....
    
    
    // on change chat input text toggle the chat btn disabled state
    $(".chat_input").on("change keyup", function (e) {
       if($(this).val() != "") {
           $(this).parents(".form-controls").find(".btn-chat").prop("disabled", false);
       } else {
           $(this).parents(".form-controls").find(".btn-chat").prop("disabled", true);
       }
    });


    // on click the btn send the message
   $(".btn-chat").on("click", function (e) {
       send($(this).attr('data-to-user'), $("#chat_box_" + $(this).attr('data-to-user')).find(".chat_input").val());
   });

   // listen for the send event, this event will be triggered on click the send btn
    channel.bind('send', function(data) {
        displayMessage(data.data);
    });
});

/**
 * send
 *
 * this function is the main function of chat as it send the message
 *
 * @param to_user
 * @param message
 */
function send(to_user, message)
{
    let chat_box = $("#chat_box_" + to_user);
    let chat_area = chat_box.find(".chat-area");

    $.ajax({
        url: base_url + "/send",
        data: {to_user: to_user, message: message, _token: $("meta[name='csrf-token']").attr("content")},
        method: "POST",
        dataType: "json",
        beforeSend: function () {
            if(chat_area.find(".loader").length  == 0) {
                chat_area.append(loaderHtml());
            }
        },
        success: function (response) {
        },
        complete: function () {
            chat_area.find(".loader").remove();
            chat_box.find(".btn-chat").prop("disabled", true);
            chat_box.find(".chat_input").val("");
            chat_area.animate({scrollTop: chat_area.offset().top + chat_area.outerHeight(true)}, 800, 'swing');
        }
    });
}

/**
 * This function called by the send event triggered from pusher to display the message
 *
 * @param message
 */
function displayMessage(message)
{
    let alert_sound = document.getElementById("chat-alert-sound");

    if($("#current_user").val() == message.from_user_id) {

        let messageLine = getMessageSenderHtml(message);

        $("#chat_box_" + message.to_user_id).find(".chat-area").append(messageLine);

    } else if($("#current_user").val() == message.to_user_id) {

        alert_sound.play();

        // for the receiver user check if the chat box is already opened otherwise open it
        cloneChatBox(message.from_user_id, message.fromUserName, function () {

            let chatBox = $("#chat_box_" + message.from_user_id);

            if(!chatBox.hasClass("chat-opened")) {

                chatBox.addClass("chat-opened").slideDown("fast");

                loadLatestMessages(chatBox, message.from_user_id);

                chatBox.find(".chat-area").animate({scrollTop: chatBox.find(".chat-area").offset().top + chatBox.find(".chat-area").outerHeight(true)}, 800, 'swing');
            } else {

                let messageLine = getMessageReceiverHtml(message);

                // append the message for the receiver user
                $("#chat_box_" + message.from_user_id).find(".chat-area").append(messageLine);
            }
        });
    }
}

As shown in the code when we click the send btn it will call the send function to make an ajax request to the server, after that comes the magic in this line:

// listen for the send event, this event will be triggered on click the send btn
    channel.bind('send', function(data) {
        displayMessage(data.data);
    });

This code listens for the send event that we already subscribed it in the server previously so every time you send a message to server the server fires an event which in turn get caught in the javascript code, and get displayed using displayMessage() function.

 

Handling old messages on scroll up:

If we need to fetch the old messages on scrolling up like facebook we can accomplish that using the first message created date so

open app/Http/Controllers/MessagesController.php  and add this method:

/**
     * getOldMessages
     *
     * we will fetch the old messages using the last sent id from the request
     * by querying the created at date
     *
     * @param Request $request
     */
    public function getOldMessages(Request $request)
    {
        if(!$request->old_message_id || !$request->to_user)
            return;

        $message = Message::find($request->old_message_id);

        $lastMessages = Message::where(function($query) use ($request, $message) {
            $query->where('from_user', Auth::user()->id)
                ->where('to_user', $request->to_user)
                ->where('created_at', '<', $message->created_at);
        })
            ->orWhere(function ($query) use ($request, $message) {
            $query->where('from_user', $request->to_user)
                ->where('to_user', Auth::user()->id)
                ->where('created_at', '<', $message->created_at);
        })
            ->orderBy('created_at', 'ASC')->limit(10)->get();

        $return = [];

        if($lastMessages->count() > 0) {

            foreach ($lastMessages as $message) {

                $return[] = view('message-line')->with('message', $message)->render();
            }

            PusherFactory::make()->trigger('chat', 'oldMsgs', ['to_user' => $request->to_user, 'data' => $return]);
        }

        return response()->json(['state' => 1, 'data' => $return]);
    }

Yet a gain we fired a new event called ‘oldMsgs‘.

Now update routes/web.php:

<?php

Route::get('/', 'HomeController@index');

Route::get('/load-latest-messages', 'MessagesController@getLoadLatestMessages');

Route::post('/send', 'MessagesController@postSendMessage');

Route::get('/fetch-old-messages', 'MessagesController@getOldMessages');

Auth::routes();

Then open public/js/chat.js and update it as follows:

$(function () {

    ....
    
    // handle the scroll top of any chat box
    // the idea is to load the last messages by date depending of last message
    // that's already loaded on the chat box
    let lastScrollTop = 0;

   $(".chat-area").on("scroll", function (e) {
       let st = $(this).scrollTop();

       if(st < lastScrollTop) {

           fetchOldMessages($(this).parents(".chat-opened").find("#to_user_id").val(), $(this).find(".msg_container:first-child").attr("data-message-id"));
       }

       lastScrollTop = st;
   });

    // listen for the oldMsgs event, this event will be triggered on scroll top
    channel.bind('oldMsgs', function(data) {
        displayOldMessages(data);
    });
});

/**
 * fetchOldMessages
 *
 * this function load the old messages if scroll up triggerd
 *
 * @param to_user
 * @param old_message_id
 */
function fetchOldMessages(to_user, old_message_id)
{
    let chat_box = $("#chat_box_" + to_user);
    let chat_area = chat_box.find(".chat-area");

    $.ajax({
        url: base_url + "/fetch-old-messages",
        data: {to_user: to_user, old_message_id: old_message_id, _token: $("meta[name='csrf-token']").attr("content")},
        method: "GET",
        dataType: "json",
        beforeSend: function () {
            if(chat_area.find(".loader").length  == 0) {
                chat_area.prepend(loaderHtml());
            }
        },
        success: function (response) {
        },
        complete: function () {
            chat_area.find(".loader").remove();
        }
    });
}

function displayOldMessages(data)
{
    if(data.data.length > 0) {

        data.data.map(function (val, index) {
            $("#chat_box_" + data.to_user).find(".chat-area").prepend(val);
        });
    }
}

As shown in the code above i listened for scroll event then i added a condition to check if we scroll in the up direction not down then we called function fetchOldMessages() which in return will fire an event to the server.

 

Download the source code

Download the source code for Laravel 8

 

3.8 35 votes
Article Rating

What's your reaction?

Excited
14
Happy
16
Not Sure
16
Confused
30

You may also like

Subscribe
Notify of
guest

68 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Danatise
Danatise
5 years ago

Thank you for such an awesome tutorial. Really helpful! But I encountered some issues while using it, this might help the next user. 1. In Chat.Js line 67. ${message.fromUserName} is not defined. I Solved this by passing it from the controller, with this code $message->fromUserName = $message->owner->fname; 2. The messages appear in ascending order when displayed. I put the message in an array and reversed the order. Here is the code in MessageController for getLoadLatestMessages, $messages = Message::where(*****)->orderBy(‘created_at’, ‘DESC’)->limit(10)->with(‘owner’)->get(); $messagereturn = []; foreach ($messages as $message) { $messagereturn[] = view(‘message-line’)->with(‘message’, $message)->render(); } $newmessage = array_reverse($messagereturn); // necessary 3. When getting… Read more »

Angel Moran
Angel Moran
4 years ago

Excelente amigo busque por todo lados y no encontraba un tutorial tan claro como este muchas gracias

radha
4 years ago

after send message chat line not added in chat area.please help.

radha
4 years ago

PusherFactory::make()->trigger(‘chat’, ‘send’, [‘data’ => $message]); this is the issue . nothing triggred

Izunna
Izunna
4 years ago

getting error 505 internal serval error after sending a message and can’t get sent messages

Last edited 4 years ago by Izunna
Izunna
Izunna
4 years ago
Reply to  WebMobTuts

internal server error, that’s what is displayed at the console

Izunna
Izunna
4 years ago
Reply to  WebMobTuts

Rectified, did not put the files in the right directory

Muhammed Abdul Aziz Khan
Muhammed Abdul Aziz Khan
4 years ago

Please Help me,
When this application browser run then registration when submit then below the message. but not login access. why?

No users found! try to add a new user using another browser by going to Register page

shijij
4 years ago

Uncaught ReferenceError: channel is not defined
  at HTMLDocument.<anonymous> (chat.js:22)
  at c (jquery.min.js:3)
  at Object.fireWith [as resolveWith] (jquery.min.js:3)
  at Function.ready (jquery.min.js:3)
  at HTMLDocument.H (jquery.min.js:3)

i got this error

Farhan
Farhan
4 years ago
Reply to  WebMobTuts

how to add pusher javascript sdk

Soham Patel
Soham Patel
3 years ago
Reply to  WebMobTuts

Still not working. Can you help me ?

chat.js:191 Uncaught ReferenceError: channel is not defined
  at HTMLDocument.<anonymous> (chat.js:191)
  at j (jquery-1.11.1.min.js:2)
  at Object.fireWith [as resolveWith] (jquery-1.11.1.min.js:2)
  at Function.ready (jquery-1.11.1.min.js:2)
  at HTMLDocument.J (jquery-1.11.1.min.js:2)

Soham Patel
Soham Patel
3 years ago
Reply to  shijij

Were you able to solve it ? I am getting same error. Can Anyone Help me ?

Umesh Rana
Umesh Rana
4 years ago

Hi, the chat is working fine at both ends. But, it is not working in real-time. It requires to close the chat panel and re-open to get the latest message. Similarly, at the sender end after sending the message, it requires to close the chat and re-open to see the sent message. Any help to resolve the issue would be appreciated.

Umesh Rana
Umesh Rana
4 years ago
Reply to  WebMobTuts

Hi Wael,
Thank you very much for your response. Yeah, there was some issue with the Pusher configuration. Now, it is working in real-time.

mehmet
mehmet
3 years ago
Reply to  WebMobTuts

Could it be that the messages I sent are not visible and not communicating due to the pusher event or channel?

mehmet
mehmet
3 years ago
Reply to  Umesh Rana
how did you solve the problem


eatsham
4 years ago

hi,after send the message the message line didnot add in chat box ,why ?

Jcor
Jcor
4 years ago

I followed all the steps but when I write a message it does not appear automatically in the chat window, when I close the chat and reopen it if it appears and does not show me any error, what could it be?

asela
3 years ago

hello, I’m getting undefine message in chat box. messages are being set real time. both users get messages but I want to refresh the page to see the real message till then it displays “undefine

Student
Student
3 years ago

It’s work. But I can’t attach file into message.

Soham Patel
Soham Patel
3 years ago

Can anyone solve this error ? I have done excat code given. Still gettting this error. I have even installed pusher by composer.

chat.js:191 Uncaught ReferenceError: channel is not defined
  at HTMLDocument.<anonymous> (chat.js:191)
  at j (jquery-1.11.1.min.js:2)
  at Object.fireWith [as resolveWith] (jquery-1.11.1.min.js:2)
  at Function.ready (jquery-1.11.1.min.js:2)
  at HTMLDocument.J (jquery-1.11.1.min.js:2)

eko
eko
3 years ago
Reply to  WebMobTuts

very helfull.. i tried in laravel 8.3 working find.. but have to fixed Message.php because your tutorial didn’t mention this

 public function fromUser()
  {
    return $this->belongsTo(‘App\User’, ‘from_user’);
  }

  public function toUser()
  {
    return $this->belongsTo(‘App\User’, ‘to_user’);
  }

Hala
Hala
3 years ago

When sending the message i got this error

“message”: “Class ‘Pusher\\Pusher’ not found”,
   “exception”: “Symfony\\Component\\Debug\\Exception\\FatalThrowableError”,
   “file”: “/home/stscrm/public_html/STScrmApp/crmChat/app/Lib/PusherFactory.php”,
   “line”: 13,

Sithanantham
Sithanantham
3 years ago

thank you

Last edited 3 years ago by Sithanantham
kirk
kirk
3 years ago

Hi, how do I set up a way to determine if user online or not

Nik
Nik
3 years ago

I followed the steps above in my project and it worked fine on my local machine.
When I uploaded the code on the server, it first run fine but after sending 10-12 messages, it stopped working and it’s giving an error,
“Argument 1 passed to Pusher\\Pusher::__construct() must be of the type string, null given”

Harry
Harry
3 years ago

Any Source Code available

suliman babar
suliman babar
2 years ago

Hello brother, who ever you are, you are a legend, now following the bull shit tech stack and sticking to simplicity, thank you.

Nazmul
Nazmul
2 years ago

Hello sir, do u use vue js for laravel8. Thanks

nazmul
nazmul
2 years ago
Reply to  WebMobTuts

Thanks. But you provide souce code for laravel 8. and that one is only laravel 8 or vue and laravel.

Nazmul
Nazmul
2 years ago
Reply to  WebMobTuts

Hello sir i have download and install laravel 8 source code. Its work but not for realtime.

In console gives error
[Vue warn]: Cannot find element: #app

please help how to solve

Nazmul
Nazmul
2 years ago
Reply to  WebMobTuts

Thanks. successfully run this project. But When I apply this one of my existing project receiver message does not appear real time. but in pusher messages are counting.

Hassan
Hassan
2 years ago

bro how to solve this error

chat.js:20 Uncaught ReferenceError: channel is not defined
  at HTMLDocument.<anonymous> (chat.js:20:5)
  at j (jquery-1.11.1.min.js:2:27244)
  at Object.fireWith [as resolveWith] (jquery-1.11.1.min.js:2:28057)
  at Function.ready (jquery-1.11.1.min.js:2:29891)
  at HTMLDocument.J (jquery-1.11.1.min.js:2:30257)

Nirav
Nirav
2 years ago

Is it real time chat?

vijeh
vijeh
2 years ago

Hello,
I am trying to configure this chat app into my system. I found an error “Uncaught You must pass your app key when you instantiate Pusher”.
How to set this error in bugs

Musaddiq Yahya
2 years ago

message will receive after page refresh.
can you help me?
i just clone your project.
add pusher key as well.

Last edited 2 years ago by Musaddiq Yahya
Patrick Wambua
2 years ago

Hi, i have been following you tutorials and they are really great.

Currently i am trying to add the chat functionality in the CRM tutorial you created ealier.
I have done it twice.
First i did the aplication as per your tutorial(the chat app with pusher).
Then i decided to add it in the crm.

But now it is not working.I get this error in the PusherFactory.php(shown in the screenshort)

kindly can you help me out to see how i can add the chat in the crm

Screenshot from 2023-02-02 14-52-17.png
Patrick Nthiwa
2 years ago
Reply to  WebMobTuts

yes i installed pusher using the following command

composer require pusher/pusher-php-server
Patrick Nthiwa
2 years ago
Reply to  WebMobTuts

I downloaded the tutorial,its working perfectly.

How do i now integrate this in my project?for example the CRM tutorial.

do i follow the same project structure in this “chat tutorial” or i embrace the structure of the “mini-crm”?