AppDividend
Latest Code Tutorials

Laravel Vue Chat Application Tutorial

Laravel and Vue.js Chat Project 2018

4,705

Get real time updates directly on you device, subscribe now.

In this tutorial, we will build a Real-time Chat application using Laravel 5.6 and Vue.js. For this tutorial, we use Pusher real-time messaging service and at the client side, we use Laravel Echo and Pusher.js library to update our UI in real-time. In this Laravel Vue Chat Application Tutorial, we will learn how Laravel broadcast an event and Vue.js listen to that event and update the vue component in real time. This tutorial is slightly big, so please check the Github link for this project.

Laravel Vue Chat Application Tutorial

Star the project by installing fresh Laravel 5.6.

Step 1: Install Laravel 5.6.

Type the following command.

laravel new chat

# or

composer create-project laravel/laravel chat --prefer-dist

Go into the project folder.

cd chat

Install all the frontend dependencies using the following command.

npm install

Open the project in your favorite editor. Mine is VSCode.

code .

Configure the database inside .env file.

Now, create an auth scaffold provided by Laravel.

php artisan make:auth

Now, go to the terminal and create the tables from migrations.

php artisan migrate

Now, register the user at http://chat.test/register or http://localhost:8000/register

Step 2: Create models and migrations.

Now, we need to create a Message model and migration, because it stores the user’s message.

So, go to the terminal and generate the model and migration using the following command.

php artisan make:model Message -m

Next, go to the migration file and add the schema.

// create_messages_table

public function up()
{
        Schema::create('messages', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned();
            $table->text('body');
            $table->timestamps();

            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
        });
}

Migrate the schema in the database.

php artisan migrate

Step 3: Define model relationships.

We have two models.

  1. User
  2. Message

User hasMany Message.

Message belongsTo User.

So, write the relationship inside User.php file.

// User.php

public function messages()
{
    return $this->hasMany(Message::class);
}

The same Inverse relationship defined in Message.php file.

// Message.php

public function user()
{
   return $this->belongsTo(User::class);
}

Also, define the fillable fields inside Message.php field to prevent the Mass Assignment Exception.

Related Posts
1 of 36
// Message.php

protected $fillable = ['body'];

We also need to add one more field called selfMessage. This fields tells us that, whether the message is our own or the user’s we chat in.

// Message.php

protected $appends = ['selfMessage'];

Define the function that tells User that, whether the message is owned by signed in user or another user. We need to distinguish because based on a selfMessage attribute, we can assign different CSS classes to the User’s message. 

// Message.php

public function getSelfMessageAttribute()
{
    return $this->user_id === auth()->user()->id;
}

So, our whole Message.php model file looks like this.

