X
    Categories: Learn Laravel Tutorials, Tips And Guides

Create a Realtime Chatroom with Laravel, VueJS and Pusher

In today’s article, I am going to create a chatroom using Laravel 5.4, Pusher and VueJs. Since these tools are popular and almost every developer has heard of them, I will skip the theoretical introductions and discussions and go straight to the fun part.

Here is a list of components that I will use for this application:

  • Laravel Auth
  • Laravel Mix
  • Laravel Broadcast
  • Pusher for Laravel and VueJS
  • Bootstrap

I recommend that you launch a Cloudways hosted Laravel application for testing the code. If you do not have an account, to sign up for a trial account.

(Note: You may use promo code: PHP15 to get FREE Cloudways hosting credit of $15 on signup.)

Check out the following GIF for server and application launch.

The complete for this application code can be found in the github repo and the live demo can be found here.

Setup User Authentication

The first step is the migration of the existing user table through the following command:

php artisan migrate

Next, create a basic auth by running the following command:

php artisan make:auth

Create a factory for creating a few users. For this, head to database/factories and open ModelFactory.php. Paste the following code in it.

$factory->define(App\User::class, function (Faker\Generator $faker) {

   static $password = "ahmedkhan";

   return [

       'name' => $faker->name,

       'email' => $faker->unique()->safeEmail,

       'password' => bcrypt($password) ?: $password = bcrypt('secret'),

       'remember_token' => str_random(10),

   ];

});

I will now create several users through the following command that will start up Tinker:

php artisan tinker

Now. run the following command to create ten users.

factory('App\User',10)->create();

Next up is the broadcaster which will broadcast the chat message(s).

Broadcaster For Chat Messages

I will use a database for queues in broadcast. I will first create migration table and then execute the actual migration.

Run the following commands in the terminal for creating the migration tables for queues:

php artisan queue:table

php artisan queue:failed-table

Now run the following command for the migration:

php artisan migrate

I will now create a Broadcast event for chat message. This event will broadcast the chat message when a user sends a new message to another user.

Run the following command to create the event:

php artisan make:event ChatMessage

Go to app/Events folder and open ChatMessage.php.

Start by implementing the class with ShouldBroadcast:

class ChatMessage implements ShouldBroadcast

Next, create a new public variable `$user`. Replace the constructor with the following:

public function __construct($user)

   {

       //

       $this->user = $user;

   }

And, replace `broadcastOn` with the following:

   

public function broadcastOn()

   {

       return ['chat-message'.$this->user['id']];

   }

What I am doing here is broadcasting the user message on ‘chat-message’.$this->user[‘id’] where $this->user[‘id’] is the id of the user to whom the message is being sent.

The final ChatMessage will look like this:

<?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 ChatMessage implements ShouldBroadcast

{

   use Dispatchable, InteractsWithSockets, SerializesModels;

   public $user;

   /**

    * Create a new event instance.

    *

    * @return void

    */

   public function __construct($user)

   {

       //

       $this->user = $user;

   }

   /**

    * Get the channels the event should broadcast on.

    *

    * @return Channel|array

    */

   public function broadcastOn()

   {
       return ['chat-message'.$this->user['id']];
   }

}

The broadcaster is now ready. I will now setup Pusher, that I will use for broadcasting messages.

Run the following command to install Pusher for PHP:

composer require pusher/pusher-php-server

Now in the .env file, set the values for the following items:

BROADCAST_DRIVER=

PUSHER_APP_ID=

PUSHER_APP_KEY=

PUSHER_APP_SECRET=

Next, I will create the chat controller.

The Chat Controller

Run the following command in the console:

php artisan make:controller ChatController

Now in the controller class, first create a constructor which contains auth middleware. This will let only the authenticated users access the chat.

   

/**

    * Create a new controller instance.

    *

    * @return void

    */

   public function __construct()

   {

       $this->middleware('auth');

   }


Now create a new function which sends the chat message:

/**

    * Send chat message

    * @param $request

    * @return void

    */

   public function sendMessage(Request $request)

   {

       $message = [

           "id" => $request->userid,

           "sourceuserid" => Auth::user()->id,

           "name" => Auth::user()->name,

           "message" => $request->message

       ];

       event(new ChatMessage($message));

       return "true";

   }


This function is simple. The `id` is the id of the user to whom the message is being sent. `sourceid` is the user id who is sending the message and `name` is the username. `message` is the chat message. Next, comes the actual broadcasting that returns true.

I will now create a function which will open a chatroom.

Start by creating a new function `chatPage()`:

