Add a real-time chat widget to your Laravel application.
With this package, you can enhance user engagement, boost collaboration, and facilitate instant communication within your existing Laravel application by providing seamlessly integrated dynamic and interactive real-time chat widget functionality.
The Basement name was inspired by Aech's private chat room from Ready Player One.
Here is a demo with scaffolding using Laravel Breeze.
php ^8.0
and laravel/framework ^9.0.0 | ^10.0.0
installed in your project.chrome >= 80
, edge >= 80
, firefox >= 74
, or equivalent. See details here.Open a terminal, and make sure you are in your Laravel project directory.
Install this package using the following command:
composer require basement-chat/basement-chat
Start integrating basement chat with your Laravel application:
php artisan basement:install
The above command will publish the configuration, assets, and migration files to your Laravel application. On the other hand, it will also ask interactive questions for you to run the database migrations and ask you to install which broadcast driver you will use.
Selecting a broadcast driver
Before selecting a broadcast driver, you need to ensure that BroadcastServiceProvider::class
is enabled by uncommenting it or adding it to your providers
in config/app.php
:
/*
* Application Service Providers...
*/
AppProvidersAppServiceProvider::class,
AppProvidersAuthServiceProvider::class,
- // AppProvidersBroadcastServiceProvider::class,
+ AppProvidersBroadcastServiceProvider::class,
AppProvidersEventServiceProvider::class,
AppProvidersRouteServiceProvider::class,
Then you can select one of the following drivers:
If you accidentally missed installing the driver in the previous step, you can install it again using the
php artisan basement:install driver
command.
After creating a new channel in the Pusher account, you need to configure Laravel .env
by providing the relevant configurations:
BASEMENT_BROADCAST_DRIVER=pusher
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=<replace-with-your-pusher-app-id>
PUSHER_APP_KEY=<replace-with-your-pusher-key>
PUSHER_APP_SECRET=<replace-with-your-pusher-secret>
PUSHER_APP_CLUSTER=<replace-with-your-pusher-cluster>
Provide relevant configurations in your .env
after creating a new app in your Ably account:
BASEMENT_BROADCAST_DRIVER=ably
BROADCAST_DRIVER=ably
ABLY_KEY=<replace-with-your-ably-key>
ABLY_PUBLIC_KEY=<replace-with-your-ably-public-key>
Configure your .env
first with the following configuration:
BASEMENT_BROADCAST_DRIVER=soketi
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=app-id
PUSHER_APP_KEY=app-key
PUSHER_APP_SECRET=app-secret
PUSHER_HOST=127.0.0.1
PUSHER_PORT=6001
PUSHER_SCHEME=http
Then, keep the Soketi server running with the following command when you want to use chat features in your app:
npx soketi start
Similar to Soketi, you need to configure .env
first with the following configuration:
BASEMENT_BROADCAST_DRIVER=laravel-websockets
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=app-id
PUSHER_APP_KEY=app-key
PUSHER_APP_SECRET=app-secret
PUSHER_HOST=127.0.0.1
PUSHER_PORT=6001
PUSHER_SCHEME=http
Then, keep the Laravel Websockets server running with the following command when you want to use chat features in your app:
php artisan websockets:serve
Configure your Sanctum Stateful Domains
Since this package uses Laravel Sanctum as the primary authentication system, you will need to configure your .env
to use the equivalent SANCTUM_STATEFUL_DOMAINS
with the domain you are currently using:
SANCTUM_STATEFUL_DOMAINS=<your-app-domain>
Example:
basement.up.railway.app
,127.0.0.1:8080
Implements basement chat functionality to your user model
In your user model (by default uses app/Models/User.php
), modify it so it implements BasementChatBasementContractsUser
and uses BasementChatBasementTraitsHasPrivateMessages
trait
<?php
namespace AppModels;
+ use BasementChatBasementContractsUser as BasementUserContract;
+ use BasementChatBasementTraitsHasPrivateMessages;
...
- class User extends Authenticatable
+ class User extends Authenticatable implements BasementUserContract
{
+ use HasPrivateMessages;
...
}
Loading the basement chat component into your views
To add a chat box component, load it in the .blade
view file where the user is already logged in. For example, if you use Laravel Breeze, the path should be in resources/views/layouts/app.blade.php
. Then, add the <x-basement::chat-box />
line before the closing </body>
tag.
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
...
+ <x-basement::chat-box />
</body>
</html>
Choosing how you use frontend assets
There are two ways to use the frontend assets of this package. You can use one of the following:
link
and script
tags directlyThis is the simplest way to integrate basement chat frontend assets with your existing Laravel application. Note that this bundle also sets the following packages to your global window
object:
window.Alpine
window.axios
window.Pusher
window.Echo
In the same file as the previous step that added the chat box component, you need to load the basement chat .css
and .js
files:
<!DOCTYPE html>
<html lang="en">
<head>
...
+ <link rel="stylesheet" href="{{ asset('vendor/basement/basement.bundle.min.css') }}">
</head>
<body>
...
<x-basement::chat-box />
+ <script src="{{ asset('vendor/basement/basement.bundle.min.js') }}"></script>
</body>
</html>
You can also import the basement chat library as a module into your own .js
file and bundle it yourself.
First, make sure you assign the following packages to the global window
object:
alpinejs@^3
with @alpinejs/intersect@^3
plugin as window.Alpine
axios@^1
as window.axios
laravel-echo@^1
as window.Echo
pusher-js@^7
as window.Pusher
To automatically install the above dependencies you can use the following command:
php artisan basement:install frontend_deps
Then, you need to import the following modules:
vendor/basement-chat/basement-chat/dist/basement.bundle.min.css
vendor/basement-chat/basement-chat/dist/basement.plugin.esm
as an Alpine.js pluginvendor/basement-chat/basement-chat/dist/basement.echo-options.esm
as a Laravel Echo argument
You can change the
.esm
suffix to.common
if you are using cjs module instead of esm.
A fully working example inside resources/js/app.js
would be like the following:
import '../../vendor/basement-chat/basement-chat/dist/basement.bundle.min.css'
import axios from 'axios'
import Pusher from 'pusher-js'
import Echo from 'laravel-echo'
import Alpine from 'alpinejs'
import intersect from '@alpinejs/intersect'
import echoOptions from '../../vendor/basement-chat/basement-chat/dist/basement.echo-options.esm'
import basement from '../../vendor/basement-chat/basement-chat/dist/basement.plugin.esm'
window.axios = axios
window.Pusher = Pusher
window.Echo = new Echo(echoOptions)
window.Alpine = Alpine
window.Alpine.plugin(intersect)
window.Alpine.plugin(basement)
window.Alpine.start()
This package publishes a config/basement.php
configuration file and offers options to configure broadcaster
, chat_box_widget_position
, user_model
, avatar
, and middleware
. See this file for more detailed information on what you can configure.
Other than configuring through the config/basement.php
file, you can customize further by changing the class implementation or overriding the default method. Let's explore some of the use cases you can do with this feature:
By default, the basement chat package will display the user's full name in your contacts list. If you want to show the last name instead, you can override the getNameAttribute
as in the following example:
<?php
namespace AppModels;
use BasementChatBasementContractsUser as BasementUserContract;
use BasementChatBasementTraitsHasPrivateMessages;
class User extends Authenticatable implements BasementUserContract
{
use HasPrivateMessages;
...
/**
* Get the user's last name instead of the user's full name.
*/
public function getNameAttribute(): string
{
return str($this->attributes['name'])->explode(' ')->last();
}
}
Like when you are changing the name shown in the contacts. You can also override the default getAvatarAttribute
to change your contact's avatar.
<?php
namespace AppModels;
use BasementChatBasementContractsUser as BasementUserContract;
use BasementChatBasementTraitsHasPrivateMessages;
class User extends Authenticatable implements BasementUserContract
{
use HasPrivateMessages;
...
/**
* Get the user's avatar url.
*/
public function getAvatarAttribute(): string
{
return $this->attributes['image_url'];
}
}
Instead of providing the chat feature to all available users, you can also conditionally provide the chat feature to specific users. For example, if you are using spatie/laravel-permission and want to provide a chat feature only for the administrator
role:
// app/Actions/AllContacts.php
<?php
namespace AppActions;
use AppModelsUser;
use BasementChatBasementActionsAllContacts as BasementAllContactsAction;
use BasementChatBasementDataContactData;
use IlluminateContractsAuthAuthenticatable;
use IlluminateDatabaseEloquentBuilder as EloquentBuilder;
use IlluminateDatabaseEloquentCollection as EloquentCollection;
use IlluminateSupportCollection;
class AllContacts extends BasementAllContactsAction
{
/**
* Extend and override the default method for getting all contacts.
* Only users with the administrator role will appear in the contact list.
*/
public function all(Authenticatable $user): Collection
{
/** @var EloquentCollection<int,User> $contacts */
$contacts = User::addSelectLastPrivateMessageId($user)
->addSelectUnreadMessages($user)
->whereHas('roles', function (EloquentBuilder $query): void {
$query->where('name', 'administrator');
})
->get();
$contacts->append('avatar');
$contacts->load('lastPrivateMessage');
return $contacts
->sortByDesc('lastPrivateMessage.id')
->values()
->map(fn (Authenticatable $contact): ContactData => $this->convertToContactData($contact));
}
}
// app/Providers/AppServiceProvider.php
<?php
namespace AppProviders;
use AppActionsAllContacts;
use BasementChatBasementBasement;
use IlluminateSupportServiceProvider;
class AppServiceProvider extends ServiceProvider
{
...
/**
* Bootstrap any application services.
*/
public function boot(): void
{
// Override the default action with your customized AllContacts action.
Basement::allContactsUsing(AllContacts::class);
}
}
The following is a list of functions that you can use to override other actions and models:
Basement::useUserModel(User::class); Basement::usePrivateMessageModel(PrivateMessage::class); PrivateMessage::observe(PrivateMessageObserver::class); Basement::allContactsUsing(AllContacts::class); Basement::allPrivateMessagesUsing(AllPrivateMessages::class); Basement::markPrivateMessagesAsReadUsing(MarkPrivatesMessagesAsRead::class); Basement::sendPrivateMessagesUsing(SendPrivateMessage::class);
<!-- resources/views/layouts/app.blade.php -->
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
...
<!-- The chat box component button will only be available if the current user is an administrator -->
@role('administrator')
<x-basement::chat-box />
@endrole
</body>
</html>
It is also possible to customize the view style. For example, you can do the following steps to change the color of the header and icon of a chat box component:
Publish views with the following command:
php artisan vendor:publish --tag=basement-views
Open the resources/views/vendor/basement/components/organisms/header.blade.php
file, and add the style attribute like the following:
<header {{ $attributes->merge([
'class' => 'bm-grid bm-grid-cols-5 bm-border-b bm-border-gray-300 bm-p-3 bm-bg-blue-500 bm-text-white bm-rounded-t-md',
+ 'style' => 'background-color: cornflowerblue;',
]) }}>
...
</header>
Open the resources/views/vendor/basement/chat-box.blade.php
file, and add text color style:
<div class="basement">
<div ...>
<button
+ style="color: cornflowerblue"
x-on:click="isMinimized = false"
x-bind:class="isMinimized === true ? '' : 'bm-hidden'"
x-bind:data-title="totalUnreadMessages === 0 ? 'Open chat box' : `There are ${totalUnreadMessages} unread messages`"
class="basement-chat-box__open-button bm-w-full bm-h-full bm-rounded-full bm-text-blue-500 bm-border bm-bg-white bm-transition bm-duration-500 hover:bm-text-white hover:bm-bg-blue-500 bm-shadow-lg">
...
</button>
...
</div>
</div>
When you are using pre-bundled assets. Every time after updating this package with composer update
, you need to keep your assets file up to date using the following command:
php artisan vendor:publish --tag=basement-assets --force
Alternatively, to run the above command automatically after the update
command is executed, you can configure composer.json
by adding it to the post-update-cmd
scripts:
"scripts": {
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force",
+ "@php artisan vendor:publish --tag=basement-assets --ansi --force"
]
},
When you are using the Vite development server and get very high memory usage, you can configure your vite.config.js
to ignore watching the vendor folder like the following example:
import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'
export default defineConfig({
+ server: {
+ watch: {
+ ignored: [
+ './vendor/**',
+ ],
+ },
+ },
});
basement chat package may fail to start and you may get a 403 Forbidden - HTTP Error when accessing the broadcasting/auth
endpoint in the browser console when you use the php artisan route:cache
command. The solution to this problem is still under further investigation, we recommend that you do not use route caching feature at this time.
Please visit the following page to view the basement chat roadmap.
You can check detailed information about the contributing guide on the following page.
The basement chat package is licensed under the MIT license.