Chat with Laravel – Pusher and Socket.io at your command

From time to time we are asked to integrate a real-time chat application or real-time notifications in our system. While this can be painful there is no actual need to be afraid of the time consumption. With this tutorial I’ll try to show you how to create a very basic chat application tutorial which can be easily transformed into any type of notification or real-time data synchronisation without much effort or coding using either Pusher or Socket.io.

What will our goal be?

Our main goal will be understanding how to push an event from our back-end server to our front-end page without a need of page refresh or tedious API calls every X time unit. As a side bonus, we will actually make a chat system that will allow us to chat one on one with any user in the system via simple chat logic.

Preparations for the development

The obvious – Laravel project and everything that is required to run it

Things to consider

Before we start, I’d like to give some comparisons between two services – Pusher and Socket.io which might help you to decide which is more suited for you.

Pusher:This is an all out of the box SaaS solution for your needs. Very simple to set up, use and debug as you will have a full dashboard available for you with different measures, logs and details. The only drawback I could see – it is paid service and could get quite pricy if used in big applications.

Socket.io:This is a DYI service that will enable you to have multiple ports open, will work on different systems and will not tie you to any SaaS product as everything is configurable and it runs on node. While this if a free service, you will have to manually run a service that will sync events so this option might be limiting factor if you are running in a shared server or you don’t have an access to servers services.

Preparing back-end

I’ll start with back-end preparations which will include:

Database for users, messages (includes Models, Migrations)

User registration and initial page loading

API Endpoint connections (to accept/return messages)

Events management

This might seem scary but we need only few things in each category to get our application up and running with our basic example.

Auth scaffolding

To start with the preparation, we will use the Laravel auth scaffolding which will give us – registration, login pages and unauthorised and authorised view pages. This is helpful as we will be re-using them.Run this command:

php artisan make:auth

.env settings

We need to prepare out Laravel instance for pusher or socket.io usage by changing these values:

In the index method we are going to get all messages that belongs to the authorised user and the selected user as either recipient or sender.As for saving message – it is a very simple saving with broadcasting of event which we will cover next.

Broadcasting an event

In order to listen for any action of the server via sockets – we need to broadcast an event. This event will contain the message details and will be directly channeled to the user that has to receive the message. Of course there are many other ways to do this but for the sake of simplicity – I’ll be demonstrating the easiest way without any authentication and with semi-public channel as there are quite a few tutorials that show you how to setup authenticated events. One of them:https://laravel.com/docs/master/broadcasting#authorizing-channels

Lets make a new event:

php artisan make:event MessageSent

This will make a file in: app/Events/MessageSent.php Lets open it and broadcast our message:

With this, we are saying that the event needs a Message instance to get and it’s public to be accessed by anyone listening to this event. Next, we are broadcasting it on the channel that is formatted like this:

newMessage-[Sender ID]-[Receiver ID]

Which tells our system that we should listen it in this format:

newMessage-[ID of the user that we are chatting with]-[Our ID]

That is it – we have our Event logic prepared. Now we are going to start on implementing it in our system, which in the end will work as a simple chat.

Routes

As for the routes, we only need api routes to be created as normal ones were created when we launched the make:auth command. So, open the web.php file and add these lines at the end of it. (we are not using full api in order to prevent unsecured usage of it)

Tha is it – we are initialising our VueJS component called ChatApplication and it will take care of displaying user list and chat messages. Feel free to modify this to your needs as the main part is the component call here.

Javascript

The javascript is going to be our main system so this part is going to be a bit longer than the previous ones. I’ll try to explain everything we will have going on there as much as I can.Keep in mind that I’ll separate code blocks for both Pusher and Socket.io that you wouldn’t have to use any of the code you don’t need.

NPM packages for pusher:

We will only need a single package – official pusher library here:

npm install pusher-js

NPM packages for Socket.io:

With socket.io we are going to use Laravel Echo service in order to handle the events in more convenient way. Please keep in mind that Laravel Echo can be used with Pusher but I intentionally kept it as native as possible.

Building the Javascript application

Next thing is our resources/js/app.js modification, where we will add our new component and assign “userID” to our logged in users ID:

