Add Messaging Functionality to Your Laravel App

9 min read
Onwuka G.
Onwuka G.
Published April 13, 2020 Updated April 14, 2020

Add Messaging Functionality to Your Laravel App

If you are building an app with Laravel and want to add a social aspect, such as chat, Stream Chat provides exactly what you need. In this tutorial, we'll do a deep dive into how to use Stream to add a messaging feature to your Laravel-based application. One of the most significant advantages to using Stream to accomplish this feat is that it significantly reduces the time and effort required; Stream's Chat API allows you to build a fully-featured user engagement platform in just a few hours.

Here is a preview of the app we’ll be building:

Stream Chat Example with Laravel

Prerequisites

Please, make sure the following are installed on your system:

To follow along with this tutorial comfortably, you should also have a basic understanding of Laravel, JavaScript, and Vue.js.

Creating a Stream Account

To be able to use the Stream API, you need to have an account. Visit the Stream Chat website and then click on the appropriate Login or Signup button.

To sign up, on the popup page that appears, fill in your Username, Email Address, and Password and then click on the Create Free Account button:

Stream Onboarding

Upon successfully logging in or creating your account, you will be taken to your dashboard, which contains metrics and credentials for your Stream app(s). If you've just created your account, your first app will have been created for you; if you've logged into your existing account, you'll likely need to create a new app for this tutorial.

From your dashboard, take note of your APP ID, API Key, and API Secret:

Stream Account Dashboard

Be sure to keep your app credentials safe and private! We’ll use these to authenticate with the SDK soon...

Bootstrapping a Laravel App

If you already have a Laravel app, you can skip this step; otherwise, open up a terminal and run the command below to bootstrap the Laravel app files:

sh
$ composer global require laravel/installer
$ laravel new laravel-stream-one-on-one

Next, cd into your new project directory:

sh
$ cd laravel-stream-one-on-one

Once you are within your app directory, navigate to your .env file and set up the database by changing DB_CONNECTION=mysql to DB_CONNECTION=sqlite. Since this is just a small test application, we'll be using SQLite, which is both portable and reliable, given its file-based nature. We can create the file for our SQLite database (database.sqlite) in the database directory by running:

sh
$ touch ./database/database.sqlite

Because we are no longer using a hosted MySQL database, we can delete the following key-value pairs from the .env file:

DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=

To update our app with the database changes we just made, execute the following command:

sh
$ php artisan migrate

Next, add the Stream Chat keys (that we grabbed from our dashboard and tucked away in a safe place) to the .env file:

STREAM_API_KEY=<STREAM_API_KEY>
STREAM_API_SECRET=<STREAM_API_SECRET>

MIX_STREAM_API_KEY="${STREAM_API_KEY}"
MIX_STREAM_API_SECRET="${STREAM_API_SECRET}"

Make sure to update the <STREAM_API_KEY> and <STREAM_API_SECRET> placeholders with your correct API Key and Secret, respectively.

Finally, you can start up your local development server, by running:

sh
$ php artisan serve

Now, if you visit http://localhost:8000, you should see the default Laravel app landing page:

Laravel Landing Page

Preparing Our Generic App for Custom Development

Before we start the code that will make the intended magic happen, open another terminal tab/window and cd into the root directory of the project (laravel-stream-one-on-one). Then, run the commands below to install all of the necessary the dependencies, and to run the app and watch for file changes:

sh
$ yarn install
$ yarn run watch

"watching" our app will automatically rebuild the Vue components with our changes, in real time, so we aren't constantly having to restart our app to see our changes.

Make sure to leave both the local development server and our yarn run watch command running while you follow along from here on out.

Installing Chat SDKs

We’ll use Stream Chat's PHP API Client to reach the Chat API endpoints on the server, and its JavaScript SDK on the client.

To install the server SDK, open a new terminal in the project root directory and install the PHP SDK:

sh
$ composer require get-stream/stream-chat

Then, install the JavaScript SDK:

sh
$ yarn add stream-chat

Adding Authentication

We need users in order to chat! Adding authentication allows us to 1) create users with unique credentials and 2) later identify returning users via their credentials.

Luckily for us, Laravel provides a single command that allows us to quickly scaffold all of the routes and views we'll need for authentication; add authentication to the app by running:

sh
$ composer require laravel/ui
$ php artisan ui vue --auth

If you refresh the app again, you will see that login and register links are now available!

Creating Users

Let's update the create function in app/Http/Controllers/Auth/RegisterController.php to configure the registration process. Upon registration, we'll create a StreamClient instance and define the user and then update the Stream server by creating the user, there:

