KelasIT.com
Database

Mengenal Eloquent Relationship

Panduan lengkap relasi Eloquent: one-to-one, one-to-many, many-to-many dengan tabel pivot, has-many-through, polymorphic, cara mendefinisikan dan memakainya, eager loading, serta menyimpan data berelasi.

Mengenal Eloquent Relationship

Data dalam aplikasi nyata saling berhubungan: pengguna punya banyak artikel, artikel punya banyak komentar, artikel punya banyak tag dan tag dipakai banyak artikel. Eloquent (ORM Laravel) mengubah hubungan antar tabel ini menjadi method di model, sehingga kamu menulis $user->posts alih-alih query JOIN manual.

Pastikan kamu memahami primary key dan foreign key. Baca Memahami Primary Key dan Foreign Key.

Ringkasan jenis relasi

RelasiContohMethod di model
Satu-ke-satuUser ↔ ProfilhasOne / belongsTo
Satu-ke-banyakUser → banyak PosthasMany / belongsTo
Banyak-ke-banyakPost ↔ TagbelongsToMany
Has-many-throughNegara → Post (lewat User)hasManyThrough
PolymorphicKomentar untuk Post atau VideomorphMany / morphTo

1. Satu-ke-banyak (paling umum)

Foreign key berada di tabel "banyak".

// migration posts
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
class User extends Model
{
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

class Post extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

Pemakaian:

$user->posts;                         // koleksi semua post milik user
$user->posts()->where('published', true)->get();   // dengan kondisi (perhatikan tanda kurung)
$post->user->name;                    // pemilik post

Perbedaan penting: $user->posts mengembalikan hasil (koleksi), sedangkan $user->posts() mengembalikan query builder yang masih bisa dirangkai.

Laravel menebak nama foreign key dari nama model (user_id). Jika berbeda: hasMany(Post::class, 'penulis_id').

2. Satu-ke-satu

class User extends Model
{
    public function profile()
    {
        return $this->hasOne(Profile::class);
    }
}

class Profile extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

Tambahkan ->unique() pada kolom user_id di tabel profiles agar database menjamin satu profil per pengguna. Gunakan withDefault() supaya $user->profile->bio tidak error saat profil belum ada:

return $this->hasOne(Profile::class)->withDefault(['bio' => '-']);

3. Banyak-ke-banyak

Butuh tabel pivot berisi dua foreign key. Konvensi nama: gabungan dua nama tabel tunggal berurutan alfabet, dipisah underscore: post_tag.

Schema::create('post_tag', function (Blueprint $table) {
    $table->foreignId('post_id')->constrained()->cascadeOnDelete();
    $table->foreignId('tag_id')->constrained()->cascadeOnDelete();
    $table->primary(['post_id', 'tag_id']);
});
class Post extends Model
{
    public function tags()
    {
        return $this->belongsToMany(Tag::class)->withTimestamps();
    }
}

class Tag extends Model
{
    public function posts()
    {
        return $this->belongsToMany(Post::class);
    }
}

Mengelola relasi:

$post->tags()->attach([1, 2]);          // tambah
$post->tags()->detach(2);               // lepas
$post->tags()->sync([1, 3, 4]);         // jadikan persis ini (yang lain dilepas)
$post->tags()->toggle([1, 5]);          // balik status
$post->tags()->syncWithoutDetaching([6]);

Pivot dengan kolom tambahan (misalnya peran atau urutan):

return $this->belongsToMany(Tag::class)->withPivot('urutan')->withTimestamps();
// akses: $tag->pivot->urutan

4. Has-many-through

Mengakses relasi "jauh" lewat model perantara. Contoh: Negara → User → Post.

class Negara extends Model
{
    public function posts()
    {
        return $this->hasManyThrough(Post::class, User::class);
    }
}

5. Polymorphic

Satu tabel bisa berelasi dengan beberapa model berbeda. Contoh: tabel comments untuk Post dan Video.

$table->morphs('commentable');   // kolom commentable_id dan commentable_type
class Comment extends Model
{
    public function commentable()
    {
        return $this->morphTo();
    }
}

class Post extends Model
{
    public function comments()
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}

Hemat tabel, tetapi database tidak bisa menerapkan foreign key pada kolom polimorfik, jadi integritas dijaga aplikasi.

Menyimpan data berelasi

// Dari sisi induk: foreign key terisi otomatis
$user->posts()->create(['title' => 'Halo', 'body' => '...']);

// Mengaitkan anak ke induk
$post->user()->associate($user)->save();
$post->user()->dissociate()->save();

// Membuat beberapa sekaligus
$user->posts()->createMany([...]);

Bungkus beberapa operasi berkaitan dalam DB::transaction() agar konsisten.

Query berdasarkan relasi

// Pengguna yang punya minimal satu post
User::has('posts')->get();

// Dengan syarat pada relasi
User::whereHas('posts', fn ($q) => $q->where('published', true))->get();

// Pengguna tanpa post
User::doesntHave('posts')->get();

// Menghitung relasi
User::withCount('posts')->orderByDesc('posts_count')->get();

Eager loading: wajib dipahami

Mengakses relasi di dalam perulangan tanpa eager loading menyebabkan N+1 query:

// Buruk: 1 + N query
foreach (Post::all() as $post) { echo $post->user->name; }

// Baik: 2 query
foreach (Post::with('user')->get() as $post) { echo $post->user->name; }

Bisa juga diatur otomatis lewat protected $with = ['user']; di model (hati-hati, ini selalu dimuat). Pembahasan mendalam ada di Tips Optimasi Query Laravel.

Aksi hapus dan integritas

  • Atur di database: cascadeOnDelete(), restrictOnDelete(), atau nullOnDelete() pada foreign key.
  • Atau di model, hapus relasi saat induk dihapus melalui event deleting.
  • Untuk data penting gunakan soft delete (SoftDeletes) agar relasinya tetap dapat dipulihkan.

Kesalahan umum

GejalaPenyebab
Call to a member function ... on nullRelasi belongsTo belum ada (nilai null); gunakan ?-> atau withDefault()
Column not found: user_idNama foreign key tidak mengikuti konvensi; sebutkan eksplisit
Relasi banyak-ke-banyak kosongNama tabel pivot tidak sesuai konvensi atau pivot kosong
Halaman lambatN+1: lupa with()
Memanggil $user->posts->where(...) lalu heran lambatItu memfilter koleksi di memori; pakai $user->posts()->where(...) agar difilter di database

Latihan

Rancang sistem blog: User, Post, Category, Tag, Comment.

  1. Tentukan relasi tiap pasangan dan tabel apa yang butuh pivot.
  2. Tulis migration dengan foreign key dan aksi hapus yang tepat.
  3. Buat halaman daftar post yang menampilkan penulis, kategori, jumlah komentar, dan tag dengan hanya sedikit query.

Kesimpulan

Relasi Eloquent mengubah kolom foreign key menjadi navigasi objek yang intuitif. Kuasai lima jenis relasi, kelola pivot dengan sync/attach, selalu gunakan eager loading untuk daftar data, dan biarkan database menjaga integritas lewat foreign key.

A
Andi

Menulis tentang pemrograman, database, AI, dan karier di dunia IT supaya belajar terasa lebih mudah dan menyenangkan.

Komentar (0)

Komentar hanya untuk member. Masuk atau daftar gratis untuk ikut berdiskusi.

Mau belajar lebih dalam?

Course online dengan latihan coding interaktif dan sertifikat.

Lihat Course →

Gabung komunitas

Tanya jawab, info event, dan teman belajar di grup WhatsApp.

Gabung Grup