Muriyi nyigisho, tuzubaka porogaramu nyayo yo kuganira dukoresheje Laravel, Nuxt 3, Sanctum, na Laravel Reverb kugirango dukemure ubutumwa bwizewe kandi bwuzuye hagati yabakoresha. Tuzashyiraho umukoresha wemeza, duhuze na API itekanye, kandi tumenye neza ko ivugurura ryibiganiro ako kanya kuburambe bworoshye kandi bwitondewe.
Tuzakoresha module yo gucunga SPA kwemeza, ikora neza byombi Urupapuro rumwe rukoreshwa (SPA) hamwe no kwemeza API. Kugira ngo umenye byinshi kubyerekeye gukoresha iyi module, reba ingingo kuri Nuxt 3 SPA yo kwemeza .
Muri uyu mushinga, tuzashyiraho Laravel Reverb mugihe nyacyo cyo gutangaza amakuru, dushyire mubikorwa kwemeza hamwe na Sanctum, kandi twubake imbere ya Nuxt 3 yerekana kandi ikayobora ubutumwa bwibiganiro. Reka dutangire!
Ibisabwa
- Kumenyera shingiro na Laravel, Sanctum, na Nuxt 3.
- Gusobanukirwa ibyatangajwe muri Laravel.
- Umushinga wa Laravel washyizweho na Sanctum.
- Nuxt 3 yashizwemo kandi igizwe nkimbere ya porogaramu yawe.
Intambwe ya 1: Shiraho Laravel Sanctum na Laravel Reverb
Ubwa mbere, menya neza ko Laravel Sanctum yashyizweho kandi igashyirwaho. Sanctum yemerera ibimenyetso-byemewe kwemeza urupapuro rumwe rukoreshwa (SPA). Noneho, shyiramo kandi ugene Laravel Reverb kubushobozi-bwigihe.
Ongeraho ibikurikira Reverb ibidukikije bihinduka muri dosiye yawe .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
Intambwe ya 2: Kora Ubutumwa bwo Kuganira Imbonerahamwe Kwimuka
Kubika ubutumwa bwibiganiro, kora iyimuka kumeza chat_messages
. Kwiruka:
php artisan make:migration create_chat_messages_table
Kuvugurura dosiye yimuka kuburyo bukurikira:
Schema::create('chat_messages', function (Blueprint $table) { $table->id(); $table->foreignId('receiver_id'); $table->foreignId('sender_id'); $table->text('text'); $table->timestamps(); });
Koresha kwimuka kugirango ukore imbonerahamwe:
php artisan migrate
Intambwe ya 3: Kurema Ubutumwa bwoherejwe
Kugirango utangaze ubutumwa mugihe nyacyo, kora MessageSent
bwibikorwa byicyiciro:
php artisan make:event MessageSent
Kuvugurura ibyiciro byibyiciro:
<?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}")]; } }
Intambwe ya 4: Sobanura Imiyoboro Yamamaza
channels.php
, sobanura umuyoboro wogutangaza:
Broadcast::channel('chat.{id}', function ($user, $id) { return (int) $user->id === (int) $id; });
Intambwe ya 5: Sobanura inzira zo Kubona no Kohereza Ubutumwa
Ongeraho izi nzira zo gucunga kohereza no kugarura ubutumwa bwibiganiro:
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; });
Intambwe ya 6: Hindura Reverb muri Nuxt 3
Kwemerera Nuxt 3 guhuza na Reverb, ongeraho ibi bihinduka muri dosiye yawe .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
Noneho, ubishyire muri 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, }, }, });
Intambwe 7: Shiraho umukiriya wa Laravel Echo muri Nuxt 3
Gucunga amakuru yigihe-gihe, kora laravel-echo.client.ts
plugin:
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 } }; });
Intambwe ya 8: Kubaka Imigaragarire nyayo-Ibiganiro muri Nuxt 3
Kugirango ushoboze kuganira mugihe nyacyo muri porogaramu yacu ya Nuxt 3, nashizeho page nshya, chats > [id].vue
, aho abakoresha bashobora guhitamo no kuganira nabandi bakoresha. Uru rupapuro ruhuza inyuma ya Laravel kugirango ucunge ubutumwa bwibiganiro hamwe namakuru yumukoresha, ukoresheje Laravel Echo na WebSockets mugihe nyacyo cyo kuvugurura no kwerekana ibimenyetso.
Dore ibice byuburyo twateguye iyi mikorere yo kuganira:
Intambwe 8.1: Fungura amakuru Yatoranijwe Yabakoresha
Ubwa mbere, dukura userID
muri URL hamwe no useRoute
ihimbye. Uyu userID
yerekana umukoresha tuganira kandi yikoreza umukoresha n'ubutumwa bwo kuganira.
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[] => [] } );
Aka gatabo gakoresha useSanctumFetch
, yihariye igizwe, kugirango yikoreze ubutumwa butajegajega iyo page ihagaze.
Intambwe 8.2: Kwerekana Ubutumwa bwo Kuganira
Dutanga buri butumwa muburyo bukomeye, tukabushushanya dukurikije niba bituruka kubakoresha ubu cyangwa abitabiriye ibiganiro.
<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>
Idirishya ryibiganiro rihita ryerekeza kubutumwa buheruka ukoresheje nextTick()
.
watch( messages, () => { nextTick(() => messageContainerScrollToBottom()); }, { deep: true } ); function messageContainerScrollToBottom() { if (!messagesContainer.value) return; messagesContainer.value.scrollTo({ top: messagesContainer.value.scrollHeight, behavior: 'smooth' }); }
Intambwe 8.3: Kohereza Ubutumwa bushya
Abakoresha barashobora kohereza ubutumwa hamwe numwanya winjiza. Mugihe cyoherejwe, icyifuzo cya POST cyoherejwe inyuma ya Laravel.
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 = ""; };
Ubutumwa buravugururwa ako kanya nyuma yo koherezwa.
Intambwe 8.4: Ibihe-Ibiranga na Laravel Echo
Laravel Echo yumva ibintu byingenzi nka MessageSent
no 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); }); } });
Ibi bikora ubutumwa bwigihe-gihe cyo kuvugurura no kwandika ibipimo, bikabura nyuma yisegonda yo kudakora.
Intambwe 8.5: Ikimenyetso cyo Kwandika
Kugirango twerekane icyerekezo cyo kwandika, dukurura "kwandika" ibyabaye whisper
.
const sendTypingEvent = () => { if (!user.value || !currentUser.value) return; $echo.private(`chat.${user.value.id}`).whisper("typing", { userID: currentUser.value.id }); };
Ibirori byoherejwe igihe cyose umukoresha yanditse, kandi uyahawe abona icyerekezo cyo kwandika.
Intambwe 8.6: Imigaragarire y'abakoresha
Isohora ryibiganiro ririmo umutwe hamwe nizina ryatoranijwe ryumukoresha nibice byubutumwa hamwe numwanya winjiza.
<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>
Ibi birangiza igihe nyacyo cyo kuganira ukoresheje Laravel Sanctum na Echo hamwe na Nuxt 3.
Umwanzuro
Ubu twashizeho porogaramu itekanye, mugihe nyacyo cyo kuganira dukoresheje Laravel, Sanctum, Reverb, na Nuxt 3. Iyi mikorere irashobora kugabanywa byoroshye kugirango ushiremo ibintu byongeweho nkibisubizo byubutumwa cyangwa ibyumba byinshi byo kuganiriramo.
Kode yuzuye, sura ububiko bwa GitHub .