Quick and active customer service is an integral part of any company or organization, especially those that conduct most of their business online. Quality customer service goes beyond merely fielding comments and questions from customers. The types of interactions your team has with your clients have a direct impact on how your customers perceive, use, and pay for your service.
In this tutorial, we'll build a live chat using Laravel, Vue.js and Stream Chat.
Laravel is a free, open-source PHP web framework, and was created for the development of web applications following the model-view-controller (MVC) architectural pattern and based on Symfony. It offers a lot of great features and presets that make developing backend applications easier and better.
Vue.js is a lightweight front-end framework used for building web applications. Laravel also supports it out of the box.
Stream Chat is an API for building scalable, enterprise-grade chat applications. The service removes all of the hassles of creating such sophisticated features from you and lets you focus on getting your service up and running quickly.
Prerequisites
To follow this tutorial, a basic understanding of Laravel, Vue.js, and JavaScript are required. To build the required application, here are a few tools we will use:
- Laravel
- Vue.js
- Stream Chat (free 14-day trial available – no credit card required)
Getting Started
To get started, we'll be using the Laravel installer to create a new Laravel application. If you don't already have the Laravel installer setup on your computer, you can follow this installation guide. Run the following command to create a new Laravel application:
laravel new stream-laravel-vue-livechat && cd stream-laravel-vue-livechat
Once the application is created, we need to install the NPM dependencies (because Vue.js comes pre-packed as an NPM dependency). In your terminal type:
npm install
For this tutorial, we'll be concerned with the resources/js directory, which is where Vue.js will be instantiated from.
Now, we can start creating our application.
1. Installing the Stream Chat PHP Client
To start working with Stream Chat and Laravel, we need to install the Stream Chat PHP Client. For that, run the following command in your terminal:
composer require get-stream/stream-chat
2. Installing the Stream Chat JavaScript Client
To be able to use Stream Chat on our Vue.js front-end, we need to install the JavaScript client. Run the following in your terminal:
npm install stream-chat
Once that is done, we need to configure our app to work with it. To start working with Stream Chat and Laravel, we need our API key.
Create an account on Stream Chat if you don't have one already. On your dashboard, create a new app by clicking the create app button.
Once that is done, you should see an API Key, API Secret and App ID (at the top) of your newly created app under the chat tab of the navigation.
Copy them and add them to your .env file in your Laravel project. To be able to access these environment variables in our front-end, we need to prefix them with MIX_:
MIX_STREAM_API_KEY=YOUR_STREAM_KEY MIX_STREAM_API_SECRET=YOUR_STREAM_SECRET
3. Creating the Base View and Controller
Since we already ran the npm install command when we installed Laravel, we don't need to define it anymore. Instead, we need to create a base content that will be displayed by Laravel. Think of the default welcome view but this time done with Vue.js.
Inside the resources/js directory, add the following code to app.js:
require('./bootstrap');
import "../sass/chat.sass";
window.Vue = require('vue');
window.axios = require('axios');
window.VueRouter=require('vue-router').default;
const Chat = Vue.component('chat', require('./components/Chat.vue').default);
const Admin = Vue.component('admin', require('./components/AdminChat.vue').default);
Vue.prototype.$baseurl = "https://localhost:8000";
window.router = new VueRouter({
routes:[
{
path: '/',
name: 'chat',
component: Chat,
},
{
path: '/admin',
name: 'admin',
component: Admin,
}
],
})
const app = new Vue({
router,
el: '#app',
});
Here, we instantiated our components and defined the routes for our application. Since we have not created the components yet, our app will throw an undefined error. Let's create them.
To differentiate between our chat screens (Admin & User), we'll create the two components: Chat and AdminChat inside resources/js/components.
Create resources/js/components/Chat.vue and add the following code to it:
// resources/js/components/Chat.vue
<template>
<div id="chat">
<button class="btn btn-primary c-chat-widget-button" ref="button" @click.prevent="toggleModal()">C</button>
<div class="c-chat-widget" ref="modal" :class="{show: modal.show}">
<div class="c-chat-widget-dialog">
<div class="c-chat-widget-content">
<div class="c-chat-widget-header">Chat With Us Admin</div>
<div class="c-chat-widget-body">
<div class="c-chat-widget-bubble c-chat-widget-bubble-left row" v-for="msg in messageData">
<div class="c-chat-widget-bubble-icon">{{msg.user.id}}</div>
<div class="c-chat-widget-bubble-text">
{{msg.text}}
</div>
</div>
</div>
<div class="c-chat-widget-footer">
<form @submit.prevent="sendmessage">
<textarea name="" id="" cols="30" v-model="message" rows="10" class="c-chat-widget-text" placeholder="Enter Text Here"></textarea>
<button class="btn btn-block btn-success">Send Message</button>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
modal: {
show: false,
},
message: '',
messageData: []
}
},
mounted() {
},
methods: {
toggleModal() {
this.modal.show = !this.modal.show;
},
showModal() {
this.modal.show = true;
},
hideModal() {
this.modal.show = false;
},
}
</script>
Next, create resources/js/components/AdminChat.vue and add the following code to it:
// resources/js/components/AdminChat.vue
<template>
<div id="chat">
<button class="btn btn-primary c-chat-widget-button" ref="button" @click.prevent="toggleModal()">C</button>
<div class="c-chat-widget" ref="modal" :class="{show: modal.show}">
<div class="c-chat-widget-dialog">
<div class="c-chat-widget-content">
<div class="c-chat-widget-header">Chat With Us Admin</div>
<div class="c-chat-widget-body">
<div class="c-chat-widget-bubble c-chat-widget-bubble-left row" v-for="msg in messageData">
<div class="c-chat-widget-bubble-icon">{{msg.name.id}}</div>
<div class="c-chat-widget-bubble-text">
{{msg.text}}
</div>
</div>
</div>
<div class="c-chat-widget-footer">
<form @submit.prevent="sendmessage">
<textarea name="" id="" cols="30" v-model="message" rows="10" class="c-chat-widget-text" placeholder="Enter Text Here"></textarea>
<button class="btn btn-block btn-success">Send Message</button>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
modal: {
show: false,
},
message: '',
messageData: []
}
},
mounted() {
},
methods: {
toggleModal() {
this.modal.show = !this.modal.show;
},
showModal() {
this.modal.show = true;
},
hideModal() {
this.modal.show = false;
},
}
</script>
Next, let's create the chat.sass file inside the resources/sass directory and add the following to it. This will serve as the necessary styling for our application:
// resources/sass
#chat
font-family: arial
.c-chat
&-widget
bottom: calc(50px + 30px + 50px)
display: block
opacity: 0
position: fixed
right: 50px
transform: translate(100%, 100%)
transition: .5s
z-index: 5000
&.show
opacity: 1
transform: translate(0)
&-content
background: #eee
box-shadow: 0 10px 30px rgba(#000, .2)
border-radius: 3px
max-width: 100%
overflow: hidden
width: 400px
&-header
background: #007bff
color: #fff
padding: 10px 15px
&-body
padding: 10px 15px
&-footer
padding: 10px 15px
&-button
border-radius: 999px
bottom: 50px
cursor: pointer
height: 50px
line-height: 50px
padding: 0
position: fixed
right: 50px
text-align: center
width: 50px
z-index: 5050
&-text
border: 1px solid #eee
border-radius: 3px
height: 100px
outline: none !important
padding: 10px 20px
width: 100%
&-bubble
background: #fff
border-radius: 3px
box-shadow: 0 3px 10px rgba(#000, .1)
margin-bottom: 15px
max-width: 96%
padding: 10px
&-icon
border-radius: 3px
color: #aaa
display: inline-block
flex: 0 0 30px
font-size: 12px
font-weight: 900
margin-right: 10px
text-align: center
vertical-align: middle
&-text
border-left: 2px solid #aaa
display: block
margin-top: 15px
padding: 10px 20px
vertical-align: middle
Next, in your views directory, create a layouts directory and create a file called app.blade.php and add the following to it. This will serve as the base layout file all components will inherit from:
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="csrf-token" content="{{ csrf_token() }}">
</head>
<body>
<div id="app">
@yield("content")
</div>
<script src="/js/app.js"></script>
</body>
</html>
Next, create chat.blade.php and add the following lines of code to it. This will serve as the main entry point for our application:
// views/chat.blade.php
@extends('layouts.app')
@section('content')
<router-view></router-view>
@endsection
At this point, we are done with our views. Let's tell Laravel to render the application for us. For that update your routes to the following:
// routes/web.php
Route::get('/', function () {
return view('chat');
});
To see this in effect, we need to start our server by typing php artisan serve. Then visit http://localhost:8000 in your browser. Click on the icon in the bottom right hand corner and you'll see the "Chat With Us" dialog display.
Now, we have our views up and running we need to add functionality to send and receive messages.
Channels & Messages
Stream utilizes channels and events to determine who our message gets sent to. Every user belongs to a channel and receives all messages that get sent to that channel. For a live chat, we need a single channel as it is only a direct line from the user to the admin. For that reason, we'll not store our channels in a database we'll instead hardcode it.
Create a new controller using the command below:
php artisan make:controller MessagesController
Next, open it and add the following to it:
// controllers/MessagesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use GetStream\StreamChat\Client;
use App\Message;
class MessagesController extends Controller
{
protected $client;
public function __construct(){
$this->client = new Client(
getenv("MIX_STREAM_API_KEY"),
getenv("MIX_STREAM_API_SECRET"),
);
}
public function generateToken(Request $request){
return response()->json([
'token' => $this->client->createToken($request->input('name'))
], 200);
}
public function getChannel(Request $request){
$from = $request->input('from');
$to = $request->input('to');
$from_username = $request->input('from_username');
$to_username = $request->input('to_username');
$channel_name = "livechat-{$from_username}-{$to_username}";
$channel = $this->client->getChannel("messaging", $channel_name);
$channel->create($from_username, [$to_username]);
return response()->json([
'channel' => $channel_name
], 200);
}
}
Here, we define two methods and a constructor. We instantiate our Stream Chat in our constructor. Then we define two other methods:
- generateToken: generates a token and returns it to the client as a response.
- getChannel: creates a new channel then adds both users (admin/user) to it and returns the newly created channel name to the client.
To access these methods from our client, we need to create our routes. Open the routes/web.php file and add the following to it:
// routes/web.php
Route::post('generate-token', 'MessagesController@generateToken');
Route::post('get-channel', 'MessagesController@getChannel');
Now that our backend work is complete. Let's implement the functionality of our front-end to start chatting.
Client-side
To use the chat methods, we need to initialize the Stream Chat JavaScript client that will enable us to send, listen, and receive new messages.
We'll write the methods in our two components. First, open up your Chat.vue file and update it with the following code:
// js/components/Chat.vue
<script>
import { StreamChat } from 'stream-chat';
export default {
data() {
return {
modal: {
show: false,
},
message: '',
messageData: [],
collapsed: false,
channel: null
}
},
computed: {
username() {
return "client"
}
},
mounted() {
this.initializeClient();
this.createChannel();
},
methods: {
async createChannel(){
const {data} = await axios.post('/getChannel', {
from_username: "client",
to_username: "admin",
from: "client",
to: "admin",
})
const channel = this.client.channel('messaging', data.channel, {
name: 'LiveChat channel',
members: ["client", "admin"]
});
this.channel = channel
channel.watch().then(state => {
channel.on('message.new', event => {
this.messageData.push(event.message)
});
})
},
async initializeClient () {
const {data} = await axios.post('/generate-token', {
name: "client"
})
const client = new StreamChat(process.env.MIX_STREAM_API_KEY);
await client.setUser(
{
id: "client",
name: "client",
},
data.token,
);
this.client = client
},
sendMessage() {
this.channel && this.channel.sendMessage({
text: this.message
});
this.message = "";
},
toggleModal() {
this.modal.show = !this.modal.show;
},
showModal() {
this.modal.show = true;
},
hideModal() {
this.modal.show = false;
},
}
}
</script>
Let's go through our newly defined property and methods:
- username: this is a computed property, which represents the username of the person chatting
- createChannel: this method handles the creation of channels. It sends the relevant data to the channel API we created earlier and gets back the response. It then passes the relevant data to the channel instance on the client, then watches for changes and immediately updates the screen.
- initializeClient: this method runs immediately, once the component loads. It sends a message to the token API we created earlier and gets a token. It then instantiates Stream Chat, passes the newly acquired token to it for verification, and finally returns the user.
- sendMessage: this method handles the sending of messages back and forth the channel. Initially, it sets the
messagevariable to an empty string. Then checks if the channel exists before trying to send it.
Next, open up your AdminChat.vue and update it with the following code:
// js/components/AdminChat.vue
<script>
import { StreamChat } from 'stream-chat';
export default {
data() {
return {
modal: {
show: false,
},
message: '',
messageData: [],
collapsed: false,
channel: null
}
},
computed: {
username() {
return "admin"
}
},
mounted() {
this.initializeClient();
this.createChannel();
},
methods: {
async createChannel(){
const {data} = await axios.post('/getChannel', {
from_username: "admin",
to_username: "client",
from: "admin",
to: "client",
})
const channel = this.client.channel('messaging', data.channel, {
name: 'LiveChat channel',
members: ["admin", "client"]
});
this.channel = channel
channel.watch().then(state => {
this.messages = state.messages
channel.on('message.new', event => {
this.messageData.push(event.message)
});
})
},
async initializeClient () {
const {data} = await axios.post('/generate-token', {
name: "admin"
})
const client = new StreamChat(process.env.MIX_STREAM_API_KEY);
await client.setUser(
{
id: "admin",
name: "admin",
},
data.token,
);
this.client = client
},
sendMessage() {
this.channel && this.channel.sendMessage({
text: this.message
});
this.message = "";
},
toggleModal() {
this.modal.show = !this.modal.show;
},
showModal() {
this.modal.show = true;
},
hideModal() {
this.modal.show = false;
},
}
}
</script>
Now visit http://localhost:8000 on your browser to start chatting as a client and visit http://localhost:8000/#admin to respond to the messages.
Final Thoughts
In this tutorial, we have explored how to make a functional live chat using Laravel and Stream. The knowledge from here can be used to create more sophisticated conversation and real-time applications.
Stream offers a wide variety of features that can be useful in creating truly mature feed and chat applications. You can learn more about Stream Chat here.
The code from this tutorial is located on GitHub.