• Home
  • About
  • Terms of Service
  • Daftar Isi
  • Kebijakan Privasi
  • Hubungi Kami

Belajar Teknologi

Belajar dan Sharing Ilmu Teknologi

Tutorial Laravel 12: Relasi Database Eloquent One to Many

06/11/2026 by ramzilhuda

Aplikasi nyata hampir tidak pernah menyimpan data dalam satu tabel saja. Sebuah blog punya banyak post, dan setiap post punya banyak komentar. Kemampuan menghubungkan tabel-tabel inilah yang disebut relasi database, dan di Laravel relasi ini dikelola dengan elegan oleh Eloquent ORM. Tutorial relasi Eloquent Laravel one to many ini akan membawamu dari konsep dasar sampai praktik lengkap: mendefinisikan hasMany dan belongsTo, membuat migrasi dengan foreign key, sampai mengatasi masalah performa N+1 dengan eager loading.

Studi kasus kita sederhana dan dekat dengan dunia nyata: satu Post bisa memiliki banyak Comment, sedangkan setiap Comment hanya milik satu Post. Pola inilah yang disebut one to many, relasi paling umum yang akan kamu temui di hampir setiap aplikasi. Setelah menguasainya, relasi lain seperti many to many akan terasa jauh lebih mudah dipahami.

Diagram relasi one to many antara Post dan Comment

Relasi Eloquent Laravel One to Many: Konsep Dasarnya

Sebelum menyentuh kode, pahami dulu anatomi relasi one to many. Di sisi database, tabel comments menyimpan kolom post_id yang menunjuk ke kolom id di tabel posts. Kolom post_id inilah yang disebut foreign key, yaitu kunci tamu yang menjadi jembatan antar tabel.

Di sisi Eloquent, relasi ini didefinisikan dari dua arah yang saling melengkapi. Dari sisi Post, relasinya dibaca sebagai “satu post memiliki banyak comment” dan ditulis dengan method hasMany. Dari sisi Comment, relasinya dibaca sebagai “satu comment milik satu post” dan ditulis dengan method belongsTo. Kedua method ini ditaruh di dalam class model masing-masing.

Konvensi penamaan Laravel sangat membantumu di sini. Bila kamu mengikuti konvensi (model Post untuk tabel posts, foreign key post_id), Eloquent bisa menebak semuanya secara otomatis tanpa konfigurasi tambahan. Kamu hanya perlu menulis method relasinya, dan sisanya ditangani framework.

Persiapan: Model dan Migrasi Post serta Comment

Kita mulai dari migrasi. Buat model beserta migrasinya dengan perintah Artisan berikut. Flag -m membuat file migrasi sekalian.

php artisan make:model Post -m
php artisan make:model Comment -m

Buka file migrasi posts dan isi dengan kolom yang dibutuhkan. Karena ini studi kasus blog sederhana, kolomnya cukup judul, isi, dan timestamps bawaan.

// database/migrations/xxxx_create_posts_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('judul');
            $table->text('isi');
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

Selanjutnya migrasi comments. Inilah bagian pentingnya: tambahkan foreignId('post_id') yang dirantai dengan constrained() agar Laravel otomatis membuat foreign key ke tabel posts, plus onDelete('cascade') supaya komentar ikut terhapus saat post induknya dihapus.

// database/migrations/xxxx_create_comments_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
    public function up(): void
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->id();
            $table->foreignId('post_id')
                  ->constrained()
                  ->onDelete('cascade');
            $table->string('nama_komentator');
            $table->text('isi_komentar');
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('comments');
    }
};

Method constrained() akan menebak tabel tujuan dari nama kolom: post_id berarti tabel posts kolom id. Bila penamaanmu tidak mengikuti konvensi, kamu bisa menyebutkannya eksplisit seperti constrained('posts', 'id'). Setelah kedua migrasi siap, jalankan php artisan migrate untuk membuat tabelnya di database. Pastikan konfigurasi database di file .env sudah benar, seperti yang dibahas di Belajar MySQL untuk Pemula.

Relasi Eloquent Laravel One to Many: hasMany dan belongsTo di Model

Sekarang buka model Post dan tambahkan method relasi comments. Nama method memakai bentuk jamak karena satu post punya banyak comment, dan return type-nya adalah HasMany.

// app/Models/Post.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Post extends Model
{
    protected $fillable = ['judul', 'isi'];

    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class);
    }
}

Lalu buka model Comment dan tambahkan method post dengan return type BelongsTo. Nama method memakai bentuk tunggal karena satu comment hanya milik satu post.

