Foruma hoş geldin 👋, Ziyaretçi

Forum içeriğine ve tüm hizmetlerimize erişim sağlamak için foruma kayıt olmalı ya da giriş yapmalısınız. Foruma üye olmak tamamen ücretsizdir.

Laravel'de global query scope'ların N+1 sorgularını nasıl tetikleyebileceği ve `withoutGlobalScopes()` kullanım senaryom

stackor

Üye
Katılım
14 Mart 2026
Mesajlar
6
Merhaba arkadaşlar, bugün başımı çok ağrıtan ve performansı yerle bir eden bir Laravel tuzağından bahsedeceğim. Uygulamamda bir anda sayfa yüklemeleri inanılmaz yavaşladı, veritabanı sunucusu adeta inlemeye başladı. Profilleyip baktığımda karşımda kocaman bir N+1 sorgu problemi vardı. İşin ilginci, ben gayet düzgün with() ve load() kullanıyordum! Suçlu, masum gibi görünen global query scope'larım çıktı.

🔍 Sorunun Kökeni

Projemde, soft delete (yumuşak silme) kullanmayan ama duruma göre "aktif/pasif" olabilen birçok modelim var. Örneğin, `Product` (Ürün) modeli. Sadece `is_active = 1` olan kayıtların çekilmesi için bir global scope tanımlamıştım. Her şey harika gidiyordu, ta ki bir liste sayfasında bu ürünlerin bağlı olduğu `Category` bilgisini de göstermek isteyene kadar.

İlişkili modelleri çekerken (`Product::with('category')->get()`), Laravel önce ana Product kayıtlarını scope'lu haliyle çekiyor. Sonra her bir product için category ilişkisini yüklemeye çalıştığında, her seferinde category modeline de aynı aktiflik scope'u uygulamaya kalkıyordu! Category modelinde böyle bir scope olmadığı için hata almadım ama her ilişki yüklemesi ayrı bir sorguya dönüştü. İşte o an kafayı yemiştim.

💡 Çözüm ve withoutGlobalScopes()

Çözüm aslında Laravel'in dokümantasyonunda saklıydı: withoutGlobalScopes() metodu. Bu metod, belirli bir sorgudan tüm veya belirli global scope'ları geçici olarak kaldırmanı sağlıyor. Benim senaryomda, ilişkileri yüklerken bu scope'ların devre dışı kalması gerekiyordu.

İşte nasıl uyguladım:

PHP:
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class Product extends Model
{
    // Global Scope Tanımı (Modelin içinde veya Service Provider'da)
    protected static function booted()
    {
        static::addGlobalScope('active', function (Builder $builder) {
            $builder->where('is_active', 1);
        });
    }

    // Category ilişkisi
    public function category()
    {
        // İlişkiyi tanımlarken, global scope'ları KALDIRARAK tanımlıyoruz.
        return $this->belongsTo(Category::class)->withoutGlobalScopes();
    }
}

Ancak dikkat! Bazen tüm scope'ları değil de sadece belirli bir scope'u kaldırmak isteyebilirsiniz. Onun için withoutGlobalScope() (tekil) metodunu kullanıp scope'un class ismini verebilirsiniz.

PHP:
// Sadece 'active' isimli scope'u kaldırmak için (Scope bir class ise)
public function category()
{
    return $this->belongsTo(Category::class)->withoutGlobalScope('active');
    // veya scope bir class ise: ->withoutGlobalScope(\App\Scopes\ActiveScope::class)
}

🚀 Performans Sonucu

Bu küçük değişiklikten sonra neler oldu bir bakalım:

Önce: 100 ürün için 1 (ürünleri getir) + 100 (her ürün için ayrı kategori sorgusu) = 101 sorgu!
Sonra: 100 ürün için 1 (ürünleri getir) + 1 (tüm kategorileri tek seferde getir) = 2 sorgu.

Gördüğünüz gibi, 101 sorgudan 2 sorguya düşürdük. Sayfa yükleme süresi de neredeyse 10 kat hızlandı.

📌 Önemli Uyarılar

1. Bu yöntemi her ilişkide kullanmayın. Sadece ilişkinin karşı tarafında (`Category` gibi) sizin global scope'unuzun geçerli olmaması gerektiğinden emin olduğunuz durumlarda kullanın.
2. Eğer `Category` modelinin de kendi `active` scope'u olsaydı ve siz onu da kaldırsaydınız, pasif kategoriler de gelmeye başlardı. Dikkatli olun.
3. Bazen sorun `with()` içinde değil, `load()` veya lazy loading sırasında da ortaya çıkabilir. Çözüm aynı: ilişki tanımına `withoutGlobalScopes()` eklemek.

Sonuç olarak, global scope'lar harika ve temiz bir pattern ama ilişkilerle birlikte kullanılırken arka planda nasıl çalıştığını iyi anlamak gerekiyor. Laravel'in bu gibi ince detayları, gücünün yanında öğrenmemiz gereken tuzakları da beraber getiriyor.

Siz de Laravel'de global scope kullanırken benzer bir N+1 tuzağına düştünüz mü? Veya bu konuda farklı bir çözüm yaklaşımınız var mı? Yorumlarda deneyimlerinizi paylaşın, beraber öğrenelim!
 

Tema özelleştirme sistemi

Bu menüden forum temasının bazı alanlarını kendinize özel olarak düzenleye bilirsiniz.

Zevkine göre renk kombinasyonunu belirle

Tam ekran yada dar ekran

Temanızın gövde büyüklüğünü sevkiniz, ihtiyacınıza göre dar yada geniş olarak kulana bilirsiniz.

Geri