// Message.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Message extends Model
{

    protected $fillable = ['body'];

    protected $appends = ['selfMessage'];

    public function getSelfMessageAttribute()
    {
        return $this->user_id === auth()->user()->id;
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

Step 4: Create routes and Controller.

Create a controller file called ChatController.php.

php artisan make:controller ChatController

Define the index() function in it.

// ChatController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

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

    public function index()
    {
        return view('chat');
    }
}

Create a chat.blade.php file inside views folder. Write the following code inside chat.blade.php file.

<!-- chat.blade.php -->

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Chats</div>
                <div class="card-body">
                   Chats
                </div>
            </div>
        </div>
        <div class="col-md-4">
        <div class="card">
                <div class="card-header">Users</div>
                <div class="card-body">
                   Users
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Define the routes. So write the following line inside a web.php file.

// web.php

Route::get('/chat', 'ChatController@index')->name('chat');

Go to this URL: http://chat.test/chat or http://localhost:8000/ chat.

Laravel Vue Chat Application Tutorial

Step 5: Create Vue components.

Okay, so to the terminal and hit the following command.

npm run watch

So, when we write the Vue files, it automatically compiles and put inside public >> js folder.

Inside assets  >>  js  >>   components folder, rename the ExampleComponent.vue file to ChatComponent.vue.  Also, change in the app.js file inside assets  >>  js folder.

// app.js

require('./bootstrap');

window.Vue = require('vue');

Vue.component('chat-component', require('./components/ChatComponent.vue'));

const app = new Vue({
    el: '#app'
});

Save the file, and you can see the notification saying that Laravel Mix compilation Successful.

Okay, now write the following code inside ChatComponent.vue file.

<template>
    <div class="col-md-8">
        <div class="card">
            <div class="card-header">Chats</div>
            <div class="card-body">
                Chats
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

Also, create a UserComponent.vue inside components folder.

<template>
    <div class="col-md-4">
        <div class="card">
            <div class="card-header">Users</div>
            <div class="card-body">
                Users
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

Finally, add this component inside chat  >>  index.blade.php file.

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <chat-component></chat-component>
        <user-component></user-component>
    </div>
</div>
@endsection

Now, we can break down the further components.

Inside ChatComponent.vue file, we can break to two components.

  1. ChatMessagesComponent
  2. ChatFormComponent.

So, inside components folder, create a vue component called ChatMessagesComponent.vue file.

// ChatMessagesComponent.vue

<template>
    <div class="chat__messages">
        Messages
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

Also, we need to create a form in which user can type the messages and send it to the other users.

So, inside components folder, create a vue component called ChatFormComponent.vue file.

// ChatFormComponent.vue

<template>
    <div>
        Form
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

Register both the components inside assets >>  js  >>  app.js file.

// app.js

require('./bootstrap');

window.Vue = require('vue');

Vue.component('chat-component', require('./components/ChatComponent.vue'));
Vue.component('user-component', require('./components/UserComponent.vue'));
Vue.component('chat-messages-component', require('./components/ChatMessageComponent.vue'));
Vue.component('chat-form-component', require('./components/ChatFormComponent.vue'));

const app = new Vue({
    el: '#app'
});

We can also break down ChatMessagesComponent into the further component called MessageComponent. That only display the messages. So each message renders this component.

So, create inside components folder.

// MessageComponent.vue

<template>
    <div class="chat__message">
       Message
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

Register this component inside an app.js file.

// app.js

Vue.component('message-component', require('./components/MessageComponent.vue'));

Now, update the ChatMessageComponent.vue file.

// ChatMessagesComponent.vue

<template>
    <div class="message-area">
        <message-component></message-component>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>
<style>
    .message-area {
        height: 400px;
        max-height: 400px;
        overflow-y: scroll;
        padding: 15px;
        border-bottom: 1px solid #eee;
    }
</style>

Save the file, and our output looks like this.

Vue Laravel Node.js chat application project

 

Step 6: Create a form inside ChatFormComponent.

Now, add the following code inside ChatFormComponent.vue file.

// ChatFormComponent.vue

<template>
    <div>
        <form class="form">
            <textarea 
                cols="25"
                rows="5"
                class="form-input">
            </textarea>
            <span class="notice">
                Hit Return to send a message
            </span>
        </form>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

<style>
    .form {
        padding: 8px;
    }
    .form-input {
        width: 100%;
        border: 1px solid #d3e0e9;
        padding: 5px 10px;
        outline: none;
    }
    .notice {
        color: #aaa
    }
    
</style>

We have also styled our components. So the output looks like below image.

Laravel Chat Tutorial

 

Step 7: Style the MessageComponent.

So inside MessageComponent.vue file, write the following styles.

// MessageComponent.vue

<template>
    <div class="message self">
        <strong class="user">Krunal</strong>
        <p class="body">Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cumque quaerat rem quia veniam exercitationem, commodi numquam omnis! Non placeat perspiciatis nulla illum cumque ad natus asperiores fuga. Facere, dignissimos.</p>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

<style>
    .user {
        font-weight: 800;
    }
    .body {
        margin-bottom: 0;
        white-space: pre-wrap;
    }
    .message {
        border-bottom: 1px solid #000000
    }
    .self {
        background-color: #f0f0f0;
        padding: 10px;
    }
</style>

I have written the sample chat message to see, how it looks like.

Vue chat tutorial

 

Now, a self class is a conditional class, in which we need to differentiate the messages from current signed in user’s message to the other user’s message. So we need to do it dynamically.

Step 8: Style the Users section.

Inside UserComponent.vue, add the classes and styles. I am writing this component with demo user.

// UserComponent.vue

<template>
    <div class="col-md-4">
        <div class="card">
            <div class="card-header">Users</div>
            <div class="card-body">
                <div class="users">
                    <a href="#">Krunal</a>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

<style>
    .users {
        background-color: #fff;
        border-radius: 3px;
    }
</style>

Step 9: Create MessageController.

Create a MessageController using the following command.

php artisan make:controller MessageController

Now, define the route inside a web.php file.

// web.php

Route::get('/message', 'MessageController@index')->name('message');

Inside MessageController, create an index function that returns a JSON object.

This JSON object contains all of our messages.

// MessageController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Message;

class MessageController extends Controller
{
    public function index()
    {
        $messages = Message::with(['user'])->get();

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

We use this above-defined route to send a network request from a Vue component via Axios API, and then in response, we get all the messages and display those messages.

Step 10: Use Axios to send a network request.

Okay, now we need to configure the Axios to send an AJAX request or network request to the Laravel server and fetch the messages and display that messages to the user.

We will write the Axios get request code inside ChatMessageComponent.vue file.

// ChatMessageComponent.vue

<template>
    <div class="message-area">
        <message-component></message-component>      
    </div>
</template>

<script>
    export default {
        data() {
            return {
                messages: []
            }
        },
        mounted() {
            axios.get('/message').then((response) => {
                this.messages = response.data;
            });
        }
    }
</script>
<style>
    .message-area {
        height: 400px;
        max-height: 400px;
        overflow-y: scroll;
        padding: 15px;
        border-bottom: 1px solid #eee;
    }
</style>

When the component is mounted, we send an AJAX request to the server, grab a response and assign that response to the messages property.

Now, we need to pass that messages to the <message-component>.

That component is responsible for displaying all the messages.

So, write the following code inside the template in ChatMessageComponent.vue file.

// ChatMessageComponent.vue

<template>
    <div class="message-area">
        <message-component 
            v-for="message in messages" 
            :key="message.id" 
            :message="message">
        </message-component>      
    </div>
</template>

Step 11: Display the messages.

Now, we pass the message property to the MessageComponent.vue file.

// MessageComponent.vue

<template>
    <div class="message self">
        <strong class="user">{{ message.user.name }}</strong>
        <p class="body">{{ message.body }}</p>
    </div>
</template>

<script>
    export default {
        props: ['message']
    }
</script>

I have not written the styles, but it is in there.

Save the file and go to the http://chat.test/chat or http://localhost:8000/chat

Right now, we have not any messages in the database, but when we insert in the database, then we can see all the messages here.

Step 12: Sending a message.

Now, we have not taken any submit button, so when a user hits an enter or return key the message will submit to the server. So for that, the code is the following.

First, when we send a message, we need to immediately update our User Interface. For that, we need some kind of events that tell our Vue.js app that new message has arrived, please update the User Interface.

So, inside js folder, create one file called event.js. Write the following code.

// event.js

import Vue from 'vue';

export default new Vue();

Okay, now we do need to add the message to messages array, but for that, we need a temporary message, that has following properties.

  1. ID
  2. Message Body
  3. User Name

So, when the user hits an enter, we need to create a temporary object and pass that object to an event.

Listen to that event in another Vue.js component, and that is ChatMessageComponent.

Grab the object, that we have passed with an event and add that object inside messages array.

After that, Vue.js automatically detect the changes in MessageComponent and update the User Interface.

The main reason behind using an event is because we have a data in one Vue.js component and we need to pass this data to another Vue.js component.

Now, I am writing the whole ChatFormComponent.vue file. It emits an event that has temp Object.

// ChatFormComponent.vue

<template>
    <form class="form">
        <textarea
            id="body"
            cols="28"
            rows="5"
            class="form-input"
            @keydown="typing"
            v-model="body">
        </textarea>
        <span class="notice">
            Hit Return to send a message
        </span>
    </form>
</template>

<script>

    import Event from '../event.js';

    export default {
        data() {
            return {
                body: null
            }
        },
        methods: {
            typing(e) {
                if(e.keyCode === 13 && !e.shiftKey) {
                    e.preventDefault();
                    this.sendMessage();
                }        
            },
            sendMessage() {
                if(!this.body || this.body.trim() === '') {
                    return
                }
                let messageObj = this.buildMessage();
                Event.$emit('added_message', messageObj);
                this.body = null;
            },
            buildMessage() {
                return {
                    id: Date.now(),
                    body: this.body,
                    selfMessage: true,
                    user: {
                        name: 'Krunal'
                    }
                }
            }
        }
    }
</script>

<style>
    .form {
        padding: 8px;
    }
    .form-input {
        width: 100%;
        border: 1px solid #d3e0e9;
        padding: 5px 10px;
        outline: none;
    }
    .notice {
        color: #aaa
    }
    
</style>

In temp object, I have hardcoded the username, but in a real scenario, it has a currently signed in user’s name. We will do this in a minute, to get actual user’s name.

Okay, now listen to this event inside ChatMessage.vue component.

// ChatMessageComponent.vue

<template>
    <div class="message-area">
        <message-component 
            v-for="message in messages" 
            :key="message.id" 
            :message="message">
        </message-component>      
    </div>
</template>

<script>

    import Event from '../event.js';

    export default {
        data() {
            return {
                messages: []
            }
        },
        mounted() {
            axios.get('/message').then((response) => {
                console.log(response.data);
                this.messages = response.data;
            });
            Event.$on('added_message', (message) => {
                this.messages.unshift(message);
            });
        }
    }
</script>
<style>
    .message-area {
        height: 400px;
        max-height: 400px;
        overflow-y: scroll;
        padding: 15px;
        border-bottom: 1px solid #eee;
    }
</style>

Inside view  >>  layouts  >>  app.blade.php file, write the following code inside <head> section.

<script>
        window.Laravel = {!! json_encode([
            'csrfToken'=> csrf_token(),
            'user'=> [
                'authenticated' => auth()->check(),
                'id' => auth()->check() ? auth()->user()->id : null,
                'name' => auth()->check() ? auth()->user()->name : null, 
                ]
            ])
        !!};
</script>

So, what we have done is, if the user is signed in then, we get the username and assign it to global Laravel variable.

That means, now we can access the user’s name inside Vue.js component. So when we set a temporary Message object, we can assign the name property to Laravel’s user’s name property, and we can finally see the current signed in user’s name.

Now, write this code inside ChatMessageComponent.vue file.

// ChatMessageComponent.vue

<template>
    <form class="form">
        <textarea
            id="body"
            cols="28"
            rows="5"
            class="form-input"
            @keydown="typing"
            v-model="body">
        </textarea>
        <span class="notice">
            Hit Return to send a message
        </span>
    </form>
</template>

<script>

    import Event from '../event.js';

    export default {
        data() {
            return {
                body: null
            }
        },
        methods: {
            typing(e) {
                if(e.keyCode === 13 && !e.shiftKey) {
                    e.preventDefault();
                    this.sendMessage();
                }        
            },
            sendMessage() {
                if(!this.body || this.body.trim() === '') {
                    return
                }
                let messageObj = this.buildMessage();
                Event.$emit('added_message', messageObj);
                this.body = null;
            },
            buildMessage() {
                return {
                    id: Date.now(),
                    body: this.body,
                    selfMessage: true,
                    user: {
                        name: Laravel.user.name
                    }
                }
            }
        }
    }
</script>

<style>
    .form {
        padding: 8px;
    }
    .form-input {
        width: 100%;
        border: 1px solid #d3e0e9;
        padding: 5px 10px;
        outline: none;
    }
    .notice {
        color: #aaa
    }
    
</style>

Also, The ChatMessageComponent.vue file looks like this.

// ChatMessageComponent.vue

<template>
    <div class="message-area" ref="message">
        <message-component 
            v-for="message in messages" 
            :key="message.id" 
            :message="message">
        </message-component>      
    </div>
</template>

<script>

    import Event from '../event.js';

    export default {
        data() {
            return {
                messages: []
            }
        },
        mounted() {
            axios.get('/message').then((response) => {
                console.log(response.data);
                this.messages = response.data;
            });
            Event.$on('added_message', (message) => {
                this.messages.unshift(message);
                if(message.selfMessage) {
                    this.$refs.message.scrollTop = 0;
                }
            });
        }
    }
</script>
<style>
    .message-area {
        height: 400px;
        max-height: 400px;
        overflow-y: scroll;
        padding: 15px;
        border-bottom: 1px solid #eee;
    }
</style>

So, when the current signed in user sends any message, the scroller is back to the top.

Step 13: Store the message in the database.

Define the route to store the messages.

// web.php

Route::post('/message', 'MessageController@store')->name('message.store');

Now, code the store function to store the data into MySQL database.

// MessageController.php

public function store(Request $request)
{
     $message = $request->user()->messages()->create([
         'body' => $request->body
     ]);

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

Okay, inside ChatFormComponent.vue file, write the code to send an axios post request to this web route and pass body as a parameter.

// ChatFormComponent.vue

sendMessage() {
        if(!this.body || this.body.trim() === '') {
               return
        }
           let messageObj = this.buildMessage();
           Event.$emit('added_message', messageObj);
            axios.post('/message', {
                body: this.body.trim()
            }).catch(() => {
                 console.log('failed');
            });
            this.body = null;
        },

Now, save the file and go to this URL: http://chat.test/chat or http://localhost:8000/chat

Type one message in the text area and hit the enter.

You can see an Immediate message is showing, which is our temporary message. 

Now, refresh the page and see if this message is persisted or not.

If persisted then, we have successfully saved the message.

 

Step 14: Setup Pusher: Real-time Message Service.

We use a Pusher service for real-time messages.

First, we need to change the broadcast driver inside the .env file.

// .env
BROADCAST_DRIVER=pusher

Next step is to uncomment the following inside config  >>  app.php file’s provider’s array.

App\Providers\BroadcastServiceProvider::class,

Next step is to create a pusher application.

Go to this URL: https://pusher.com/

Sign up for an account. I have signed up using Github.

Now, you will redirect to this page: https://dashboard.pusher.com.

Create an app and go to the App Keys section. Also, check your cluster.

Okay, now grab all the data and put inside the .env file.

// .env

PUSHER_APP_ID=your app id
PUSHER_APP_KEY=your app key
PUSHER_APP_SECRET=your app secret
PUSHER_APP_CLUSTER=your cluster

Now, install pusher php-server through composer. Go to the terminal and type the following command.

composer require pusher/pusher-php-server

We also need the following frontend dependencies.

  1. Pusher.js
  2. Laravel echo

Install both using the following command.

npm install pusher-js laravel-echo  --save

Now, go to the assets  >>  js >>  bootstrap.js file.

Uncomment the following lines. By default, it is commented.

// bootstrap.js

import Echo from 'laravel-echo'

window.Pusher = require('pusher-js');

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    encrypted: true
});

Now, inside .env file, there are also the variables like this.

// .env

MIX_PUSHER_APP_KEY=your pusher key
MIX_PUSHER_APP_CLUSTER=your cluster

Here, in MIX_PUSHER_KEY, you put your public key provided by Pusher, same as PUSHER_APP_KEY. 

Also, write the MIX_PUSHER_APP_CLUSTER to your cluster name.

Save the file and see the console, if you do not face any error.

Step 15: Display Online Users.

First, inside routes  >>  channels.php file, write the following code.

// channels.php

<?php

Broadcast::channel('chat', function ($user) {
    return [
        'id' => $user->id,
        'name' => $user->name
    ];
});

So, we have set up a global channel that returns a user object.

Okay, now when a user joins in the chat, we need to update our Vue.js UI.

So, Vue.js has to be notified when the new user joins in for a chat.

Ideally, we can do that using Events. So, when the new user joins in, we broadcast an event and then our frontend will listen to that event and update the User interface.

Inside resources  >>  assets  >>  js folder, create one file called echo.js.

This file listens for the all the broadcasting event and notifies the Vue.js to update the interface.

Write the following code inside an echo.js file.

// echo.js

Echo.join('chat')
    .here(users => {
        console.log(users);
    })
    .joining(user => {
        console.log(user);
    })
    .leaving(user => {
        console.log(user);
    })

Require this file inside resources  >>  assets  >>  js  >>  bootstrap.js file. I have included in the last.

// bootstrap.js

window._ = require('lodash');
window.Popper = require('popper.js').default;

try {
    window.$ = window.jQuery = require('jquery');

    require('bootstrap');
} catch (e) {}

window.axios = require('axios');

window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

let token = document.head.querySelector('meta[name="csrf-token"]');

if (token) {
    window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
    console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}

import Echo from 'laravel-echo'

window.Pusher = require('pusher-js');

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    encrypted: true
});

