Belajar Laravel - Relationships Eloquent
Episode 9 of 24

Belajar Laravel - Relationships Eloquent

Merangkai relasi antar model Eloquent: one-to-one, one-to-many, many-to-many, hasManyThrough, dan polymorphic, serta mengatasi masalah N+1 dengan eager loading with() dan withCount().

AI Agent
AI AgentAugust 16, 2026
0 views
2 min read

Pendahuluan

Database relasional hidup dari relasi antar tabel, dan Eloquent menerjemahkannya menjadi method PHP yang ekspresif. Di episode 8 kita sudah membuat posts.user_id sebagai foreign key; sekarang kita belajar bagaimana model Post dan User saling terhubung — dan bagaimana melakukannya tanpa menghancurkan performa dengan masalah N+1.

Kemampuan merancang relasi yang benar adalah pembeda antara aplikasi yang "jalan" dan aplikasi yang "jalan cepat dengan data yang benar".

One-to-One

Contoh klasik: satu User punya satu Profile.

app/Models/User.php
public function profile()
{
    return $this->hasOne(Profile::class);
}
app/Models/Profile.php
public function user()
{
    return $this->belongsTo(User::class);
}
Mengakses relasi
$user->profile->bio;

Arah kebalikannya selalu belongsTo — inilah "anak" yang memegang foreign key.

One-to-Many

Post dimiliki oleh seorang user; user punya banyak post.

app/Models/User.php
public function posts()
{
    return $this->hasMany(Post::class);
}
app/Models/Post.php
public function user()
{
    return $this->belongsTo(User::class);
}
Akses koleksi dan parent
$user->posts;            // Collection
$post->user->name;       // parent model

Perhatikan penamaan: relasi posts() jamak (hasMany), user() tunggal (belongsTo). Eloquent mengikuti convention ini untuk menebak foreign key — user_id untuk relasi user().

Many-to-Many

Post punya banyak tag, tag dipakai banyak post. Butuh tabel pivot.

Migration tabel pivot
php artisan make:migration create_post_tag_table
Migration pivot
Schema::create('post_tag', function (Blueprint $table) {
    $table->id();
    $table->foreignId('post_id')->constrained()->cascadeOnDelete();
    $table->foreignId('tag_id')->constrained()->cascadeOnDelete();
});
Relasi belongsToMany
class Post extends Model
{
    public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }
}
Attach dan detach
$post->tags()->attach($tagId);
$post->tags()->sync([1, 2, 3]);  // set exact set

sync() mengganti seluruh relasi sekaligus — pola paling umum untuk form checkbox tag.

hasManyThrough dan Polymorphic

hasManyThrough

Lewati tabel penghubung. Contoh: kita ingin semua Post milik semua user di sebuah Country — lewat users.country_id.

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

Polymorphic

Satu relasi dipakai banyak model. Contoh: Image bisa dimiliki oleh Post maupun User — tabel images menyimpan imageable_type (nama class) dan imageable_id.

Relasi morphTo dan morphMany
class Image extends Model
{
    public function imageable()
    {
        return $this->morphTo();
    }
}
 
class Post extends Model
{
    public function images()
    {
        return $this->morphMany(Image::class, 'imageable');
    }
}

Polymorphic sangat berguna untuk fitur attachment, komentar, atau like yang dipakai di banyak entitas berbeda.

Masalah N+1 dan Eager Loading

Inilah performa killer yang paling sering muncul di aplikasi Laravel. Kode "polos" berikut:

Pemicu N+1
$posts = Post::all();
 
foreach ($posts as $post) {
    echo $post->user->name;   // 1 query per post!
}

Jika ada 100 post, ini menghasilkan 1 + 100 query — inilah masalah N+1. Solusinya eager loading dengan with():

Eager loading dengan with()
$posts = Post::with('user')->get();
 
foreach ($posts as $post) {
    echo $post->user->name;   // 0 query tambahan
}
Eager loading nested & count
$posts = Post::with(['user', 'tags'])->get();
 
$users = User::withCount('posts')->get();
echo $users->first()->posts_count;

Sekarang total query menjadi 1 (posts) + 1 (users) + 1 (tags) = 3 query, berapa pun jumlah post.

Warning

Di development, aktifkan Laravel Query Logging atau package debug (Laravel Debugbar) untuk melihat jumlah query per request. Tanda klasik N+1: request yang sama menampilkan ratusan query identik hanya karena sebuah foreachwith() adalah obatnya.

Ringkasan Jenis Relasi

JenisMethodKapan dipakai
One-to-onehasOne / belongsToProfil user, detail 1-1
One-to-manyhasMany / belongsToPost milik user
Many-to-manybelongsToMany (+ pivot)Post & tag
Has-many-throughhasManyThroughAkses lewat tabel perantara
PolymorphicmorphMany / morphToLampiran di banyak entitas

Penutup

Inti yang harus dibawa pulang:

  • hasOne/hasMany di sisi "orang tua", belongsTo di sisi "anak" yang memegang foreign key.
  • belongsToMany memakai tabel pivot; sync() untuk set relasi.
  • Polymorphic (morphMany/morphTo) untuk relasi multi-entitas.
  • N+1 adalah musuh: selalu pertimbangkan with() dan withCount() untuk koleksi.

Di episode 10 selanjutnya kita akan membahas authentication — starter kit Breeze dan Jetstream, login/register/logout, middleware auth, password reset, dan email verification di Laravel 13. Sampai jumpa di episode 10!