/**
* 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')
window.Vue = require('vue')
/**
* 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.
*/
Vue.component('example-component', require('./components/ExampleComponent.vue'))
Vue.component('chat-application', require('./components/ChatApplication.vue'))
const app = new Vue({
el: '#app',
data: {
userID: null
},
mounted () {
// Assign the ID from meta tag for future use in application
this.userID = document.head.querySelector('meta[name="userID"]').content
}
})

And while we are here, on the core of our application – lets set up bootstrap methods so that it would load plugins needed for Pusher or Socket.io:

Pusher:

window._ = require('lodash')
window.Popper = require('popper.js').default
// Pusher
window.Pusher = require('pusher-js')
/**
* We'll load jQuery and the Bootstrap jQuery plugin which provides support
* for JavaScript based Bootstrap features such as modals and tabs. This
* code may be modified to fit the specific needs of your application.
*/
try {
window.$ = window.jQuery = require('jquery')
require('bootstrap')
} catch (e) {
}
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
window.axios = require('axios')
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
/**
* Next we will register the CSRF Token as a common header with Axios so that
* all outgoing HTTP requests automatically have it attached. This is just
* a simple convenience so we don't have to attach every token manually.
*/
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')
}
/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/

And now, we are ready to go with our application main file – VueJS component. It will handle all of our UI/UX display, users list loading, messages loading and message sending with few methods. I’ve tried to keep the names clear as much as possible so you shouldn’t be able to get lost. Not to mention that the file itself is around 150 lines long.

That is it. If you were to run the following commands on your server – you may load the Laravel page and register to it (I recommend you to register at least two accounts, so you could chat using different browsers or incognito mode).

npm run prod
// If using socket.io - run next commands, if not - skip them.
laravel-echo-server start
// If not running:
redis-server

After loading the Laravel project, you should see this:

Users list on the left and no messages.

Now if you click on the user, you may send him some messages:

As you can see, we have an input and a button to send a message

And the user will receive a message from you in real time – no more delays.

That is it – you now have a fully working chat application that you might extend to your needs or use the same logic to display real-time notifications and other messages/items to users. For example, you can implement this to track a scoreboard in real time – just append the data received from event to a table and you will be good to go.

How this can be used or extended

While this chat application example is working – it is by no means recommended to be used in production as it is the basic example with no actual data protection nor usable and responsive UX. That said, you are free to extend this with your needs, here are some of my recommendations:

Nicer UI/UX solution

Indicators for new messages in chats

Typing indicator using “whisper” functionality

Group chats (this needs rework in the database level)

Protected API endpoints

Protected Event listeners/Broadcasters

Attachment uploads

All in all, you should get a feel of how the real-time chat applications work and how to handle the sockets on your front-end and back-end.

Hey, without any screenshots/details its hard to tell you what might be wrong in your case… At the moment it would either suggest that the controller is working incorrectly or something else is a bit off

Hi There! I was trying to implement this feature on my existing application. But here’s a thing, I have a “home” view inside my user folder. There was no “Home” in the “view” folder itself. And I don’t know if it’s happening because of that reason or what, but I can not see “” working.

It’s a bit unclear on what are you trying to say. Could you add screenshots, error logs or something to clarify what is the issue? IT sounds like you have a misconfigured routes but that might not be it

Sorry for overdue reply, for some reason I haven’t received a notification!

Laravel can’t really open two home.blade.php files but for this application – you can place the code in any that is opening as that should not bring any issues. To code can be moved anywhere, with modifications of paths if it is needed

For some reason, I don’t receive the message on the other end.
If I send a message, it stores to database nicely, but the receiver is not receiving in real-time, I can only see the message when restarting the page and then join the channel again.
Laravel echo only shows joined or left the channel:

As for protection – you could check pushers documentation – it has a nice authentication documentation for channels. For the api – I’d recommend a passport from laravel. It can be used directly within same application with consume own api flag setted up

Following your guide, I now wonder how one should change it to a private channel? What would be the authentication rule? As now I can listen to any channel available and output the messages between other users.
I’m just asking this because I am a beginner and trying to learn things

That really depends. I’m not aware of your needs so it would be hard to recommend anything. My guess would be that you could add a specific user to the system where everyone would chat with them OR add some kind of a condition for users display (maybe from back-end on data return).

In short, there are many ways to do that and the only solution is to… Experiment 🙂