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
message
variable 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.