DEV Community

Cover image for Adding Real-Time Chat to Laravel App with Reverb and Vue.js 3
Aaron Reddix
Aaron Reddix

Posted on

Adding Real-Time Chat to Laravel App with Reverb and Vue.js 3

In today's digital age, users crave real-time interaction. Whether it's for a customer support channel, a collaborative platform, or a social networking app, real-time chat functionality can significantly enhance the user experience. Laravel, the popular PHP framework known for its elegant syntax and robust features, offers a powerful tool for building such features: Laravel Reverb.

This article will guide you through creating a real-time chat system using Laravel Reverb and Vue.js 3. We'll cover setting up Laravel Reverb, building the backend logic with Laravel, and implementing the frontend with Vue.js.

What is Laravel Reverb?

Laravel Reverb is a first-party WebSocket server specifically designed for Laravel applications. It facilitates seamless, two-way communication between your server and client, allowing for instant data updates without requiring constant page refreshes.

Why Choose Laravel Reverb for Real-Time Chat?

  • Seamless Integration: As an official Laravel package, Reverb integrates effortlessly with the Laravel ecosystem.
  • Performance: Built for speed and scalability, Reverb can handle a high volume of concurrent users and messages.
  • Security: Reverb leverages Laravel's built-in security mechanisms for secure communication.

Let's Get Started!

1. Project Setup

Make sure you have a fresh Laravel installation. If not, create one:

composer create-project laravel/laravel your-chat-app
cd your-chat-app 
Enter fullscreen mode Exit fullscreen mode

2. Install and Configure Laravel Reverb

Install Reverb using Composer:

composer require laravel/reverb
php artisan install:broadcasting
Enter fullscreen mode Exit fullscreen mode

This command will also generate the necessary configuration files for broadcasting and create echo.js in your resources/js directory.

Update your .envfile with the necessary Reverb configurations. You can generate application credentials from your dashboard on the service you are using.

BROADCAST_DRIVER=reverb
REVERB_APP_ID=your-app-id
REVERB_APP_KEY=your-app-key
REVERB_APP_SECRET=your-app-secret
Enter fullscreen mode Exit fullscreen mode

3. Database Setup

We'll use a simple database structure for this example. Create a migration for a messages table:

php artisan make:migration create_messages_table

Enter fullscreen mode Exit fullscreen mode

Add the following code to the up method of the generated migration file:

public function up()
{
    Schema::create('messages', function (Blueprint $table) {
        $table->id();
        $table->foreignId('user_id')->constrained()->cascadeOnDelete();
        $table->text('message');
        $table->timestamps();
    });
}
Enter fullscreen mode Exit fullscreen mode

Run the migrations:

php artisan migrate
Enter fullscreen mode Exit fullscreen mode

4. Create the Message Model

Generate a Message model:

php artisan make:model Message

Enter fullscreen mode Exit fullscreen mode

And define the relationship with the User model:

// app/Models/Message.php

use Illuminate\Database\Eloquent\Model;

class Message extends Model
{
    protected $fillable = ['user_id', 'message'];

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
Enter fullscreen mode Exit fullscreen mode

5. Create the Broadcasting Event

Generate an event that will be broadcast whenever a new message is sent:

php artisan make:event MessageSent

Enter fullscreen mode Exit fullscreen mode

Update the MessageSent event with the following:

// app/Events/MessageSent.php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use App\Models\Message;

class MessageSent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $message;

    /**
     * Create a new event instance.
     */
    public function __construct(Message $message)
    {
        $this->message = $message;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return array<int, \Illuminate\Broadcasting\Channel>
     */
    public function broadcastOn(): array
    {
        return [
            new PresenceChannel('chat'),
        ];
    }
}

Enter fullscreen mode Exit fullscreen mode

6. Define the Broadcast Route

In your routes/channels.php file, define the authorization logic for the chat channel:

Broadcast::channel('chat', function ($user) {
    return ['id' => $user->id, 'name' => $user->name];
});
Enter fullscreen mode Exit fullscreen mode

This ensures that only authenticated users can join the chat channel.

7. Create the Chat Controller

Create a controller to handle chat-related logic:

php artisan make:controller ChatController

Enter fullscreen mode Exit fullscreen mode

Define the methods for retrieving messages and sending new messages:

// app/Http/Controllers/ChatController.php

namespace App\Http\Controllers;

use App\Models\Message;
use Illuminate\Http\Request;
use App\Events\MessageSent;

class ChatController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }

    public function index()
    {
        return view('chat');
    }

    public function messages()
    {
        return Message::with('user')->latest()->take(10)->get();
    }

    public function sendMessage(Request $request)
    {
        $message = auth()->user()->messages()->create([
            'message' => $request->message,
        ]);

        broadcast(new MessageSent($message->load('user')))->toOthers();

        return $message;
    }
}

Enter fullscreen mode Exit fullscreen mode

8. Set up Routes

Define the routes for your chat application in your routes/web.php file:

// routes/web.php

use App\Http\Controllers\ChatController;

Route::get('/chat', [ChatController::class, 'index']);
Route::get('/messages', [ChatController::class, 'messages']);
Route::post('/messages', [ChatController::class, 'sendMessage']);
Enter fullscreen mode Exit fullscreen mode

9. Create the Vue.js Component

Now, let's build the frontend using Vue.js. Create a new component file:

touch resources/js/components/Chat.vue

Enter fullscreen mode Exit fullscreen mode

Add the following code to Chat.vue:

<template>
  <div class="container">
    <div class="row">
      <div class="col-md-8 offset-md-2">
        <div class="card">
          <div class="card-header">Chat Room</div>
          <div class="card-body" style="height: 400px; overflow-y: scroll;">
            <ul class="list-unstyled">
              <li v-for="message in messages" :key="message.id">
                <strong>{{ message.user.name }}:</strong> {{ message.message }}
              </li>
            </ul>
          </div>
          <div class="card-footer">
            <input type="text" class="form-control" v-model="newMessage" @keyup.enter="sendMessage">
            <button class="btn btn-primary mt-2" @click="sendMessage">Send</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      messages: [],
      newMessage: ''
    };
  },
  mounted() {
    this.fetchMessages();
    Echo.join('chat')
      .here((users) => {
        console.log('Users online:', users);
      })
      .joining((user) => {
        console.log(user.name + ' joined.');
      })
      .leaving((user) => {
        console.log(user.name + ' left.');
      })
      .listen('MessageSent', (e) => {
        this.messages.push(e.message);
      });
  },
  methods: {
    fetchMessages() {
      axios.get('/messages').then(response => {
        this.messages = response.data;
      });
    },
    sendMessage() {
      if (this.newMessage === '') {
        return;
      }
      axios.post('/messages', { message: this.newMessage }).then(response => {
        this.newMessage = '';
      });
    }
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

This component sets up a basic chat interface, retrieves messages, and sends new messages using Axios. It also utilizes Laravel Echo to listen for the MessageSent event and update the messages in real-time.

10. Compile Assets and Run

Finally, compile your assets and start the Laravel development server:

npm run dev
php artisan serve
Enter fullscreen mode Exit fullscreen mode

That's it! You should now have a functional real-time chat application built with Laravel Reverb and Vue.js.

Conclusion

Laravel Reverb provides a straightforward yet powerful way to add real-time features to your Laravel applications. Paired with the flexibility of Vue.js, you can create engaging and interactive user experiences. This guide covered the fundamental steps to get your real-time chat application up and running. Explore the documentation further to discover more advanced features and customization options offered by Laravel Reverb and Vue.js.

Top comments (0)