در این آموزش، ما یک برنامه چت بلادرنگ با استفاده از Laravel، Nuxt 3، Sanctum و Laravel Reverb برای مدیریت پیامهای امن و زنده بین کاربران میسازیم. ما احراز هویت کاربر را راهاندازی میکنیم، به یک API ایمن متصل میشویم و مطمئن میشویم که چت فوراً بهروزرسانی میشود تا تجربهای روان و پاسخگو داشته باشد.
ما از این ماژول برای مدیریت احراز هویت SPA استفاده خواهیم کرد، که به طور کارآمدی از تأیید اعتبار یک صفحه (SPA) و API استفاده می کند. برای کسب اطلاعات بیشتر در مورد استفاده از این ماژول، به مقاله احراز هویت Nuxt 3 SPA مراجعه کنید.
در این پروژه، ما Laravel Reverb را برای پخش رویدادهای بیدرنگ پیکربندی میکنیم، احراز هویت را با Sanctum پیادهسازی میکنیم و یک فرانتاند Nuxt 3 میسازیم که پیامهای چت را به صورت پویا نمایش و مدیریت میکند. بیایید شروع کنیم!
ابتدا مطمئن شوید که Laravel Sanctum نصب و پیکربندی شده است. Sanctum امکان احراز هویت مبتنی بر توکن را برای برنامه های کاربردی تک صفحه ای (SPA) می دهد. سپس، Laravel Reverb را برای قابلیت های بلادرنگ نصب و پیکربندی کنید.
متغیرهای محیط Reverb زیر را به فایل .env
خود اضافه کنید:
REVERB_APP_ID=my-app-id REVERB_APP_KEY=my-app-key REVERB_APP_SECRET=my-app-secret REVERB_HOST="localhost" REVERB_PORT=8080 REVERB_SCHEME=http
برای ذخیره پیام های چت، یک انتقال برای جدول chat_messages
ایجاد کنید. اجرا کنید:
php artisan make:migration create_chat_messages_table
فایل مهاجرت را به صورت زیر به روز کنید:
Schema::create('chat_messages', function (Blueprint $table) { $table->id(); $table->foreignId('receiver_id'); $table->foreignId('sender_id'); $table->text('text'); $table->timestamps(); });
برای ایجاد جدول، migration را اجرا کنید:
php artisan migrate
برای پخش پیامها در زمان واقعی، یک کلاس رویداد MessageSent
ایجاد کنید:
php artisan make:event MessageSent
به روز رسانی کلاس رویداد:
<?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, InteractsWithSockets, SerializesModels; public function __construct(public ChatMessage $message) { // } public function broadcastOn(): array { return [new PrivateChannel("chat.{$this->message->receiver_id}")]; } }
در channels.php
، کانال پخش را تعریف کنید:
Broadcast::channel('chat.{id}', function ($user, $id) { return (int) $user->id === (int) $id; });
این مسیرها را برای مدیریت ارسال و بازیابی پیام های چت اضافه کنید:
Route::get('/messages/{user}', function (User $user, Request $request) { return ChatMessage::query() ->where(function ($query) use ($user, $request) { $query->where('sender_id', $request->user()->id) ->where('receiver_id', $user->id); }) ->orWhere(function ($query) use ($user, $request) { $query->where('sender_id', $user->id) ->where('receiver_id', $request->user()->id); }) ->with(['sender', 'receiver']) ->orderBy('id', 'asc') ->get(); })->middleware('auth:sanctum'); Route::post('/messages/{user}', function (User $user, Request $request) { $request->validate(['message' => 'required|string']); $message = ChatMessage::create([ 'sender_id' => $request->user()->id, 'receiver_id' => $user->id, 'text' => $request->message ]); broadcast(new MessageSent($message)); return $message; });
برای اینکه Nuxt 3 با Reverb ارتباط برقرار کند، این متغیرها را به فایل .env
خود اضافه کنید:
NUXT_PUBLIC_REVERB_APP_ID=my-app-id NUXT_PUBLIC_REVERB_APP_KEY=my-app-key NUXT_PUBLIC_REVERB_APP_SECRET=my-app-secret NUXT_PUBLIC_REVERB_HOST="localhost" NUXT_PUBLIC_REVERB_PORT=8080 NUXT_PUBLIC_REVERB_SCHEME=http
سپس، آنها را در nuxt.config.ts
بارگیری کنید:
export default defineNuxtConfig({ runtimeConfig: { public: { REVERB_APP_ID: process.env.NUXT_PUBLIC_REVERB_APP_ID, REVERB_APP_KEY: process.env.NUXT_PUBLIC_REVERB_APP_KEY, REVERB_APP_SECRET: process.env.NUXT_PUBLIC_REVERB_APP_SECRET, REVERB_HOST: process.env.NUXT_PUBLIC_REVERB_HOST, REVERB_PORT: process.env.NUXT_PUBLIC_REVERB_PORT, REVERB_SCHEME: process.env.NUXT_PUBLIC_REVERB_SCHEME, }, }, });
برای مدیریت بهروزرسانیهای بلادرنگ، یک پلاگین laravel-echo.client.ts
ایجاد کنید:
import Echo from "laravel-echo"; import Pusher, { type ChannelAuthorizationCallback } from "pusher-js"; declare global { interface Window { Echo: Echo; Pusher: typeof Pusher; } } export default defineNuxtPlugin(() => { window.Pusher = Pusher; const config = useRuntimeConfig(); const echo = new Echo({ broadcaster: "reverb", key: config.public.REVERB_APP_KEY, wsHost: config.public.REVERB_HOST, wsPort: config.public.REVERB_PORT ?? 80, wssPort: config.public.REVERB_PORT ?? 443, forceTLS: (config.public.REVERB_SCHEME ?? "https") === "https", enabledTransports: ["ws", "wss"], authorizer: (channel, options) => ({ authorize: (socketId, callback) => { useSanctumFetch("api/broadcasting/auth", { method: "post", body: { socket_id: socketId, channel_name: channel.name }, }) .then(response => callback(null, response)) .catch(error => callback(error, null)); }, }), }); return { provide: { echo } }; });
برای فعال کردن چت بلادرنگ در برنامه Nuxt 3 ما، یک صفحه جدید ایجاد کردم، chats > [id].vue
، که در آن کاربران میتوانند کاربران دیگر را انتخاب کرده و با آنها چت کنند. این صفحه برای مدیریت پیامهای چت و دادههای کاربر، با استفاده از Laravel Echo و WebSockets برای بهروزرسانیهای بیدرنگ و نشانگرهای تایپ، به پشتیبان لاراول متصل میشود.
در اینجا به طور خلاصه نحوه ساختار این عملکرد چت ارائه شده است:
ابتدا userID
از URL با useRoute
composable بازیابی می کنیم. این userID
کاربری را که با او چت میکنیم شناسایی میکند و پیامهای کاربر و چت لازم را بارگیری میکند.
const route = useRoute(); const userID = route.params.id; const { user: currentUser } = useSanctum<User>(); const { data: user } = await useAsyncData( `user-${userID}`, () => useSanctumFetch<User>(`/api/users/${userID}`) ); const { data: messages } = useAsyncData( `messages-${userID}`, () => useSanctumFetch<ChatMessage[]>(`/api/messages/${userID}`), { default: (): ChatMessage[] => [] } );
این قطعه از useSanctumFetch
، یک ترکیب سفارشی، برای بارگیری پیامها به صورت ناهمزمان هنگام نصب صفحه استفاده میکند.
ما هر پیام را به صورت پویا ارائه میکنیم، و بر اساس اینکه از کاربر فعلی یا شرکتکننده چت است، به آنها سبک میدهیم.
<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>
پنجره چت به طور خودکار با استفاده از nextTick()
به آخرین پیام می رود.
watch( messages, () => { nextTick(() => messageContainerScrollToBottom()); }, { deep: true } ); function messageContainerScrollToBottom() { if (!messagesContainer.value) return; messagesContainer.value.scrollTo({ top: messagesContainer.value.scrollHeight, behavior: 'smooth' }); }
کاربران می توانند با یک فیلد ورودی پیام ارسال کنند. پس از ارسال، یک درخواست POST به باطن لاراول ارسال می شود.
const newMessage = ref(""); const sendMessage = async () => { if (!newMessage.value.trim()) return; const messageResponse = await useSanctumFetch<ChatMessage>(`/api/messages/${userID}`, { method: "post", body: { message: newMessage.value } }); messages.value.push(messageResponse); newMessage.value = ""; };
پیام ها بلافاصله پس از ارسال به روز می شوند.
Laravel Echo به رویدادهای کلیدی مانند MessageSent
و typing
گوش می دهد.
onMounted(() => { if (currentUser.value) { $echo.private(`chat.${currentUser.value.id}`) .listen('MessageSent', (response: { message: ChatMessage }) => { messages.value.push(response.message); }) .listenForWhisper("typing", (response: { userID: number }) => { isUserTyping.value = response.userID === user.value?.id; if (isUserTypingTimer.value) clearTimeout(isUserTypingTimer.value); isUserTypingTimer.value = setTimeout(() => { isUserTyping.value = false; }, 1000); }); } });
این به روز رسانی پیام ها و نشانگرهای تایپ بلادرنگ را کنترل می کند که پس از یک ثانیه عدم فعالیت ناپدید می شوند.
برای نشان دادن یک نشانگر تایپ، رویداد "تایپ" را با whisper
فعال می کنیم.
const sendTypingEvent = () => { if (!user.value || !currentUser.value) return; $echo.private(`chat.${user.value.id}`).whisper("typing", { userID: currentUser.value.id }); };
این رویداد هر زمان که کاربر تایپ کند ارسال می شود و گیرنده نشانگر تایپ را می بیند.
رابط چت شامل یک هدر با نام کاربر انتخابی و بخش هایی برای پیام ها و فیلد ورودی است.
<template> <div> <header v-if="user" class="bg-white shadow"> <div class="px-4 py-6 mx-auto max-w-7xl sm:px-6 lg:px-8"> <h1 class="text-2xl font-bold ">{{ user.name }}</h1> </div> </header> <div class="flex flex-col items-center py-5"> <div class="container w-full h-full p-8 space-y-3 bg-white rounded-xl"> <div v-if="currentUser"> <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-shrink-0"> <span v-if="user && isUserTyping" class="text-gray-500"> {{ user.name }} is typing... </span> <div class="flex items-center justify-between w-full p-4 border-t border-gray-200"> <input type="text" v-model="newMessage" @keydown="sendTypingEvent" @keyup.enter="sendMessage" class="w-full p-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="Type a message..." /> <button @click.prevent="sendMessage" class="inline-flex items-center justify-center w-12 h-12 ml-4 text-white bg-blue-500 rounded-lg hover:bg-blue-600"> <svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" /> </svg> </button> </div> </div> </div> </div> </div> </div> </template>
این راه اندازی ویژگی چت بلادرنگ را با استفاده از Laravel Sanctum و Echo با Nuxt 3 تکمیل می کند.
ما اکنون یک برنامه چت امن و بیدرنگ با استفاده از Laravel، Sanctum، Reverb، و Nuxt 3 ایجاد کردهایم. این تنظیمات را میتوان بهراحتی مقیاسبندی کرد تا شامل ویژگیهای اضافی مانند واکنشهای پیام یا اتاقهای گفتگوی متعدد باشد.
برای دریافت کد کامل، به مخزن GitHub مراجعه کنید.