// app/Models/Comment.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Comment extends Model
{
    protected $fillable = ['post_id', 'nama_komentator', 'isi_komentar'];

    public function post(): BelongsTo
    {
        return $this->belongsTo(Post::class);
    }
}

Perhatikan bahwa $fillable pada Comment menyertakan post_id agar bisa diisi massal saat membuat komentar baru. Dengan dua method ini, Eloquent sekarang paham hubungan kedua tabel. Kamu bisa mengakses $post->comments untuk mendapat koleksi komentar, atau $comment->post untuk mendapat post induknya, tanpa menulis satu pun query SQL manual.

Praktik: Menyimpan dan Membaca Data Berelasi

Relasi yang sudah didefinisikan membuat kode penyimpanan data menjadi sangat ekspresif. Untuk membuat comment yang otomatis terhubung ke post tertentu, cukup panggil relasinya lalu method create.

use App\Models\Post;

$post = Post::find(1);

// Membuat comment yang otomatis terisi post_id = 1
$post->comments()->create([
    'nama_komentator' => 'Ani',
    'isi_komentar' => 'Artikelnya sangat membantu, terima kasih!',
]);

Bentuk $post->comments() dengan tanda kurung mengembalikan query builder relasi sehingga kamu bisa merantainya dengan create, where, atau count. Sedangkan $post->comments tanpa tanda kurung langsung mengeksekusi dan mengembalikan koleksi hasil. Perbedaan kecil ini penting dan sering membingungkan pemula, jadi hafalkan polanya.

Untuk membaca, kamu bisa mengakses relasi dari kedua arah dengan natural.

// Dari post ke comments-nya
$post = Post::find(1);
foreach ($post->comments as $comment) {
    echo $comment->nama_komentator . ': ' . $comment->isi_komentar . "\n";
}

// Dari comment ke post induknya
$comment = Comment::find(5);
echo $comment->post->judul;

Tampilkan di Blade juga semudah itu. Contoh menampilkan daftar post beserta komentarnya:

@foreach ($posts as $post)
  <h2>{{ $post->judul }}</h2>
  <p>{{ $post->isi }}</p>

  <h4>Komentar ({{ $post->comments->count() }})</h4>
  <ul>
    @foreach ($post->comments as $comment)
      <li><strong>{{ $comment->nama_komentator }}:</strong> {{ $comment->isi_komentar }}</li>
    @endforeach
  </ul>
@endforeach

Waspadai Masalah N+1 dan Atasi dengan Eager Loading

Ada satu jebakan performa yang wajib kamu pahami sejak awal: masalah N+1 query. Perhatikan kode controller berikut yang terlihat wajar tetapi bermasalah.

// CARA LAMBAT: memicu N+1 query
$posts = Post::all(); // 1 query untuk mengambil semua post

foreach ($posts as $post) {
    echo $post->comments->count(); // +1 query untuk SETIAP post!
}

Bila ada 50 post, kode ini menjalankan 1 query untuk posts ditambah 50 query untuk comments, total 51 query. Inilah N+1: satu query awal plus N query tambahan. Pada data kecil tidak terasa, tetapi di production dengan ribuan baris, halamanmu bisa menjadi sangat lambat.

Solusinya adalah eager loading dengan method with(), yang memuat relasi sekaligus dalam query tambahan yang efisien.

// CARA CEPAT: hanya 2 query untuk semua data
$posts = Post::with('comments')->get(); // 1 query posts + 1 query comments

foreach ($posts as $post) {
    echo $post->comments->count(); // tidak ada query baru, data sudah dimuat
}

Eloquent akan menjalankan satu query untuk semua comments yang post_id-nya cocok, lalu mencocokkannya di memori. Hasilnya selalu hanya 2 query berapa pun jumlah post-nya. Aturan praktisnya: setiap kali kamu mengakses relasi di dalam loop, pastikan relasi itu dimuat dengan with() di awal. Kamu juga bisa memuat relasi bertingkat seperti with('comments.user') bila comment berelasi lagi ke tabel users.

Relasi di Route dan Controller: Contoh Lengkap

Mari rangkai semuanya dalam contoh controller nyata yang menampilkan satu post beserta komentarnya, plus form tambah komentar. Pertama daftarkan route-nya.

// routes/web.php
use App\Http\Controllers\PostController;

Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');
Route::post('/posts/{post}/comments', [PostController::class, 'storeComment'])
    ->name('posts.comments.store');

