Building a real-time chat application with Laravel and Larasocket
Nowadays, pretty much every web application requires some form of real-time communication. Laravel offers a simple interface for developers to add WebSocket technologies to their applications. For years, Pusher has been the default Laravel option. However, now there is a new option: Larasocket.
Related articles:
- Building a real-time chat room with Larasocket, Tailwind, Alpine, Livewire, and Laravel
- Larasocket: Laravel’s Newest Websocket Broadcasting Solution
- Laravel Broadcasting in 2020
In this article, we are going to explore Laravel’s newest broadcasting solution: Larasocket. Larasocket is a hosted, no configuration, pay as you option for handling your broadcasting in your Laravel applications.
The main benefit Larasocket offers over Pusher is pricing. When using Larasocket, you pay only for what you use, with a free tier that will get small projects off ground. You can find more details about Larasocket’s pricing here.
Getting Started
Today, we will be building a realtime chat room application. This is broken into 2 parts: the backend and the frontend. The final source code for this tutorial can be found here.
Part 1: The Backend
First, let’s get started by creating ourselves a fresh Laravel application:
laravel new larasocket-chat --auth
In order to turn on broadcasting for any Laravel application, we need to go to config/app.php
and uncomment:
App\Providers\BroadcastServiceProvider::class,
Setting up Larasocket
Step 1: Include the broadcasting driver
composer require larasocket/larasocket-driver
Step 2: Add Larasocket as a broadcasting connection option in config/broadcasting.php
'larasocket' => [
'driver' => 'larasocket',
'token' => env('LARASOCKET_TOKEN'),
],
Step 3: Head on over to Larasocket.com and get yourself a free token.
Step 4: Let’s update our .env
BROADCAST_DRIVER=larasocket
LARASOCKET_TOKEN=token
MIX_LARASOCKET_TOKEN="${LARASOCKET_TOKEN}"
Our backend is now set up to start using Larasocket.
Models
Now we can create ourselves a Message
model with a migration:
php artisan make:model Message -m
We can update the generatedcreate_messages_table
migration to:
Schema::create('messages', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->text('message');
$table->timestamps();
});
We are done making database changes so let’s migrate our tables:
php artisan migrate
Let’s update our Message.php
to look like:
class Message extends Model
{
protected $fillable = [
'message'
];
public function user() {
return $this->belongsTo(User::class);
}
}
While we are at it, let’s add a messages
eloquent relationship in User.php
public function messages() {
return $this->hasMany(Message::class);
}
Controllers
Now that we have our models ready to go, we can add the endpoints needed for our chat application to send and receive messages.
php artisan make:controller MessageController
Let’s update our MessageController:
class MessageController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
return Message::with('user')->get();
}
public function store(Request $request)
{
$user = Auth::user();
$message = $user->messages()->create([
'message' => $request->input('message')
]);
return [
'message' => $message,
'user' => $user,
];
}
}
Let’s add these new routes to our web.php
:
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');
Route::view('/', 'chat')->middleware('auth');
Route::resource('messages', 'MessageController')->only([
'index',
'store'
]);
Broadcasting
The last thing we need to do on the backend is to set up our events so they are broadcasted when new messages are created.
php artisan make:event MessageSentEvent
This is the event that will be dispatched to our listeners. We can use it to pass real-time information to the listening clients. In this example, our clients will be a web browser. However, this could be an iOS or Android application as well.
class MessageSentEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
public $user;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Message $message, User $user)
{
$this->message = $message;
$this->user = $user;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('chat');
}
}
Important: Don’t forget to addimplements ShouldBroadcast
to the class signature. Events that implement the ShouldBroadcast
will be dispatched to the broadcast driver by Laravel. All public properties will be passed along as data. There is much more customization available that you can read about in the Laravel broadcasting documentation.
Back over in MessageController
, we need to broadcast our new event in our store
method:
public function store(Request $request)
{
$user = Auth::user();
$message = $user->messages()->create([
'message' => $request->input('message')
]); // send event to listeners
broadcast(new MessageSentEvent($message, $user))->toOthers();
return [
'message' => $message,
'user' => $user,
];
}
Since we are only allowing authenticated users to use the chat feature, we are broadcasting events on the private chat
channel. To control access to private and presence channels, Laravel uses the routes/channels.php
. In that file, we will add authentication logic for the new channel.
Broadcast::channel('chat', function () {
return \Illuminate\Support\Facades\Auth::check();
});
Now it is time to build the UI.
Part 2: The Frontend
Before building the UI, let’s setup Laravel Echo to use Larasocket
npm i laravel-echo larasocket-js
Over in bootstrap.js
we can now create our Echo instance using larasocket-js
:
import Echo from 'laravel-echo';
import Larasocket from 'larasocket-js';window.Echo = new Echo({
broadcaster: Larasocket,
token: process.env.MIX_LARASOCKET_TOKEN,
});
UI
Our chat application will start with a single blade view powered by VueJS. Let’s create chat.blade.view
in the resources/views
directory:
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<h2>Messages</h2>
<div
class="clearfix"
v-for="message in messages"
>
@{{ message.user.name }}: @{{ message.message }}
</div>
<div class="input-group">
<input
type="text"
name="message"
class="form-control"
placeholder="Type your message here..."
v-model="newMessage"
@keyup.enter="sendMessage"
>
<button
class="btn btn-primary"
@click="sendMessage"
>
Send
</button>
</div>
</div>
</div>
</div>
@endsection
The next thing we need to do is initialize VueJS. We can do that in out resources/js/app.js
. Let’s replace our const app
initialization with the following code:
Before:
const app = new Vue({
el: '#app',
});
After:
const app = new Vue({
el: '#app',
data: {
messages: [],
newMessage: ''
},
created() {
this.fetchMessages();
Echo.private('chat')
.listen('MessageSentEvent', (e) => {
this.messages.push({
message: e.message.message,
user: e.user
});
});
},
methods: {
fetchMessages() {
axios.get('/messages').then(response => {
this.messages = response.data;
});
},
addMessage(message) {
axios.post('/messages', {
message
}).then(response => {
this.messages.push({
message: response.data.message.message,
user: response.data.user
});
});
},
sendMessage() {
this.addMessage(this.newMessage);
this.newMessage = '';
}
}
});
Let’s compile our assets and we are ready to run this application!
npm run dev
Start our Laravel server:
php artisan serve
You can navigate to http://127.0.0.1:8000 and start playing with Larasocket.
If you have any questions or issues, you can get in touch here.