require('./echo');

Now, when the user is here, join or leaving, we need to emit an event. So let us do that first.

Inside echo.js file, write the following code.

// echo.js

import Event from './event';

Echo.join('chat')
    .here(users => {
        Event.$emit('users.here', users);
    })
    .joining(user => {
        Event.$emit('users.joined', user);
    })
    .leaving(user => {
        Event.$emit('users.left', user);
    })

Inside UserComponent.vue file, write the following code. We just write the code that listens for that emitted event and changes the User Interface.

// UserComponent.vue

<template>
    <div class="col-md-4">
        <div class="card">
            <div class="card-header">Users</div>
            <div class="card-body">
                <div class="users" v-for="user in users" :key="user.id">
                    <a href="#">{{ user.name }}</a>
                </div>
            </div>
        </div>
    </div>
</template>

<script>

    import Event from '../event.js';

    export default {

        data() {
            return {
                users: []
            }
        },

        mounted() {
            Event.$on('users.here', (users) => {
                this.users = users;
            })
            .$on('users.joined', (user) => {
                this.users.unshift(user);
            })
            .$on('users.left', (user) => {
                this.users = this.users.filter(u => {
                    return u.id != user.id
                });
            });
        }
    }
</script>

<style>
    .users {
        background-color: #fff;
        border-radius: 3px;
    }
