Laravel Eloquent Relationship Eager Loading Query Pattern
Selesai membaca dokumentasi ini?
Kembali ke rujukan utama Laravel untuk melanjutkan topik lainnya.
Kembali ke rujukan utama Laravel untuk melanjutkan topik lainnya.
Eloquent relationship adalah cara Laravel memodelkan hubungan antar tabel database melalui model. Dengan relasi ini, model bisa saling mengambil data tanpa harus menulis join manual di setiap tempat.
Eager loading adalah teknik untuk memuat relasi sejak awal agar Laravel tidak menjalankan query tambahan berulang-ulang saat data relasi diakses. Tujuannya utama adalah mencegah masalah N+1 query.
Query pattern adalah pola menyusun query Eloquent supaya tetap jelas, efisien, dan mudah dirawat. Di praktiknya, ini berarti tahu kapan memakai with, kapan memakai load, kapan membatasi kolom relasi, dan kapan memindahkan logika query ke tempat yang lebih tepat.
Kalau relasi Eloquent dipakai tanpa strategi yang jelas, aplikasi biasanya terlihat normal di data kecil, lalu mulai lambat saat jumlah record naik. Penyebab paling umum adalah N+1 query: satu query untuk data utama, lalu satu query tambahan untuk setiap baris ketika relasi diakses.
Masalah ini sering tidak terlihat saat development karena data masih sedikit. Begitu dataset membesar, query membengkak, response time naik, dan database ikut terbebani.
Laravel menyediakan beberapa tipe relasi utama.
hasOneDipakai saat satu model memiliki satu model terkait.
Contoh: User punya satu Profile.
hasManyDipakai saat satu model memiliki banyak model terkait.
Contoh: User punya banyak Post.
belongsToDipakai di sisi yang memiliki foreign key.
Contoh: Post milik satu User.
belongsToManyDipakai untuk relasi many-to-many melalui tabel pivot.
Contoh: User punya banyak Role, dan Role dimiliki banyak User.
Laravel juga punya relasi seperti morphOne, morphMany, dan morphTo untuk kasus polymorphic. Untuk dokumentasi dasar, empat relasi di atas biasanya sudah jadi fondasi utama.
// app/Models/User.php
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
public function posts()
{
return $this->hasMany(Post::class);
}
}
// app/Models/Post.php
Diagram ini menunjukkan arah hubungan, bukan detail implementasi SQL. Saat membaca Eloquent, arah relasi penting karena menentukan model mana yang mendefinisikan method relasi dan model mana yang menyimpan foreign key.
Tanpa eager loading, pola yang sering terjadi seperti ini:
posts$post->userKalau ada 100 post, bisa muncul 101 query: satu query untuk post, lalu 100 query untuk user.
Dengan eager loading, Laravel mengambil data utama dan relasi dalam query terpisah yang lebih sedikit, lalu menghubungkannya di memory.
Contoh:
$posts = Post::with('user')->get();
foreach ($posts as $post) {
echo $post->user->name;
}Pada pola ini, Laravel umumnya menjalankan:
postsusers yang dibutuhkanJadi jumlah query tidak bertambah seiring jumlah item di loop.
Alur ini menggambarkan perbedaan utama eager loading: data relasi diambil sebelum loop, bukan saat loop berjalan.
N+1 query adalah pola ketika aplikasi menjalankan 1 query untuk data utama, lalu N query tambahan untuk relasi di dalam loop.
Contoh yang rawan:
$posts = Post::all();
foreach ($posts as $post) {
echo $post->user->name;
}Kalau user belum dimuat, setiap akses $post->user memicu query baru.
with() saat mengambil datawithGunakan with() saat relasi sudah diketahui sejak query awal.
Contoh:
$posts = Post::with('user')->latest()->paginate(10);with() cocok untuk:
Kalau relasi bertingkat, with() juga bisa dipakai secara nested.
$posts = Post::with(['user', 'comments.user'])->get();Ini berguna saat sebuah relasi punya relasi lagi, misalnya komentar beserta penulis komentar.
Kalau data yang dibutuhkan sedikit, batasi kolom yang diambil.
$posts = Post::with(['user:id,name'])->get();Pola ini sehat karena mengurangi payload yang tidak perlu. Yang penting, kolom key seperti id tetap disertakan agar relasi bisa dipetakan dengan benar.
loadGunakan load() saat model atau collection sudah terlanjur diambil, lalu kamu ingin memuat relasi setelahnya.
Contoh:
$post = Post::findOrFail($id);
$post->load('user', 'comments.user');load() cocok untuk:
withwith() dipasang saat query belum dieksekusiload() dipakai setelah model atau collection sudah didapatSecara praktis:
with()load()loadMissingloadMissing() berguna kalau kamu hanya ingin memuat relasi yang belum ada.
$post->loadMissing('user');Ini cocok saat layer sebelumnya mungkin sudah memuat sebagian relasi, dan kamu ingin menghindari query ulang yang tidak perlu.
$posts = Post::select('id', 'title', 'user_id')
->with('user:id,name')
->latest()
->paginate(10);Pola ini menjaga query tetap ramping. Jangan ambil kolom yang tidak dipakai.
Kalau query makin kompleks, jangan tumpuk semuanya di controller.
Lebih sehat kalau:
// kurang sehat
$users = User::all();
foreach ($users as $user) {
echo $user->posts->count();
}Kalau perlu jumlah post, lebih baik eager load atau pakai agregasi.
$users = User::withCount('posts')->get();Kalau yang dibutuhkan hanya jumlah, jangan ambil seluruh koleksi relasi.
$users = User::withCount('posts')->get();Kalau yang dibutuhkan cuma total, ini lebih efisien daripada memuat semua posts lalu menghitung di PHP.
Eager loading memang membantu, tapi bukan berarti semua relasi harus selalu dimuat.
Tambahkan relasi hanya jika memang dipakai. Terlalu banyak eager loading juga bisa membuat query besar dan payload membengkak.
$users = User::all();
foreach ($users as $user) {
echo $user->posts->count();
}$users = User::with('posts')->get();
foreach ($users as $user) {
echo $user->posts->count();
}$users = User::withCount('posts')->get();
foreach ($users as $user) {
echo $user->posts_count;
}Urutan pilihan yang sehat biasanya begini:
withCount kalau cuma butuh hitunganwith kalau perlu data relasiwith() saat query awal sudah tahu relasi yang dibutuhkan.load() kalau model atau collection sudah ada di memory.loadMissing() untuk menghindari query ulang yang tidak perlu.withCount, withSum, atau agregasi lain kalau yang dibutuhkan hanya nilai ringkas.with(): memuat relasi saat query dibangun.load(): memuat relasi setelah model atau collection sudah didapat.loadMissing(): memuat hanya relasi yang belum ada.