Tutorial Laravel #21: Relasi Eloquent: One to One, One to Many, Many to Many
1. Relasi Database di Eloquent
Relasi memungkinkan model terhubung satu sama lain, mencerminkan hubungan antar tabel di database. Eloquent menyediakan cara yang sangat ekspresif untuk mendefinisikan dan menggunakan relasi ini.
2. One to One
Satu record di tabel A hanya punya satu record di tabel B. Contoh: User memiliki satu Profile.
// Migration: tabel profiles
$table->foreignId('user_id')->unique()->constrained()->onDelete('cascade');
// Model User
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
// Model Profile
class Profile extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
// Penggunaan
$user = User::find(1);
$profile = $user->profile; // akses profile
$bio = $user->profile->bio; // akses field profile
$profile = Profile::find(1);
$nama = $profile->user->name; // akses user dari profile
3. One to Many
Satu record di tabel A punya banyak record di tabel B. Contoh: User memiliki banyak Artikel.
// Model User
class User extends Model
{
public function artikels()
{
return $this->hasMany(Artikel::class);
}
}
// Model Artikel
class Artikel extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
// Penggunaan
$user = User::find(1);
$artikels = $user->artikels; // semua artikel user ini
$terbaru = $user->artikels()->latest()->get(); // dengan query tambahan
$artikel = Artikel::find(1);
$penulis = $artikel->user->name; // nama penulis artikel
4. Many to Many
Record di tabel A bisa punya banyak record di tabel B dan sebaliknya. Butuh tabel pivot. Contoh: Artikel bisa punya banyak Tag, dan Tag bisa ada di banyak Artikel.
// Migration: tabel pivot artikel_tag
Schema::create('artikel_tag', function (Blueprint $table) {
$table->foreignId('artikel_id')->constrained()->onDelete('cascade');
$table->foreignId('tag_id')->constrained()->onDelete('cascade');
$table->primary(['artikel_id', 'tag_id']);
});
// Model Artikel
class Artikel extends Model
{
public function tags()
{
return $this->belongsToMany(Tag::class);
}
}
// Model Tag
class Tag extends Model
{
public function artikels()
{
return $this->belongsToMany(Artikel::class);
}
}
// Penggunaan
$artikel = Artikel::find(1);
$tags = $artikel->tags; // semua tag
// Attach, detach, sync
$artikel->tags()->attach([1, 2, 3]); // tambah tag
$artikel->tags()->detach(2); // hapus satu tag
$artikel->tags()->sync([1, 3, 5]); // ganti semua tag
5. Eager Loading - Hindari N+1 Problem
// N+1 Problem - JANGAN lakukan ini di loop!
$artikels = Artikel::all();
foreach ($artikels as $artikel) {
echo $artikel->user->name; // query baru setiap iterasi!
}
// Solusi: Eager loading dengan with()
$artikels = Artikel::with('user')->get(); // 2 query total
$artikels = Artikel::with(['user', 'tags'])->get(); // 3 query total
$artikels = Artikel::with('user.profile')->get(); // nested relation
foreach ($artikels as $artikel) {
echo $artikel->user->name; // tidak ada query tambahan
}
6. Withcount - Hitung Relasi
// Tambahkan hitungan relasi tanpa load semua datanya
$artikels = Artikel::withCount('tags')->get();
foreach ($artikels as $artikel) {
echo $artikel->tags_count; // jumlah tag
}
// Bisa dikombinasikan dengan kondisi
$users = User::withCount([
'artikels',
'artikels as published_count' => fn($q) => $q->where('status', 'published')
])->get();
7. Ringkasan
- hasOne/belongsTo - relasi satu ke satu
- hasMany/belongsTo - relasi satu ke banyak
- belongsToMany - relasi banyak ke banyak (butuh tabel pivot)
- Selalu gunakan
with()(eager loading) saat akses relasi di dalam loop untuk hindari N+1 problem
Tutorial berikutnya membahas resource controller di Laravel.
.jpg)
