Bu eğitimde, kullanıcılar arasında güvenli ve canlı mesajlaşmayı yönetmek için Laravel, Nuxt 3, Sanctum ve Laravel Reverb kullanarak gerçek zamanlı bir sohbet uygulaması oluşturacağız. Kullanıcı kimlik doğrulamasını kuracağız, güvenli bir API'ye bağlanacağız ve sorunsuz ve duyarlı bir deneyim için sohbetin anında güncellenmesini sağlayacağız.
Modülü, hem Tek Sayfalık Uygulama (SPA) hem de API kimlik doğrulamasını verimli bir şekilde işleyen SPA kimlik doğrulamasını yönetmek için kullanacağız. Bu modülü kullanma hakkında daha fazla bilgi edinmek için Nuxt 3 SPA kimlik doğrulaması makalesine bakın.
Bu projede, Laravel Reverb'i gerçek zamanlı etkinlik yayını için yapılandıracağız, Sanctum ile kimlik doğrulamayı uygulayacağız ve sohbet mesajlarını dinamik olarak görüntüleyen ve yöneten bir Nuxt 3 ön ucu oluşturacağız. Başlayalım!
Öncelikle, Laravel Sanctum'un kurulu ve yapılandırılmış olduğundan emin olun. Sanctum, tek sayfalık uygulamalar (SPA) için belirteç tabanlı kimlik doğrulamaya izin verir. Ardından, Laravel Reverb'i gerçek zamanlı yetenekler için kurun ve yapılandırın.
Aşağıdaki Reverb ortam değişkenlerini .env
dosyanıza ekleyin:
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
Sohbet mesajlarını depolamak için chat_messages
tablosu için bir geçiş oluşturun. Çalıştırın:
php artisan make:migration create_chat_messages_table
Göç dosyasını aşağıdaki şekilde güncelleyin:
Schema::create('chat_messages', function (Blueprint $table) { $table->id(); $table->foreignId('receiver_id'); $table->foreignId('sender_id'); $table->text('text'); $table->timestamps(); });
Tabloyu oluşturmak için geçişi çalıştırın:
php artisan migrate
Gerçek zamanlı mesaj yayınlamak için bir MessageSent
olay sınıfı oluşturun:
php artisan make:event MessageSent
Olay sınıfını güncelle:
<?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
dosyasında yayın kanalını tanımlayın:
Broadcast::channel('chat.{id}', function ($user, $id) { return (int) $user->id === (int) $id; });
Sohbet mesajlarının gönderilmesini ve alınmasını yönetmek için şu rotaları ekleyin:
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'ün Reverb ile bağlantı kurmasını sağlamak için .env
dosyanıza şu değişkenleri ekleyin:
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
Daha sonra bunları nuxt.config.ts
yükleyin:
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, }, }, });
Gerçek zamanlı güncellemeleri yönetmek için laravel-echo.client.ts
eklentisini oluşturun:
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 uygulamamızda gerçek zamanlı sohbeti etkinleştirmek için kullanıcıların diğer kullanıcıları seçip sohbet edebileceği yeni bir sayfa, chats > [id].vue
oluşturdum. Bu sayfa, gerçek zamanlı güncellemeler ve yazım göstergeleri için Laravel Echo ve WebSockets kullanarak sohbet mesajlarını ve kullanıcı verilerini yönetmek için bir Laravel arka ucuna bağlanır.
Bu sohbet işlevselliğini nasıl yapılandırdığımıza dair bir döküm şöyle:
İlk olarak, useRoute
bileşeniyle URL'den userID
alırız. Bu userID
sohbet ettiğimiz kullanıcıyı tanımlar ve gerekli kullanıcı ve sohbet mesajlarını yükler.
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[] => [] } );
Bu kod parçacığı, sayfa bağlandığında mesajları eşzamansız olarak yüklemek için özel bir bileşen olan useSanctumFetch
kullanır.
Her mesajı dinamik olarak oluşturuyoruz ve mevcut kullanıcıdan mı yoksa sohbet katılımcısından mı geldiğine göre biçimlendiriyoruz.
<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>
Sohbet penceresi nextTick()
kullanılarak otomatik olarak en son mesaja kaydırılır.
watch( messages, () => { nextTick(() => messageContainerScrollToBottom()); }, { deep: true } ); function messageContainerScrollToBottom() { if (!messagesContainer.value) return; messagesContainer.value.scrollTo({ top: messagesContainer.value.scrollHeight, behavior: 'smooth' }); }
Kullanıcılar bir girdi alanıyla mesaj gönderebilirler. Gönderim üzerine, Laravel arka ucuna bir POST isteği gönderilir.
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 = ""; };
Mesajlar gönderildikten hemen sonra güncellenir.
Laravel Echo MessageSent
ve typing
gibi önemli olayları dinler.
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); }); } });
Bu, bir saniyelik hareketsizlikten sonra kaybolan gerçek zamanlı mesaj güncellemelerini ve yazma göstergelerini yönetir.
Bir yazma göstergesi göstermek için, whisper
ile bir "yazma" olayını tetikleriz.
const sendTypingEvent = () => { if (!user.value || !currentUser.value) return; $echo.private(`chat.${user.value.id}`).whisper("typing", { userID: currentUser.value.id }); };
Bu olay, kullanıcı her yazı yazdığında gönderilir ve alıcı yazma göstergesini görür.
Sohbet arayüzünde, seçilen kullanıcının adının yer aldığı bir başlık ve mesajlar ile giriş alanı için bölümler yer alır.
<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 ve Echo with Nuxt 3 kullanılarak gerçek zamanlı sohbet özelliği kurulumu tamamlandı.
Artık Laravel, Sanctum, Reverb ve Nuxt 3 kullanarak güvenli, gerçek zamanlı bir sohbet uygulaması oluşturduk. Bu kurulum, mesaj tepkileri veya birden fazla sohbet odası gibi ek özellikleri içerecek şekilde kolayca ölçeklenebilir.
Kodun tamamı için GitHub deposunu ziyaret edin.