Senaryo:
// DeviceService.php - Satır 168
DB::table('sessions')->where('id', $oldest->id)->delete();
// ❌ Sadece DB'den siliniyor, browser'a bildirim YOK!
⏱️ Max Gecikme: Sonsuz! (Manuel refresh yapana kadar)
Senaryo:
// User.php - isPremium()
Cache::remember($cacheKey, 300, function() {
return $this->subscriptions()
->where('current_period_end', '>', now())
->first();
});
// ⚠️ Cache 5dk geçerli, expire anında tetiklenmiyor!
⏱️ Max Gecikme: 5 dakika
// Her 30 saniyede session kontrolü
setInterval(async () => {
const response = await fetch('/api/auth/check-session');
const data = await response.json();
if (!data.valid) {
// Session geçersiz (başka cihazdan çıkarıldı)
this.handleForceLogout();
}
if (data.is_premium !== this.isPremium) {
// Premium status değişti
this.handlePremiumChange(data.is_premium);
}
}, 30000); // 30 saniye
public function checkSession(Request $request)
{
// Session geçerli mi kontrol et
$sessionExists = DB::table('sessions')
->where('id', session()->getId())
->exists();
if (!$sessionExists) {
return response()->json([
'valid' => false,
'reason' => 'device_limit_exceeded'
]);
}
// Premium status
$user = Auth::user();
$isPremium = $user ? $user->isPremiumOrTrial() : false;
return response()->json([
'valid' => true,
'is_premium' => $isPremium
]);
}
handleForceLogout() {
// Müziği durdur
this.stopCurrentPlayback();
// Modal göster
this.showModal({
title: 'Başka Cihazdan Giriş Yapıldı',
message: 'Hesabınıza başka bir cihazdan giriş yapıldı.',
icon: '🔐',
actions: [
{ label: 'Tamam', action: 'close' }
]
});
// Logout
this.isLoggedIn = false;
this.currentUser = null;
// Guest preview moduna geç
setTimeout(() => {
window.location.reload();
}, 2000);
}
handlePremiumChange(newStatus) {
const oldStatus = this.isPremium;
this.isPremium = newStatus;
if (oldStatus && !newStatus) {
// Premium → Normal: Sınırsız → 30s preview
this.showToast('Üyeliğiniz sona erdi. 30s preview moduna geçildi.', 'warning');
// Mevcut şarkıyı preview mode ile yeniden yükle
if (this.currentSong) {
this.reloadCurrentSongWithPreview();
}
}
if (!oldStatus && newStatus) {
// Normal → Premium: 30s → Sınırsız
this.showToast('Premium üyeliğiniz aktif! Sınırsız dinleyebilirsiniz.', 'success');
// Preview timer'ları temizle
this.clearPreviewTimers();
}
}
protected function schedule(Schedule $schedule)
{
// Her gece 00:01'de expired subscription'ları kontrol et
$schedule->call(function () {
$expiredSubs = Subscription::where('status', 'active')
->where('current_period_end', '<=', now())
->get();
foreach ($expiredSubs as $sub) {
// Status güncelle
$sub->update(['status' => 'expired']);
// Cache temizle (Observer otomatik tetiklenir)
// SubscriptionObserver::updated() çalışır
Log::info('Subscription expired', [
'user_id' => $sub->user_id,
'subscription_id' => $sub->subscription_id
]);
}
})->dailyAt('00:01');
}
composer require beyondcode/laravel-websockets
# veya
composer require pusher/pusher-php-server
// Backend: DeviceService.php
event(new DeviceLimitExceeded($user->id, $oldSessionId));
// Frontend: Echo (Laravel Echo)
Echo.private(`user.${userId}`)
.listen('DeviceLimitExceeded', (e) => {
if (e.sessionId === currentSessionId) {
muzibu.handleForceLogout();
}
});
// Backend: SubscriptionObserver.php
event(new SubscriptionExpired($subscription->user_id));
// Frontend: Echo
Echo.private(`user.${userId}`)
.listen('SubscriptionExpired', (e) => {
muzibu.handlePremiumChange(false);
});
| Özellik | Polling (Hızlı) | WebSocket (Tam) |
|---|---|---|
| Geliştirme Süresi | 2-3 Saat | 1-2 Gün |
| Maliyet | Ücretsiz | $0-49/ay |
| Gecikme (Device Limit) | Max 30 saniye | 1-2 saniye |
| Gecikme (Subscription) | Max 5 dakika | Anında |
| Sunucu Yükü | Orta (Her 30s API) | Düşük (Event-driven) |
| Ölçeklenebilirlik | Orta | Yüksek |
| Kullanıcı Deneyimi | İyi | Mükemmel |
✅ Polling sistemi kur (2-3 saat)
✅ Laravel Scheduler ekle (Subscription expire)
✅ Test et, canlıya al
Sonuç: Max 30s gecikmeli ama çalışır bir sistem
🚀 Laravel WebSockets entegre et (1-2 gün)
🚀 Real-time event'lere geç
🚀 Polling sistemini kaldır
Sonuç: Anında tetikleme, mükemmel UX