使用 Reverb 和 Vue 向 Laravel 添加实时聊天功能

2024 年 6 月 27 日

Laravel 是 Web 工匠最喜爱的 PHP 框架,现在又在其武器库中增加了一个强大的新工具:Reverb。在 Laravel 的官方软件包中,这个 WebSocket 服务器应用程序可让您无缝集成基于 Laravel 的应用程序中实时功能,从而将交互提升到一个全新的水平。

什么是 Laravel Reverb?

Reverb 充当基于 Laravel 的应用程序与其用户之间的中介。它基于 WebSockets 技术建立双向实时通信,允许网页在服务器上接收更新而无需完全刷新页面。这意味着您的用户可以更加动态、更灵敏地体验您的应用程序。

Laravel Reverb 的主要功能

疾速速度: 提供出色的实时信息性能,无任何延迟。

可扩展性: 与您的应用程序一起成长,以处理不断增加的用户流量。

无缝整合: 它与 Laravel 和 Laravel Echo 中添加的广播功能配合使用,使开发变得简单。

推送更新: 向客户推送更新、消息或事件,以便即时分享您的信息。

内置安全性: 数据加密与认证保障安全通信

将 Laravel Reverb 添加到你的聊天项目中

使用 Laravel Reverb,您可以构建动态聊天应用程序。消息会立即发布,让用户全面参与。以下是所涉及步骤的细分:

步骤 1:设置你的 Laravel 项目:

  • 确保您已安装 Laravel 应用程序(建议使用 11 或更高版本)。

  • 如果你是新手,请使用 composer create-project laravel/laravel your-chat-app-name

步骤 2:安装并配置 Reverb:

通过运行以下命令安装 Laravel Reverb:

php artisan install:broadcasting

安装 Reverb 后,你现在可以从 config/reverb.php 文件。要与 Reverb 建立连接,必须在客户端和服务器之间交换一组 Reverb“应用程序”凭据。这些凭据在服务器上配置,用于验证来自客户端的请求。您可以使用以下环境变量定义这些凭据:

BROADCAST_DRIVER=reverb
REVERB_APP_ID=my-app-id
REVERB_APP_KEY=my-app-key
REVERB_APP_SECRET=my-app-secret

它还会自动创建 echo.js 在里面 resources/js 目录。

import Echo from 'laravel-echo';

import Pusher from 'pusher-js';
window.Pusher = Pusher;

window.Echo = new Echo({
    broadcaster: 'reverb',
    key: import.meta.env.VITE_REVERB_APP_KEY,
    wsHost: import.meta.env.VITE_REVERB_HOST,
    wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
    wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});

按照 Laravel 文档了解特定于你的应用程序服务器的配置步骤 https://laravel.com/docs/11.x/reverb

步骤 3:运行服务器

您可以使用以下方式启动 Reverb 服务器 reverb:start Artisan 命令:

php artisan reverb:start

默认情况下,Reverb 服务器将在 0.0.0.0:8080 ,这使得它可以从所有网络接口访问。

如果要设置特定主机或端口,可以使用 –host–port 启动服务器时的选项。

php artisan reverb:start --host=127.0.0.1 --port=9000

您还可以定义 REVERB_SERVER_HOSTREVERB_SERVER_PORT 应用程序中的环境变量 .env 配置文件。

步骤 4:设置数据库

打开你的 .env 文件并调整设置以设置您的数据库。以下是使用 SQLite 的示例,以简化操作:

DB_CONNECTION=sqlite
DB_DATABASE=/path/to/database.sqlite

您只需运行以下命令即可创建 SQLite 数据库:

touch /path/to/database.sqlite

在本演示中,我们将创建五个预定义房间。让我们首先生成一个模型 ChatMessage 随着移民 chat_messages 桌子。

php artisan make:model ChatMessage --migration

为了简单起见,只为该模型创建名称属性并迁移它。

 Schema::create('chat_messages', function (Blueprint $table) {
         $table->id();
         $table->foreignId('receiver_id');
         $table->foreignId('sender_id');
         $table->text('text');
         $table->timestamps();
});
php artisan migrate

