Teknik rehber

Eloquent N+1 Sorguları: Eager Loading ve Sorgu Sayısını Doğrulama

Laravel Eloquent'te N+1 sorgu problemini anlamak, eager loading ile çözmek ve sorgu sayısını doğrulamak için uygulamalı rehber. Laravel 13 ve PHP 8.3+ ile uyumlu teknikler.

Yazılım geliştirme ve kodlama görseli
Görsel: Unsplash · Görsel kaynağı

Giriş

Laravel uygulamalarında performans darboğazlarının en yaygın nedenlerinden biri, Eloquent ilişkilerinin yanlış kullanımıyla ortaya çıkan N+1 sorgu problemidir. Özellikle listeleme sayfalarında, her bir kayıt için ayrı bir ilişki sorgusu çalıştırıldığında veritabanına gereksiz yük biner ve yanıt süreleri hızla artar. Bu yazıda, N+1 probleminin nasıl oluştuğunu, eager loading ile nasıl çözüleceğini ve sorgu sayısını nasıl doğrulayacağınızı resmi Laravel 13 ve PHP 8.3+ dokümantasyonuna dayanarak anlatıyoruz.

N+1 Sorgu Problemi Nedir?

N+1 problemi, bir ana sorgu (1) çalıştırıldıktan sonra, dönen her kayıt için ayrı bir ilişki sorgusu (N) tetiklendiğinde ortaya çıkar. Örneğin, 100 gönderi (post) listelediğinizde ve her gönderinin yazarını göstermek için $post->user çağrısını döngü içinde yaparsanız, toplam 101 sorgu çalışır. Bu durum, veritabanı bağlantı maliyetini ve gecikmeyi artırır.

Örnek: N+1'e Yol Açan Kod

// routes/web.php veya bir controller içinde
$posts = Post::all(); // 1 sorgu

foreach ($posts as $post) {
    echo $post->user->name; // Her post için 1 sorgu (N sorgu)
}

Yukarıdaki kod, 100 gönderi için 101 sorgu üretir. Laravel Debugbar veya DB::listen ile bu sayıyı gözlemleyebilirsiniz.

Eager Loading ile Çözüm

Eloquent, ilişkileri önceden yüklemek için with() metodunu sunar. Bu yöntem, ana sorguya ek olarak ilişkili kayıtları tek bir sorguda çeker ve N+1 problemini ortadan kaldırır.

Temel Eager Loading Kullanımı

$posts = Post::with('user')->get(); // 2 sorgu: posts + users

foreach ($posts as $post) {
    echo $post->user->name; // Ek sorgu yok
}

Bu kod, gönderi sayısından bağımsız olarak yalnızca 2 sorgu çalıştırır. Laravel 13'te with() metodu, ilişki tanımlarına göre otomatik olarak whereIn kullanır.

İç İçe İlişkiler ve Koşullu Eager Loading

Daha derin ilişkiler için nokta notasyonu kullanabilirsiniz:

$posts = Post::with(['user', 'comments.author'])->get();

Belirli koşullarla eager loading yapmak için closure kullanın:

$posts = Post::with(['comments' => function ($query) {
    $query->where('approved', true);
}])->get();

Laravel 13 dokümantasyonuna göre, with() içinde ilişki adı ve closure kullanımı desteklenir. Ayrıca load() metodu ile mevcut modellere sonradan eager loading uygulanabilir.

Sorgu Sayısını Doğrulama Yöntemleri

Eager loading uyguladıktan sonra sorgu sayısını doğrulamak, performans iyileştirmesini kanıtlamak için önemlidir. Laravel 13'te bunun için birkaç yerleşik yol vardır.

1. DB::listen ile Sorguları Dinleme

use Illuminate\Support\Facades\DB;

DB::listen(function ($query) {
    logger($query->sql, $query->bindings, $query->time);
});

$posts = Post::with('user')->get();

Bu yöntem, çalıştırılan her SQL sorgusunu loglar. Test ortamında sayım yapmak için bir sayaç kullanabilirsiniz.

2. Testlerde Sorgu Sayısını Doğrulama

Laravel 13, testlerde sorgu sayısını doğrulamak için DB::enableQueryLog() ve DB::getQueryLog() metodlarını sunar. Ayrıca assertDatabaseCount gibi yardımcılar da mevcuttur. Örnek bir PHPUnit testi:

public function test_eager_loading_reduces_queries()
{
    DB::enableQueryLog();

    $posts = Post::with('user')->get();

    $queries = DB::getQueryLog();
    $this->assertCount(2, $queries); // posts + users
}

Bu test, eager loading'in gerçekten 2 sorgu çalıştırdığını doğrular.

3. Laravel Debugbar ve Telescope

Geliştirme ortamında barryvdh/laravel-debugbar veya Laravel Telescope kullanarak sorgu sayısını görsel olarak izleyebilirsiniz. Bu araçlar, her istek için çalışan sorguları ve sürelerini listeler. Resmi Laravel dokümantasyonunda Telescope, yerleşik bir hata ayıklama aracı olarak önerilir.

Uygulanabilir Kod Örneği: Blog Listeleme

Aşağıda, N+1 problemini çözen ve sorgu sayısını doğrulayan tam bir örnek verilmiştir. Laravel 13 ve PHP 8.3+ uyumludur.

// app/Http/Controllers/PostController.php
namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function index()
    {
        DB::enableQueryLog();

        $posts = Post::with(['user', 'comments' => function ($query) {
            $query->latest()->limit(3);
        }])->paginate(20);

        $queryCount = count(DB::getQueryLog());
        logger("Toplam sorgu sayısı: {$queryCount}");

        return view('posts.index', compact('posts'));
    }
}

Bu kod, sayfalama ile birlikte eager loading kullanır ve sorgu sayısını loglar. paginate(20) kullanıldığında toplam sorgu sayısı genellikle 3-4 civarındadır (count, posts, users, comments).

Sonuç

N+1 sorgu problemi, Laravel uygulamalarında performansı ciddi şekilde etkileyebilir. Eager loading (with()) kullanarak ilişkili verileri tek sorguda çekmek, veritabanı yükünü azaltır ve yanıt sürelerini iyileştirir. Sorgu sayısını DB::listen, DB::getQueryLog() veya testler aracılığıyla doğrulamak, iyileştirmenin etkinliğini kanıtlar. Laravel 13 ve PHP 8.3+ ile bu teknikler resmi dokümantasyonda desteklenmektedir. Unutmayın: her zaman üretim ortamında sorgu loglarını kapatın ve performans testlerini gerçekçi veri setleriyle yapın.

Kaynaklar

🍪
Çerez Kullanımı

Kodumun Hatası, web sitesinin çalışmasını sağlamak ve kullanıcı deneyimini geliştirmek için çerezler kullanabilir. Zorunlu olmayan çerezler için tercihlerinizi belirleyebilir, ayrıntılı bilgiye Çerez Politikamızdan ulaşabilirsiniz.