Eloquent One to Many Laravel: Contoh Relasi yang Nyata
Relasi database adalah bagian yang paling sering bikin pemula Laravel nyerah setengah jalan. Padahal konsepnya sederhana: artikel ini pakai satu contoh nyata yang kamu pasti kenal — satu penulis punya banyak artikel — dan kita bangun sampai bisa jalan.
Skenario Nyatanya
Bayangkan aplikasi blog. Ada dua tabel:
users— penulisposts— artikel
Satu user bisa menulis banyak post. Satu post hanya dimiliki satu user. Ini pola klasik one to many, dan pola yang sama berlaku untuk: satu kategori punya banyak produk, satu order punya banyak item, satu sekolah punya banyak siswa.
Langkah 1: Siapkan Migrasi
Post butuh kolom user_id sebagai foreign key yang menunjuk ke users.id:
// database/migrations/xxxx_create_posts_table.php
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('title');
$table->text('body');
$table->timestamps();
});
constrained() otomatis membuat foreign key ke tabel users. cascadeOnDelete() artinya kalau user dihapus, semua post-nya ikut terhapus — masuk akal untuk contoh ini, tapi pikir dua kali kalau datanya penting.
Jalankan:
php artisan migrate
Langkah 2: Definisi Relasi di Model
Di sisi "banyak" (User), pakai hasMany:
// app/Models/User.php
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
Di sisi "satu" (Post), pakai belongsTo:
// app/Models/Post.php
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
Sudah. Itu seluruh definisi relasinya. Laravel menebak nama foreign key (user_id) dan tabel dari konvensi penamaan — selama kamu mengikuti konvensi, tidak perlu konfigurasi tambahan.
Langkah 3: Pakai Relasinya
Mengambil semua post milik seorang user:
$user = User::find(1);
foreach ($user->posts as $post) {
echo $post->title;
}
Mengambil pemilik sebuah post:
$post = Post::find(5);
echo $post->user->name;
Menyimpan post baru yang otomatis terhubung ke user:
$user->posts()->create([
'title' => 'Belajar Eloquent',
'body' => 'Isi artikelnya...',
]);
Perhatikan bedanya: $user->posts (tanpa kurung) mengembalikan collection hasil, sedangkan $user->posts() (dengan kurung) mengembalikan query builder relasi yang bisa dilanjut dengan create(), where(), atau count().
Filtering Lewat Relasi
Mau post milik user yang judulnya mengandung kata tertentu?
$posts = $user->posts()->where('title', 'like', '%Laravel%')->get();
Atau cari user yang punya minimal satu post:
$users = User::has('posts')->get();
Langkah 4: Hindari N+1 Query dengan Eager Loading
Ini jebakan terbesar pemula. Kode ini terlihat normal:
// ❌ JANGAN: memicu N+1 query
$users = User::all();
foreach ($users as $user) {
echo $user->name . ': ' . $user->posts->count() . ' artikel';
}
Kalau ada 100 user, Laravel menjalankan 1 query untuk user + 100 query untuk post masing-masing user = 101 query. Solusinya, eager loading:
// ✅ BENAR: hanya 2 query
$users = User::with('posts')->get();
foreach ($users as $user) {
echo $user->name . ': ' . $user->posts->count() . ' artikel';
}
with('posts') membuat Laravel mengambil semua post terkait dalam satu query tambahan, lalu memasangkannya ke user masing-masing di memori.
Bahkan ada cara lebih hemat kalau cuma butuh hitungan:
$users = User::withCount('posts')->get();
foreach ($users as $user) {
echo $user->posts_count; // kolom virtual, tanpa query tambahan
}
Kesalahan Umum Pemula
1. Nama Method Relasi Salah Kaprah
Laravel menebak foreign key dari nama method. Method user() menebak user_id. Kalau method-nya dinamai author() tapi kolomnya tetap user_id, relasinya gagal diam-diam. Solusinya: samakan nama, atau sebutkan eksplisit:
public function author(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
2. Lupa Eager Loading di Halaman Daftar
Halaman index yang menampilkan 20 post beserta nama penulisnya tanpa with('user') = 21 query. Di local terasa cepat, di production dengan traffic nyata jadi lambat. Biasakan: setiap kali loop mengakses relasi, pasang with().
3. Mengisi Foreign Key Manual
Pemula sering menulis:
// ❌ Bisa jalan, tapi rawan salah
Post::create(['user_id' => $user->id, 'title' => '...']);
Lebih aman dan lebih jelas lewat relasi: $user->posts()->create([...]). Foreign key terisi otomatis dan tidak mungkin salah menunjuk user lain.
4. Foreign Key Tanpa Index
foreignId()->constrained() sudah membuat index otomatis. Tapi kalau kamu bikin kolom foreign key manual dengan $table->unsignedBigInteger('user_id') tanpa ->index() atau ->constrained(), query join-nya akan lambat saat data membesar. Selalu pakai foreignId()->constrained() kecuali ada alasan kuat.
Nested Eager Loading: Relasi Bertingkat
Relasi bisa dirantai. Misal setiap post punya banyak komentar (Post hasMany Comment), dan kamu mau menampilkan user beserta post dan komentarnya sekaligus:
$users = User::with('posts.comments')->get();
Satu panggilan with('posts.comments') mengambil tiga tabel sekaligus — user, post, dan komentar — tanpa N+1 di level mana pun. Aturan praktisnya: setiap titik di rantai properti relasi di dalam loop harus sudah di-with().
Satu catatan penting soal mass assignment: create() lewat relasi tetap tunduk pada $fillable di model. Jadi pastikan model Post mengizinkan kolom yang kamu isi:
// app/Models/Post.php
protected $fillable = ['title', 'body'];
Tidak perlu memasukkan user_id ke $fillable — Laravel mengisinya sendiri lewat relasi, dan justru lebih aman begitu karena user tidak bisa memalsukan pemilik post lewat input form.
Coba Sekarang (5 Menit)
- Buat migrasi post dengan
foreignId('user_id')->constrained(), jalankanphp artisan migrate. - Tambahkan method
posts()di modelUserdanuser()di modelPost. - Di
php artisan tinker, buat satu user dan tiga post lewat$user->posts()->create([...]). - Ambil user dengan
User::with('posts')->find(1)dan cek$user->posts->count().
Kalau empat langkah itu jalan tanpa error, kamu sudah paham 80% relasi Eloquent yang dipakai di project nyata. Sisanya — many to many, polymorphic — tinggal pengembangan pola yang sama.
Kapan Tidak Pakai Relasi Eloquent?
Jujur saja: untuk laporan agregat berat (misal rekap penjualan per bulan dari jutaan baris), query builder mentah atau SQL view sering lebih cepat dan lebih jelas daripada memaksa relasi Eloquent. Eloquent unggul untuk CRUD dan logika domain; untuk reporting skala besar, jangan ragu pakai DB::table().
Pertanyaan Umum
Apa itu relasi one to many di Laravel Eloquent?
Relasi di mana satu baris di tabel A bisa terhubung ke banyak baris di tabel B, seperti satu penulis punya banyak artikel. Di Eloquent, sisi "banyak" pakai hasMany() dan sisi "satu" pakai belongsTo().
Kapan pakai hasMany dan kapan pakai belongsTo?
Pakai hasMany() di model yang "memiliki" banyak item (misal User punya banyak Post), dan belongsTo() di model "anak" yang menunjuk ke pemiliknya (Post dimiliki satu User).
Kenapa query Eloquent saya lambat saat menampilkan relasi?
Biasanya karena N+1 query problem: setiap baris relasi memicu query baru. Atasi dengan eager loading memakai with(), misalnya User::with('posts')->get().
Bagaimana cara menyimpan data anak yang otomatis terhubung ke induk?
Gunakan method relasinya, misalnya $user->posts()->create([...]). Laravel otomatis mengisi foreign key, jadi tidak perlu menulis user_id secara manual.