N+1 Problem & Eager Loading — Database

Apa itu N+1 Problem? N+1 problem terjadi ketika kode menjalankan 1 query untuk mendapatkan daftar data, lalu N query tambahan untuk setiap item. Ini sangat tida

Apa itu N+1 Problem?

N+1 problem terjadi ketika kode menjalankan 1 query untuk mendapatkan daftar data, lalu N query tambahan untuk setiap item. Ini sangat tidak efisien dan menjadi lebih buruk seiring data bertambah.

Contoh N+1

// ❌ N+1 Problem
// 1 query: ambil semua users
$users = User::all();

// N queries: satu query per user untuk ambil posts
foreach ($users as $user) {
    echo $user->posts->count();
    // Setiap iterasi = 1 query ke database
}
// Jika ada 100 users → 101 queries!

⚠️ Skenario produksi nyata: halaman dashboard menampilkan 50 user dengan jumlah order-nya. Di local dengan 10 user terasa cepat (11 query, 30ms). Saat deploy ke production dengan 50 user per halaman, tiba-tiba halaman loading 2+ detik karena 51 query ke database. Parahnya, saat trafik naik, database overwhelmed oleh request "kecil" yang menggandakan beban. Monitor dengan Laravel Telescope atau Debugbar — kalau kamu lihat >5 query per request, kemungkinan besar ada N+1 terselubung. Di dev, enable Model::preventLazyLoading() — Laravel akan throw exception kalau ada lazy load yang tidak di-whitelist.

Solusi: Eager Loading

// ✅ Eager Loading — hanya 2 queries
$users = User::with('posts')->get();
// Query 1: SELECT * FROM users
// Query 2: SELECT * FROM posts WHERE user_id IN (1, 2, 3, ...)

foreach ($users as $user) {
    echo $user->posts->count();
    // Data sudah ada di memory, tidak ada query tambahan
}

Teknik Eager Loading

// 1. with() — eager load saat query
$users = User::with('posts', 'profile')->get();

// 2. Nested eager loading
$users = User::with('posts.comments')->get();

// 3. Eager load dengan constraint
$users = User::with(['posts' => function ($query) {
    $query->where('published', true)->orderBy('created_at', 'desc');
}])->get();

// 4. withCount() — hanya hitung, tidak load semua data
$users = User::withCount('posts')->get();
// $user->posts_count tersedia tanpa load semua posts

Cara Mendeteksi N+1

// Di AppServiceProvider
public function boot(): void
{
    Model::preventLazyLoading(! $this->app->isProduction());
}

Yang akan kamu pelajari