🎯 4 Farklı Yaklaşım
Yaklaşım 1: Cron Job (Klasik)
Her gün belirli saatte çalışan otomatik script.
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
Subscription::whereHas('plan', fn($q) => $q->where('is_trial', true))
->where('status', 'active')
->whereDate('ends_at', '<=', now())
->update(['status' => 'expired']);
})->daily();
}
// Crontab
* * * * * php /path/to/artisan schedule:run
✅ Artıları:
- Zamanında çalışır (her gün aynı saatte)
- Performans etkisi yok (arka planda)
- Profesyonel yaklaşım
❌ Eksileri:
- Cron çalışmazsa sistem durur!
- Shared hosting'de sorunlu olabilir
- Kurulum gerektirir
Yaklaşım 2: Lazy Check (Kontrol Sırasında)
Trial kontrolü yapılırken otomatik expire eder.
// User model
public function isPremium(): bool
{
// Aktif subscription var mı?
$subscription = $this->subscriptions()
->where('status', 'active')
->first();
if (!$subscription) {
return false;
}
// 🔥 LAZY CHECK: Bitmiş mi kontrol et!
if ($subscription->ends_at < now()) {
// Otomatik expire et
$subscription->update(['status' => 'expired']);
return false;
}
return true;
}
✅ Artıları:
- CRON GEREKSIZ! Her kontrol sırasında otomatik
- Her zaman çalışır (kullanıcı siteye girdiğinde)
- Shared hosting'de sorunsuz
- Kurulum gerektirmez
❌ Eksileri:
- Kullanıcı siteye girmezse expire olmaz (nadir)
- Her kontrol sırasında ekstra sorgu (minimal)
Yaklaşım 3: Middleware (Her İstek)
Her HTTP isteğinde arka planda kontrol eder.
// app/Http/Middleware/CheckExpiredTrials.php
public function handle($request, Closure $next)
{
// Sadece auth kullanıcı için
if (auth()->check()) {
$user = auth()->user();
// Trial subscription var mı?
$trialSubscription = $user->subscriptions()
->whereHas('plan', fn($q) => $q->where('is_trial', true))
->where('status', 'active')
->first();
if ($trialSubscription && $trialSubscription->ends_at < now()) {
$trialSubscription->update(['status' => 'expired']);
}
}
return $next($request);
}
✅ Artıları:
- Gerçek zamanlı (her istek)
- Cron gerektirmez
- Otomatik çalışır
❌ Eksileri:
- Her istekte DB sorgusu (performans)
- Gereksiz yük (her sayfa yüklenişinde)
Yaklaşım 4: Database Scope (Akıllı Sorgu)
Database seviyesinde otomatik filtreleme.
// Subscription model
public function scopeActive($query)
{
return $query->where('status', 'active')
->where(function($q) {
// Bitiş tarihi gelmemişse VEYA null ise
$q->where('ends_at', '>', now())
->orWhereNull('ends_at');
});
}
// Kullanım
$activeSubscriptions = $user->subscriptions()->active()->get();
// Bitmiş trial'lar otomatik filtrelenir!
✅ Artıları:
- Gerçek zamanlı (sorgu sırasında)
- Cron gerektirmez
- Performanslı (DB seviyesinde)
❌ Eksileri:
- Status güncellenmiyor (sadece filtreliyor)
- Raporlama karışık (status hala active)
💻 Hibrit Çözüm - Kod Örneği
1. User Model (Lazy Check)
// app/Models/User.php
public function isPremium(): bool
{
if (!$this->isMuzibuTenant()) {
return false;
}
$subscription = $this->subscriptions()
->where('status', 'active')
->where('ends_at', '>', now()) // Bitişi gelmiş olanları alma!
->first();
// 🔥 LAZY CHECK: Bitmiş ama status active kalanları temizle
$expiredSubscription = $this->subscriptions()
->where('status', 'active')
->where('ends_at', '<=', now())
->first();
if ($expiredSubscription) {
$expiredSubscription->update(['status' => 'expired']);
}
return $subscription ? true : false;
}
2. Subscription Model (Helper Method)
// app/Models/Subscription.php
public static function expireTrials()
{
$expired = self::whereHas('plan', function($q) {
$q->where('is_trial', true);
})
->where('status', 'active')
->where('ends_at', '<=', now())
->get();
foreach ($expired as $subscription) {
$subscription->update(['status' => 'expired']);
// Log
\Log::info("Trial expired for user: {$subscription->user_id}");
}
return $expired->count();
}
3. Cron Job (Yedek)
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
$count = \App\Models\Subscription::expireTrials();
\Log::info("Expired {$count} trial subscriptions via cron");
})->daily();
}
✅ Sonuç:
- Kullanıcı siteye girer: Lazy check devreye girer → Trial expire
- Kullanıcı 1 hafta girmez: Cron job devreye girer → Trial expire
- Cron çalışmaz: Sorun yok! Lazy check her zaman çalışır
- Kullanıcı hiç gelmez: Cron job temizler (önemli değil zaten)