public function chatPage()

   {

       $users = User::take(10)->get();

       return view('chat',['users'=> $users]);

   }

The final version of ChatController.php will look like this:

<?php

namespace App\Http\Controllers;


use Illuminate\Http\Request;

use App\User;

use Auth;

use App\Events\ChatMessage;


class ChatController extends Controller

{

   /**

    * Create a new controller instance.

    *

    * @return void

    */

   public function __construct()

   {

       $this->middleware('auth');

   }

   /**

    * Send chat message

    * @param $request

    * @return void

    */

   public function sendMessage(Request $request)

   {

       $message = [

           "id" => $request->userid,

           "sourceuserid" => Auth::user()->id,

           "name" => Auth::user()->name,

           "message" => $request->message

       ];

       event(new ChatMessage($message));

       return "true";

   }

   public function chatPage()

   {

       $users = User::take(10)->get();

       return view('chat',['users'=> $users]);

   }

}

The controller is ready for use.

Creating the Chatroom

Go to resources/views and create a new file called chat.blade.php. Paste the following code in it. This code is a mixture of VueJS and blade:

 

@extends('layouts.app')

@section('content')

<div class="row">

   <div class="col-md-2">

       <ul class="list-group">

       @foreach($users as $chatuser)

           <li v-on:click="getUserId" class="list-group-item" id="{{ $chatuser->id }}" >
Here is how this code works:

I started with adding the list of users to whom you can send messages:
@foreach($users as $chatuser)

           <li v-on:click="getUserId" class="list-group-item" id="{{ $chatuser->id }}" >
Notice that I have binded `getUserId` function using VueJS. I will write this function in the app.js file.  

Next, the chat window is binded using VueJS variable `chatWindows`. It handles all the chat windows that the user is using. It will only be opened when you click a user for sending a message  or some other user send a message to you. Next is the `chats` variable which handles the chat for that window. 
<div class="col-md-4" v->
I have also binded `chatMessage` function. 

I will now create routes .Go to the routes folder and open web.php file. Paste the following routes in it.
Route::post('/chat','ChatController@sendMessage');

Route::get('/chat','ChatController@chatPage');

The frontend is done. I will now create function in VueJS that will handle the chats. .

Write Functions to Handle Chat in VueJS

Before getting started with VueJS. make sure you have npm. Run the following command to install all the packages:

npm install

Now run the following command to install VueResource, which I will use for HTTP requests.

npm install vue-resource

Now go to resources/assets/js and open app.js file. Replace the code in it with the following:

/**

* First we will load all of this project's JavaScript dependencies which

* includes Vue and other libraries. It is a great starting point when

* building robust, powerful web applications using Vue and Laravel.

*/

require('./bootstrap');

/**

* Next, we will create a fresh Vue application instance and attach it to

* the page. Then, you may begin adding components to this application

* or customize the JavaScript scaffolding to fit your unique needs.

*/

import VueResource from "vue-resource"

import Echo from "laravel-echo"

import Pusher from "pusher-js"


Vue.use(VueResource);


window.Echo = new Echo({

   broadcaster: 'pusher',

   key: 'adba74bea688557ca' //Add your pusher key here

});


const app = new Vue({

   el: '#app',

   data: {

       chatMessage : [],

       userId : null,

       chats : [],

       chatWindows : [],

       chatStatus : 0,

       chatWindowStatus : [],

       chatCount : []

   },

   created(){

       window.Echo.channel('chat-message'+window.userid)

           .listen('ChatMessage', (e) => {

               console.log(e.user);

               this.userId = e.user.sourceuserid;

               if(this.chats[this.userId]){

                   this.show = 1;

                   this.$set(app.chats[this.userId], this.chatCount[this.userId] ,e.user);

                   this.chatCount[this.userId]++;

                   console.log("pusher");

                   console.log(this.chats[this.userId]);                   

               }else{

                   this.createChatWindow(e.user.sourceuserid,e.user.name)

                   this.$set(app.chats[this.userId], this.chatCount[this.userId] ,e.user);

                   this.chatCount[this.userId]++;

               }            

       });

   },

   http: {

       headers: {

           'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')

       }

   },

   methods: {

   sendMessage(event){

           this.userId = event.target.id;

           var message = this.chatMessage[this.userId];

           

           this.$http.post('chat',{

               'userid' : this.userId,

               'message' : message

           }).then(response => {

               this.chatMessage[this.userId] = '';

               this.$set(app.chats[this.userId], this.chatCount[this.userId] , {

                   "message": message,

                   "name" : window.username

           });

           this.chatCount[this.userId]++;

               console.log("send");

           }, response => {

               this.error = 1;

               console.log("error");

               console.log(response);

           });

       },

   getUserId(event){

       this.userId = event.target.id;

       this.createChatWindow(this.userId,event.target.innerHTML);

       console.log(this.userId);

   },

   createChatWindow(userid,username){

       if(!this.chatWindowStatus[userid]){  

           this.chatWindowStatus[userid] = 1;

           this.chatMessage[userid] = '';

           this.$set(app.chats, userid , {});

           this.$set(app.chatCount, userid , 0);

           this.chatWindows.push({"senderid" : userid , "name" : username});

       }    

   }

}});