现在,让我们在 ChatMessage 模型。打开 ChatMessage.php 存档于 app/Models 目录并进行如下更新:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class ChatMessage extends Model
{
    use HasFactory;

    protected $fillable = [
        'sender_id',
        'receiver_id',
        'text'
    ];

    public function sender()
    {
        return $this->belongsTo(User::class, 'sender_id');
    }

    public function receiver()
    {
        return $this->belongsTo(User::class, 'receiver_id');
    }
}

步骤 5:创建活动

为了处理消息的广播,我们将创建一个名为 MessageSent .此事件将实现 Laravel 的 ShouldBroadcastNow 接口,允许通过 WebSocket 立即广播而无需排队。请按照以下步骤创建和设置事件:

  1. 在中创建一个新的 PHP 文件 App\Events 目录并命名 MessageSent.php

  2. 打开新创建的文件并添加以下代码:

<?php

namespace App\Events;

use App\Models\ChatMessage;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class MessageSent implements ShouldBroadcastNow
{
    use Dispatchable;
    use InteractsWithSockets;
    use SerializesModels;

    /**
     * Create a new event instance.
     */
    public function __construct(public ChatMessage $message)
    {
        //
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return array<int, \Illuminate\Broadcasting\Channel>
     */
    public function broadcastOn(): array
    {
        return [
            new PrivateChannel("chat.{$this->message->receiver_id}"),
        ];
    }
}

步骤 6:创建私人频道路由

channels.php Laravel 应用程序中的文件在定义用于实时通信功能的广播频道方面起着至关重要的作用。

<?php

use Illuminate\Support\Facades\Broadcast;

Broadcast::channel('chat.{id}', function ($user, $id) {
    return (int) $user->id === (int) $id;
});

此代码定义了一个名为 chat.{id} 使用 Laravel 的 Broadcast 外观。私有通道根据用户身份验证和授权逻辑限制访问。

步骤 7:定义路线

1. 聊天室路线:

Route::get('/chat/{friend}', function (User $friend) {
    return view('chat', [
        'friend' => $friend
    ]);
})->middleware(['auth'])->name('chat');

此路由负责渲染聊天界面。它接受一个动态参数 {friend} 代表用户的聊天伙伴。

3.获取聊天消息路线:

Route::get('/messages/{friend}', function (User $friend) {
    return ChatMessage::query()
        ->where(function ($query) use ($friend) {
            $query->where('sender_id', auth()->id())
                ->where('receiver_id', $friend->id);
        })
        ->orWhere(function ($query) use ($friend) {
            $query->where('sender_id', $friend->id)
                ->where('receiver_id', auth()->id());
        })
        ->with(['sender', 'receiver'])
        ->orderBy('id', 'asc')
        ->get();
})->middleware(['auth']);

此路由检索经过身份验证的用户与指定好友之间交换的聊天消息( {friend} )。该查询确保它检索到用户作为发送者或接收者的消息,包括对话的两个方向。

4.发送聊天消息路线:

Route::post('/messages/{friend}', function (User $friend) {
    $message = ChatMessage::create([
        'sender_id' => auth()->id(),
        'receiver_id' => $friend->id,
        'text' => request()->input('message')
    ]);

    broadcast(new MessageSent($message));

    return $message;
});

创建消息后,它利用 Laravel 的广播功能 broadcast(new MessageSent($message)) 。该线路通过 Reverb 将新创建的消息广播给所有连接的用户,从而实现实时聊天功能。

步骤 8:创建 Blade 视图

要呈现聊天界面,您需要创建一个 Blade 视图文件。创建一个名为 chat.blade.php 在里面 resources/views 目录并添加以下代码:

<x-app-layout>
    <x-slot name="header">
        <h2 class="text-xl font-semibold leading-tight text-gray-800">
            {{ $friend->name }}
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
            <div class="overflow-hidden bg-white shadow-sm sm:rounded-lg">
                <div class="p-6 bg-white border-b border-gray-200">
                    <chat-component 
                       :friend="{{ $friend }}" 
                       :current-user="{{ auth()->user() }}" 
                    />
                </div>
            </div>
        </div>
    </div>
</x-app-layout>

这里的关键要素是 <chat-component :friend="{{ $friend }}" :current-user="{{ auth()->user() }}" /> .此行渲染一个名为的 Vue.js 组件 chat-component

步骤 8:创建聊天组件

<template>
    <div>
        <div class="flex flex-col justify-end h-80">
            <div ref="messagesContainer" class="p-4 overflow-y-auto max-h-fit">
                <div
                    v-for="message in messages"
                    :key="message.id"
                    class="flex items-center mb-2"
                >
                    <div
                        v-if="message.sender_id === currentUser.id"
                        class="p-2 ml-auto text-white bg-blue-500 rounded-lg"
                    >
                        {{ message.text }}
                    </div>
                    <div v-else class="p-2 mr-auto bg-gray-200 rounded-lg">
                        {{ message.text }}
                    </div>
                </div>
            </div>
        </div>
        <div class="flex items-center">
            <input
                type="text"
                v-model="newMessage"
                @keydown="sendTypingEvent"
                @keyup.enter="sendMessage"
                placeholder="Type a message..."
                class="flex-1 px-2 py-1 border rounded-lg"
            />
            <button
                @click="sendMessage"
                class="px-4 py-1 ml-2 text-white bg-blue-500 rounded-lg"
            >
                Send
            </button>
        </div>
        <small v-if="isFriendTyping" class="text-gray-700">
            {{ friend.name }} is typing...
        </small>
    </div>
</template>

<script setup>
import axios from "axios";
import { nextTick, onMounted, ref, watch } from "vue";

const props = defineProps({
    friend: {
        type: Object,
        required: true,
    },
    currentUser: {
        type: Object,
        required: true,
    },
});

const messages = ref([]);
const newMessage = ref("");
const messagesContainer = ref(null);
const isFriendTyping = ref(false);
const isFriendTypingTimer = ref(null);

watch(
    messages,
    () => {
        nextTick(() => {
            messagesContainer.value.scrollTo({
                top: messagesContainer.value.scrollHeight,
                behavior: "smooth",
            });
        });
    },
    { deep: true }
);

const sendMessage = () => {
    if (newMessage.value.trim() !== "") {
        axios
            .post(`/messages/${props.friend.id}`, {
                message: newMessage.value,
            })
            .then((response) => {
                messages.value.push(response.data);
                newMessage.value = "";
            });
    }
};

const sendTypingEvent = () => {
    Echo.private(`chat.${props.friend.id}`).whisper("typing", {
        userID: props.currentUser.id,
    });
};

onMounted(() => {
    axios.get(`/messages/${props.friend.id}`).then((response) => {
        console.log(response.data);
        messages.value = response.data;
    });

    Echo.private(`chat.${props.currentUser.id}`)
        .listen("MessageSent", (response) => {
            messages.value.push(response.message);
        })
        .listenForWhisper("typing", (response) => {
            isFriendTyping.value = response.userID === props.friend.id;

            if (isFriendTypingTimer.value) {
                clearTimeout(isFriendTypingTimer.value);
            }

            isFriendTypingTimer.value = setTimeout(() => {
                isFriendTyping.value = false;
            }, 1000);
        });
});
</script>

此 Vue.js 组件管理聊天界面的动态行为。它显示一个可滚动的消息列表,根据发送者(当前用户或朋友)采用不同的样式。它提供了一个用于撰写新消息的输入字段和一个用于发送消息的按钮。它利用 axios 用于发出 HTTP 请求来获取初始消息并发送新消息。

使用 Laravel Echo 实现实时功能:

  1. 它监听广播 MessageSent 每当有新消息到达时,事件都会更新消息列表。

  2. 它利用私人频道上的耳语将用户的打字活动通知聊天伙伴,并从朋友那里接收类似的通知。

步骤 9:运行项目

要运行 Laravel 项目,我们需要执行以下命令:

php artisan serve

对于起始前端:

npm run dev

运行混响:

php artisan reverb:start

源代码

您可以在以下 GitHub 存储库中找到此 Laravel Reverb 聊天实现的源代码: https://github.com/qirolab/laravel-reverb-chat

通过利用 Laravel Reverb,开发人员可以确保他们的应用程序保持响应和动态,提供现代用户期望的实时更新和交互。祝您编码愉快!


帖子 使用 Reverb 和 Vue 向 Laravel 添加实时聊天功能 首先出现在 Laravel 新闻

加入 Laravel 时事通讯 直接在您的收件箱中获取所有此类最新的 Laravel 文章。