// [...]
        /**
         * Create a new user instance after a valid registration.
         *
         * @param  array  $data,.     * @return \App\User
         */
        protected function create(array $data)
        {
           // create the user to Stream Chat
            $client = new StreamClient(
                getenv("STREAM_API_KEY"),
                getenv("STREAM_API_SECRET"),
                null,
                null,
                9 // timeout

Note that we also imported the SDK to the file, by adding:

php
use GetStream\StreamChat\Client as StreamClient;

Finally, create an EventBus that we can use to share data between our Vue components, by adding the following code to the resources/js/app.js file:

// [...]
// For sharing of data between components
Vue.prototype.EventBus = new Vue();
// [...]

Make sure to add the above code right after the window.Vue = require('vue'); code line.

Crafting the Chat Interface

Since we are building the UI using Vue.js, we will divide the UI into smaller manageable components:

  • StreamChat.vue - The parent component that will be holding other components
  • UsersComponent.vue - For listing available users with whom we can converse on the app
  • ActiveChatsComponent.vue - For holding all active chats
  • MessagesComponent.vue - Contains logic for sending and rendering of messages

Create the above components files (StreamChat.vue, UsersComponent.vue, ActiveChatsComponent.vue, MessagesComponent.vue), and import them to the resources/js/components directory by adding the following to resources/js/app.js:

Vue.component('users-component', require('./components/UsersComponent.vue').default);
Vue.component('message-component', require('./components/MessagesComponent.vue').default);
Vue.component('active-chats-component', require('./components/ActiveChatsComponent.vue').default);
Vue.component('stream-chat', require('./components/StreamChat.vue').default);

Make sure to place the code right after the Vue.component('example-component',… component already present in the file.

Then, finally, add this component to the view file - resources/views/home.blade.php:

Building your own app? Get early access to our Livestream or Video Calling API and launch in days!
html
<stream-chat :autheduser="{{ Auth::user() }}"></stream-chat>

So that the file looks like below:

@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">Dashboard</div>

                <div class="card-body">
                    @if (session('status'))
                        <div class="alert alert-success" role="alert">
                            {{ session('status') }}
                        </div>
                    @endif

This will render the chat. We are adding this to the home.blade.php file because Laravel, by default, renders the file as the dashboard when a user logs in. If you want the chat to appear on a different page, you just need to add it to your preferred page!

StreamChat Component

To start building out the StreamChat component, add the code below to the resources/js/components/StreamChat.vue file:

<template>
    <div class="stream-chat row">
        <active-chats-component :autheduser="autheduser"></active-chats-component>
        <users-component></users-component>
    </div>
</template>

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

Using this, we can easily duplicate the chat across various pages. We pass the logged in user details as autheduser to the props of the component, so we can use it across the components.

Users Component

To create the Users component, add the following code to the resources/js/components/UsersComponent.vue file:

<template>
    <div class="card users-box">
        <div class="card-header" @click="collapsed = !collapsed">
            Users
        </div>
        <div class="card-body users" v-show="!collapsed">
            <div class="user" v-for="user in users" :key="user.id" @click="addToActiveChat(user)">
                <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQSXmfj4kUOZR1oT7ood5_AqnC_TgkuyVojx73oE2eYdp4Mvl29" width="30" height="30">
                {{ user.name }}
            </div>
        </div>
    </div>
</template>

<script>

Once the component is created, we make a request to the /api/get-users endpoint and fetch all the available users, then list them. We are yet to create the endpoint, but we’ll be doing that soon!

Messages Component

To flush out the Messages component, add the following code to the MessagesComponent.vue file:

<template>
    <div class="card user-box">
        <div class="card-header" @click="collapsed = !collapsed">
            {{ chat.name }}
        </div>
        <div class="card-body" v-show="!collapsed">
            <div class="user-messages">
                <div
                    class="chat-message"
                    v-for="message in messages"
                    v-bind:key="message.id"
                    v-bind:class="[(message.user.id == username) ? 'from-client' : 'from-admin']"
                >
                    {{ message.text }}
                </div>

ActiveChats Component

To create our final "ActiveChats" component, add the code for listing the active chats to resources/js/components/ActiveChatComponent.vue file:

<template>
    <div class="active-chats row">
         <message-component
            v-for="chat in activeChats"
            :key="chat.id"
            :token="token"
            :client="client"
            :chat="chat"
            :autheduser="autheduser">
         </message-component>
    </div>
</template>

<script>
import { StreamChat } from 'stream-chat';

The Styles

Finally, update the styles in the resources/saas/app.scss file:

/* [...] */
input[type="text"] {
    padding: 10px 8px;
    margin-top: 10px;
    border-radius: 2px;
    border: 1px solid darkgray;
    font-size: 16px;
    box-sizing: border-box;
    display: block;
}
.inputs {
    text-align: center;
    align-self: center;
    justify-self: center;
}

Our chat UI is now ready! Although chatting is not yet working, you can still move around within the app and get the feeling of it.

Now, if you log in to the app, you should see something that looks a lot like this:

Laravel Auth

Here, only the UsersComponent is visible, since there is no user listed yet.

Generating a Token

The Stream Chat JavaScript SDK requires a valid token for us to start using it. The next thing we’ll do is create an endpoint where this token can be generated!

Create a new controller named ChatController.php in the app/Http/Controllers/ directory. Then, add the following code to your new ChatController.php file:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use GetStream\StreamChat\Client as StreamClient;
use App\User;
use App\Channel;
use Illuminate\Support\Facades\Auth;

class ChatController extends Controller
{
    protected $client;

    /**

In the code above, after initializing the PHP SDK, we create a function for handling the generation of token, named generateToken.

To actually use the generated token, add the route to handle the token request by adding the code below to the routes/api.php file:

php
Route::post('/generate-token', 'ChatController@getnerateToken');

To generate tokens, we'll simply make a POST request to /api/generate-token, passing along the username of the user.

Get All Users

Since we want conversations to take place among all users on the app, we need to list these users in the available users list for every logged user; add the following function for fetching users to ChatController.php to accomplish this:

// [...]
    /**
     * Get all users
     */
    public function getUsers(Request $request)
    {
        return response()->json([
            'users' => User::all()
        ], 200);
    }
// [...]

Then, create the route for handling the request by adding the following code to the routes/api.php file:

// [...]
Route::get('/get-users', 'ChatController@getUsers');
// [...]

Now, if you visit http://localhost:8000/api/get-users, you should see a JSON containing all the registered users on the app.

If you reload the app again, you should now see a list of users. When you click on a user, you will see the MessageComponent for that user appear to the left:

One-on-one Chat with Stream

Wiring Things Up

We are almost finished with our chat! What is left is to initialize the Stream Chat JavaScript SDK, and send and listen for new messages.

Add a function for initializing the Stream Chat JavaScript SDK to the methods:{…} block of the resources/js/components/ActiveChatsComponent.vue file:

// [...]
        async initializeClient () {
            // Initialize the StreamChat SDK
            const {data} = await axios.post('/api/generate-token', {
                username: this.autheduser.email.replace(/[@\.]/g, '_')
            })

            const client = new StreamChat(process.env.MIX_STREAM_API_KEY, {timeout: 9000});
            await client.setUser(
                {
                    id: this.autheduser.email.replace(/[@\.]/g, '_'),
                    name: this.autheduser.name,
                },
                data.token,
            );

            this.client = client
        },
// [...]

Since Laravel's default auth does not provide a username, but uses email instead, to uniquely identify registered users, we will be using the email as the username for Stream Chat. We’ll replace the “@” and “.” characters in the email with “_”s, for convenience.

In the above code, after initializing the Client, we set the current user to it using await client.setUser.

Once the component is created, call the initialize function in the created hook to initialize the Client SDK:

// [...]
this.initializeClient()
// [...]

Notice that we are passing the initialized client down to the messages component via its props - this.client = client, so we can make use of it there.

Next, initialize a channel to the created:{…} block of the resources/js/components/MessagesComponent.vue file, to allow two users to converse, when they so choose:

 // [...]
        const to_username = this.chat.email.replace(/[@\.]/g, '_')

        // Initialize the channel, and create a one to one channel
        const channel = this.client.channel('messaging', {
            name: 'Awesome channel',
            members: [this.username, to_username]
        });

        this.channel = channel
// [...]

Then, fetch messages on that channel, if any, and start listening for new messages to the channel:

// [...]
        // fetch the channel state, subscribe to future updates
         channel.watch().then(state => {
            this.messages = state.messages

            // Listen for new messages on the channel
            channel.on('message.new', event => {
                this.messages.push(event.message)
            });
         })
// [...]

Finally, create function for sending new messages to the message: {…} block of resources/js/components/MessagesComponent.vue:

// [...]
        addMessage() {
            // Send message to the channel
            this.channel && this.channel.sendMessage({
                text: this.message
            });

            this.message = "";
        }
// [...]

Testing the Chat

Good job! You have successfully added a one-to-one chat to your Laravel app! To test the app:

  1. Open two tabs on your browser
  2. Register or log into two different users
  3. On both tabs, select the other users from the list of users
  4. Start chatting!

Wrapping Up

In this tutorial, you learned how to add a one-to-one chat to your Laravel app! Although you have gotten a solid foundation of how to use the Stream Chat API and some of the features it provides, there are even more awesome features that will make your chat more lively. This includes features like:

  1. Adding a typing indicator feature
  2. Showing when a user is online or offline
  3. Showing unread messages
  4. Alerting the user of new messages

Check out the events that enable you to add these features here!

You can also find the codebase for this tutorial in the GitHub repo and let us know how you have modified this app to suit your needs!

Thanks for reading, and happy coding!

decorative lines
Integrating Video With Your App?
We've built an audio and video solution just for you. Launch in days with our new APIs & SDKs!
Check out the BETA!