Next, run the following command to compile it.

npm run dev

This is how this code works:

First I imported Pusher and vue-resource components:

import VueResource from "vue-resource"

import Echo from "laravel-echo"

import Pusher from "pusher-js"

Next, I added vue-resource to VueJS and initialized Laravel Echo:

Vue.use(VueResource);

window.Echo = new Echo({

   broadcaster: 'pusher',

   key: 'adba74bea68819d557ca' //Add your pusher key here

});

I then created VueJS instance in which first I defined the id of the div in which the app would load.

Next, I defined the variables which I will use in this app:

const app = new Vue({

   el: '#app',

   data: {

       chatMessage : [],

       userId : null,

       chats : [],

       chatWindows : [],

       chatStatus : 0,

       chatWindowStatus : [],

       chatCount : []

   },

Then, I added Laravel Echo to listen for broadcast events in `created()` method (which will work when the Vue app is fully created):

   

created(){

       window.Echo.channel('chat-message'+window.userid)

           .listen('ChatMessage', (e) => {

               console.log(e.user);

               this.userId = e.user.sourceuserid;

               if(this.chats[this.userId]){

                   this.show = 1;

                   this.$set(app.chats[this.userId], this.chatCount[this.userId] ,e.user);

                   this.chatCount[this.userId]++;

                   console.log("pusher");

                   console.log(this.chats[this.userId]);                   

               }else{

                   this.createChatWindow(e.user.sourceuserid,e.user.name)

                   this.$set(app.chats[this.userId], this.chatCount[this.userId] ,e.user);

                   this.chatCount[this.userId]++;

               }       

       });

   },



This function gets the message, and first checks whether an existing chat window is already open for that user. If yes, it sends the message to that chat window. If the chat window does not exists, it will create a new chat window, open it and display the message. Next, I defined ‘X-CSRF-TOKEN’ header for the HTTP requests.

I also created the methods which will handle sending of the message and creation of new chat window for the users.

methods: {

   sendMessage(event){

           this.userId = event.target.id;

           var message = this.chatMessage[this.userId];

           this.$http.post('chat',{

               'userid' : this.userId,

               'message' : message

           }).then(response => {

               this.chatMessage[this.userId] = '';

               this.$set(app.chats[this.userId], this.chatCount[this.userId] , {

                   "message": message,

                   "name" : window.username

           });

           this.chatCount[this.userId]++;

               console.log("send");

           }, response => {

               this.error = 1;

               console.log("error");

               console.log(response);

           });

       },

   getUserId(event){

       this.userId = event.target.id;

       this.createChatWindow(this.userId,event.target.innerHTML);

       console.log(this.userId);

   },

   createChatWindow(userid,username){

       if(!this.chatWindowStatus[userid]){

           

           this.chatWindowStatus[userid] = 1;

           this.chatMessage[userid] = '';

           this.$set(app.chats, userid , {});

           this.$set(app.chatCount, userid , 0);

           this.chatWindows.push({"senderid" : userid , "name" : username});

       } 

   }

}

The application code is finished. I will now test it.  

Testing the Chat Room

Open the login page in three separate browser instances and sign in with any user that you created by the faker.

Now click on any (logged in) user and send a message. In this case, I sent a message to myself from another user. Currently, all browser windows look like this:

I will now send a message to an online user.

Message will be received by that user and a chat window will open.

Now send a message to another user:

And keep sending message to the users.  

Final Words

In this article, I discussed how you could easily setup a realtime private chatroom using Laravel, VueJS and Pusher. This application is very basic and could use several improvements such as saving the messages and filtering users on the basis of their online status.

If you need further help with the code or the application setup, do leave a comment and I will get back to you ASAP.

 

Ahmed Khan: Ahmed was a PHP community expert at Cloudways - A Managed PHP Hosting Cloud Platform. He is a software engineer with extensive knowledge in PHP and SEO. He loves watching Game of Thrones is his free time. Follow Ahmed on Twitter to stay updated with his works. You can email him at ahmed.khan@cloudways.com