در این آموزش، ما یک برنامه چت بلادرنگ با استفاده از Laravel، Nuxt 3، Sanctum و Laravel Reverb برای مدیریت پیامهای امن و زنده بین کاربران میسازیم. ما احراز هویت کاربر را راهاندازی میکنیم، به یک API ایمن متصل میشویم و مطمئن میشویم که چت فوراً بهروزرسانی میشود تا تجربهای روان و پاسخگو داشته باشد.
ما از این ماژول برای مدیریت احراز هویت SPA استفاده خواهیم کرد، که به طور کارآمدی از تأیید اعتبار یک صفحه (SPA) و API استفاده می کند. برای کسب اطلاعات بیشتر در مورد استفاده از این ماژول، به مقاله احراز هویت Nuxt 3 SPA مراجعه کنید.
در این پروژه، ما Laravel Reverb را برای پخش رویدادهای بیدرنگ پیکربندی میکنیم، احراز هویت را با Sanctum پیادهسازی میکنیم و یک فرانتاند Nuxt 3 میسازیم که پیامهای چت را به صورت پویا نمایش و مدیریت میکند. بیایید شروع کنیم!
پیش نیازها
- آشنایی اولیه با Laravel، Sanctum و Nuxt 3.
- آشنایی با پخش رویداد در لاراول
- پروژه لاراول که با Sanctum راه اندازی شده است.
- Nuxt 3 به عنوان قسمت جلویی برنامه شما نصب و پیکربندی شده است.
مرحله 1: Laravel Sanctum و Laravel Reverb را راه اندازی کنید
ابتدا مطمئن شوید که 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
مرحله 2: انتقال جدول پیام های چت را ایجاد کنید
برای ذخیره پیام های چت، یک انتقال برای جدول 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
مرحله 3: رویداد MessageSent را ایجاد کنید
برای پخش پیامها در زمان واقعی، یک کلاس رویداد 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}")]; } }
مرحله 4: کانال های پخش را تعریف کنید
در channels.php
، کانال پخش را تعریف کنید:
Broadcast::channel('chat.{id}', function ($user, $id) { return (int) $user->id === (int) $id; });
مرحله 5: مسیرهایی را برای واکشی و ارسال پیام تعریف کنید
این مسیرها را برای مدیریت ارسال و بازیابی پیام های چت اضافه کنید:
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; });
مرحله 6: Reverb را در Nuxt 3 پیکربندی کنید
برای اینکه 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, }, }, });
مرحله 7: Laravel Echo Client را در Nuxt 3 راه اندازی کنید
برای مدیریت بهروزرسانیهای بلادرنگ، یک پلاگین 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 } }; });
مرحله 8: رابط چت بلادرنگ را در Nuxt 3 بسازید
برای فعال کردن چت بلادرنگ در برنامه Nuxt 3 ما، یک صفحه جدید ایجاد کردم، chats > [id].vue
، که در آن کاربران میتوانند کاربران دیگر را انتخاب کرده و با آنها چت کنند. این صفحه برای مدیریت پیامهای چت و دادههای کاربر، با استفاده از Laravel Echo و WebSockets برای بهروزرسانیهای بیدرنگ و نشانگرهای تایپ، به پشتیبان لاراول متصل میشود.
در اینجا به طور خلاصه نحوه ساختار این عملکرد چت ارائه شده است:
مرحله 8.1: داده های چت کاربر انتخابی را بارگیری کنید
ابتدا 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
، یک ترکیب سفارشی، برای بارگیری پیامها به صورت ناهمزمان هنگام نصب صفحه استفاده میکند.
مرحله 8.2: نمایش پیام های چت
ما هر پیام را به صورت پویا ارائه میکنیم، و بر اساس اینکه از کاربر فعلی یا شرکتکننده چت است، به آنها سبک میدهیم.
<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' }); }
مرحله 8.3: ارسال پیام های جدید
کاربران می توانند با یک فیلد ورودی پیام ارسال کنند. پس از ارسال، یک درخواست 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 = ""; };
پیام ها بلافاصله پس از ارسال به روز می شوند.
مرحله 8.4: ویژگی های بلادرنگ با Laravel Echo
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); }); } });
این به روز رسانی پیام ها و نشانگرهای تایپ بلادرنگ را کنترل می کند که پس از یک ثانیه عدم فعالیت ناپدید می شوند.
مرحله 8.5: نشانگر تایپ
برای نشان دادن یک نشانگر تایپ، رویداد "تایپ" را با whisper
فعال می کنیم.
const sendTypingEvent = () => { if (!user.value || !currentUser.value) return; $echo.private(`chat.${user.value.id}`).whisper("typing", { userID: currentUser.value.id }); };
این رویداد هر زمان که کاربر تایپ کند ارسال می شود و گیرنده نشانگر تایپ را می بیند.
مرحله 8.6: رابط کاربری
رابط چت شامل یک هدر با نام کاربر انتخابی و بخش هایی برای پیام ها و فیلد ورودی است.
<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 مراجعه کنید.