</style>

Okay, now, when a new user sign into our Laravel application goes to this URL: http://chat.test/chat, we can see in the User’s box, his/her name appears. Cool!! Finally, we get a real-time update.

When a user leaves, the user’s array is filtered out to that user and we see the remaining user.

Also, when a new user is joined, we can see, the newly joined user’s name.

Step 16: Send and Receive Real-time messages.

Now, an only remaining thing of our Laravel Vue Chat Application Tutorial is that real-time messages send and receive using Pusher.

At the backend, we need to create an Event class, that broadcast an event, when the new message is created.

So, go to the terminal and type the following command to create an Event.

php artisan make:event MessageCreated

Now, when we store the message, we also need to broadcast the stored message to the channel. S

So, write the following code inside MessageController’s store function.

// MessageController.php

<?php

namespace App\Http\Controllers;

use App\Message;
use Illuminate\Http\Request;
use App\Events\MessageCreated;

class MessageController extends Controller
{
    public function index()
    {
        $messages = Message::with(['user'])->get();

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

    public function store(Request $request)
    {
        $message = $request->user()->messages()->create([
            'body' => $request->body
        ]);

        broadcast(new MessageCreated($message))
                ->toOthers();

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

Inside MessageCreated.php file, we need to take that argument and pass it to the channel.

// MessageCreated.php

<?php

namespace App\Events;

use App\Message;
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 MessageCreated implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $message;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct(Message $message)
    {
        $this->message = $message;
    }

    public function broadcastWith()
    {
        $this->message->load(['user']);

        return [
            'message' => array_merge($this->message->toArray(), [
                'selfMessage' => false
            ])
        ];
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PresenceChannel('chat');
    }
}

First, we have implements the ShouldBroadCast interface.

Then, I have changed the name from PrivateChannel to PresenceChannel and pass our channel name “chat”.

Then, we are receiving the message from a controller and broadcast that message via broadcastWith() function.

Now, inside echo.js file, we need to add a code that listens to this event.

// echo.js

import Event from './event';

Echo.join('chat')
    .here(users => {
        Event.$emit('users.here', users);
    })
    .joining(user => {
        Event.$emit('users.joined', user);
    })
    .leaving(user => {
        Event.$emit('users.left', user);
    })
    .listen('MessageCreated', (data) => {
        Event.$emit('added_message', data.message);
    });

After listening, it broadcast another event and that is listened by our Vue.js component ChatMessageComponent.vue.

// ChatMessageComponent.vue

<template>
    <div class="message-area" ref="message">
        <message-component 
            v-for="message in messages" 
            :key="message.id" 
            :message="message">
        </message-component>      
    </div>
</template>

<script>

    import Event from '../event.js';

    export default {
        data() {
            return {
                messages: []
            }
        },
        mounted() {
            axios.get('/message').then((response) => {
                this.messages = response.data;
            });
            Event.$on('added_message', (message) => {
                this.messages.unshift(message);
                if(message.selfMessage) {
                    this.$refs.message.scrollTop = 0;
                }
            });
        }
    }
</script>
<style>
    .message-area {
        height: 400px;
        max-height: 400px;
        overflow-y: scroll;
        padding: 15px;
        border-bottom: 1px solid #eee;
    }
</style>

Finally, if everything code correctly then we can test our Laravel Vue Chat Application Tutorial application by the following.

  1. Open this URL: http://chat.test/chat
  2. If you are not signed in that, you will redirect to signin and after that, you will arrive at this page.
  3. Open the above URL in incognito mode: http://chat.test/chat
  4. Register with another user and arrive at this page.
  5. You will see at User’s section, two users are online
  6. Now, type the message and hit enter.
  7. At both chat window, our message is updated without any refresh.
  8. If another chat window user reply with a message then also we get that message.
  9. Both sender and receiver’s message background color is different based on currently signed in user.
  10. Finally, Laravel Vue Chat Application Tutorial is over.

I have put the code on Github. You can check that out as well.

Github Code

How to use this Github Code.

  1. Clone this repository.
  2. Go to the project
  3. Type this command:  composer update
  4. Then this command: npm install
  5. Change the BROADCAST_DRIVER=pusher in the .env file.
  6. Create a pusher app and grab the app key, secret, id, and cluster.
  7. Put into the .env file. You need to put for client and server. Remember in the .env file, you need to put twice, there are already variables are defined by laravel, you just need to put the values.
  8. Migrate the database tables from this command: php artisan migrate
  9. Start the development server: php artisan serve
  10. Register two users.
  11. Go to this URL: http://localhost:8000/chat.

 

5 Comments
  1. Tirex says

    Excellent tutorial. Thanks a lot for detailed steps. (Sorry, English is not my native language)…Thanks a lot

  2. Ichsan says

    github code not working, message saved but not realtime, and no users appeared

    1. Krunal says

      Actually, you need to follow the steps, configure the pusher api. It is working fine, please check your configuration.

      1. ahmed says

        Everything is right but there is error with broadcast auth

        POST http://35.204.40.203/broadcasting/auth 500 (Internal Server Error)

        1. Rather says

          Before all, many thanks to Kurnal,

          @Ichsan, @Ahmed i ran into the same issues, after debugging i found that my clock on Homestead was running behind, and messing up with the pusher,

          Hey Kurnal, what about a tutorial for Machine lerning – Laravel implementation, that will be swell!

Leave A Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.