Lalu buat controllernya. Perhatikan penggunaan with('comments') untuk eager loading dan validasi input sebelum menyimpan.

// app/Http/Controllers/PostController.php
namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function show(Post $post)
    {
        // Muat ulang post beserta comments-nya secara eager
        $post->load('comments');

        return view('posts.show', compact('post'));
    }

    public function storeComment(Request $request, Post $post)
    {
        $validated = $request->validate([
            'nama_komentator' => 'required|max:100',
            'isi_komentar' => 'required|max:1000',
        ]);

        $post->comments()->create($validated);

        return redirect()->route('posts.show', $post)
            ->with('sukses', 'Komentar berhasil ditambahkan.');
    }
}

Teknik Route::get('/posts/{post}') yang dipasangkan dengan parameter Post $post disebut route model binding: Laravel otomatis mengambilkan model Post berdasarkan ID di URL. Ini menghemat kode pencarian manual dan sudah mengikuti praktik terbaik Laravel 12.

Kesalahan Umum Saat Membuat Relasi Eloquent

Beberapa error klasik yang sering ditemui pemula beserta penyebabnya. Pertama, error Class not found pada relasi biasanya karena lupa mengimpor class model terkait dengan use App\Models\Comment; di atas file. Kedua, foreign key tidak terbentuk karena urutan migrasi salah: migrasi posts harus berjalan sebelum comments, jadi perhatikan timestamp pada nama file migrasi.

Ketiga, mass assignment error saat create biasanya karena post_id belum masuk ke $fillable pada model Comment. Keempat, relasi mengembalikan null atau koleksi kosong padahal data ada di database. Cek apakah nama method relasi, nama foreign key, dan konvensi tabel semuanya konsisten. Bila memakai nama tabel atau kolom yang tidak standar, sebutkan eksplisit di parameter hasMany dan belongsTo, misalnya $this->hasMany(Comment::class, 'tulisan_id').

Langkah Selanjutnya

Kamu sekarang bisa memodelkan relasi one to many dengan benar: migrasi foreign key lewat foreignId dan constrained(), method hasMany dan belongsTo di model, sampai teknik eager loading untuk menghindari N+1 query. Ini adalah skill database yang dipakai di hampir setiap proyek Laravel profesional.

Relasi ini juga menjadi fondasi untuk topik lanjutan seperti many to many dengan tabel pivot, polymorphic relations, dan API Resources yang menyajikan data berelasi sebagai JSON. Untuk melihat bagaimana data berelasi disajikan ke aplikasi lain, lanjutkan ke Cara Membuat REST API dengan Laravel: Panduan Lengkap untuk Pemula. Dan bila kamu ingin mengulang dasar pengelolaan data tunggal sebelum melangkah lebih jauh, Tutorial Laravel 12: Membuat CRUD Sederhana dari Nol untuk Pemula selalu siap menjadi tempatmu berlatih lagi.

Filed Under: Laravel Tagged With: eloquent, laravel, one to many, relasi database, tutorial laravel

Categories

  • Beasiswa
  • Bedtime Tales
  • Berita Teknologi
  • Berita Terkini
  • Dunia Islam
  • Flutter
  • Laravel
  • Machine Learning
  • Manfaat Buah
  • Mata Kuliah
  • Matematika Diskrit
  • Memori Kolektif
  • Metode Numerik
  • Mobile Computing
  • Panduan
  • Teori Peluang
  • Tutorial
  • Tutorial Pemrograman

Recent Posts

  • Belajar CSS Flexbox dan Grid: Layout Modern dengan Contoh Praktis
  • Tutorial Laravel 12: Relasi Database Eloquent One to Many
  • Cara Install XAMPP dan Menjalankan Program PHP Pertama
  • Tutorial JavaScript untuk Pemula: Variabel, Fungsi, dan Manipulasi DOM
  • Pendaftaran CPNS 2026: Syarat Umum, Dokumen, dan Cara Buat Akun SSCASN

Archives

  • November 2026
  • October 2026
  • April 2026
  • June 2025
  • April 2025
  • January 2025
  • March 2024
  • January 2024
  • December 2023
  • May 2023
  • September 2022
  • July 2022
  • June 2022
  • May 2022
  • April 2022
  • February 2022
  • December 2021
  • November 2021
  • June 2021
  • May 2021
  • March 2021
  • February 2021
  • November 2020
  • April 2020
  • March 2020
  • November 2019
  • October 2019
  • September 2019

Recent Comments

    Copyright © 2026 · Magazine Pro Theme on Genesis Framework · WordPress · Log in