Relasi menghubungkan satu model dengan model lain sehingga data antartabel dapat diakses dari objek tanpa menyusun pernyataan join secara manual. Topik ini membahas empat jenis relasi Eloquent, yaitu hasOne, hasMany, belongsTo, dan belongsToMany, dengan fokus pada sepasang relasi yang sudah terdaftar pada aplikasi catatan, yakni Category yang memiliki banyak Note dan Note yang dimiliki satu Category. Setelah membacanya, relasi dapat dituliskan pada model dan diakses sebagai properti, mulai dari satu catatan sampai sekumpulan catatan milik satu kategori.
Empat jenis relasi dasar#
Eloquent mengelompokkan relasi menurut arah hubungannya, dan setiap jenis dinyatakan sebagai satu metode biasa pada kelas model. Tabel berikut merangkum keempat jenisnya beserta contoh penggunaannya.
| Jenis relasi | Arah hubungan | Contoh |
|---|---|---|
hasOne | satu ke satu | Satu pengguna memiliki satu kartu anggota |
hasMany | satu ke banyak | Satu kategori memiliki banyak catatan |
belongsTo | kebalikan hasOne atau hasMany | Satu catatan dimiliki satu kategori |
belongsToMany | banyak ke banyak | Satu pengguna memiliki banyak peran dan sebaliknya |
Arah relasi mengikuti letak kolom penghubung, yaitu foreign key. Pada aplikasi catatan, kolom category_id berada di tabel notes, sehingga model Note menyatakan belongsTo ke arah Category, sedangkan model Category menyatakan hasMany ke arah Note. Kedua metode itu selalu berpasangan dan berada di kelas yang berbeda, sehingga relasi dapat diakses dari kedua sisi tanpa menulis ulang query-nya.
Relasi one-to-many pada aplikasi catatan#
Metode relasi ditulis sebagai metode biasa dengan tipe kembalian dari ruang nama Illuminate\Database\Eloquent\Relations. Metode notes() pada model Category memanggil hasMany() dengan nama kelas model yang berelasi, dan metodenya diberi nama jamak karena menghasilkan banyak baris.
// tidak-dijalankan
public function notes(): HasMany
{
return $this->hasMany(Note::class);
}Kebalikannya berada pada model Note. Metode category() diberi nama tunggal karena menghasilkan paling banyak satu objek, dan pemanggilan belongsTo() menunjuk ke kelas Category sebagai model induk.
// tidak-dijalankan
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}Eloquent menurunkan nama kolom penghubung secara otomatis dari nama model, yaitu nama kelas induk dalam bentuk snake_case ditambah akhiran _id, sehingga keduanya mengarah ke kolom category_id tanpa parameter tambahan. Apabila kolomnya bernama lain, misalnya id_kategori, nama kolom tersebut dilewatkan sebagai argumen kedua, seperti $this->belongsTo(Category::class, 'id_kategori'). Penamaan metode yang konsisten juga penting karena nama metode itulah yang digunakan sebagai nama properti pada saat akses.
Relasi hasOne dan belongsToMany#
Jenis hasOne digunakan ketika satu baris hanya boleh berhubungan dengan satu baris lain. Contoh berikut bersifat ilustrasi dan berada di luar aplikasi catatan, yaitu seorang pengguna yang memiliki satu kartu anggota. Bentuknya mirip hasMany, tetapi metode yang dipanggil berbeda dan nama metodenya tunggal.
// tidak-dijalankan
public function kartuAnggota(): HasOne
{
return $this->hasOne(KartuAnggota::class);
}Jenis belongsToMany menangani hubungan banyak ke banyak yang tidak dapat diselesaikan dengan satu kolom foreign key. Contoh berikut menghubungkan pengguna dengan peran melalui satu tabel perantara yang disebut pivot table, dan menurut konvensi tabelnya bernama peran_pengguna dengan kolom pengguna_id dan peran_id. Kedua model yang berelasi sama-sama menyatakan belongsToMany satu sama lain.
// tidak-dijalankan
public function peran(): BelongsToMany
{
return $this->belongsToMany(Peran::class);
}Berbeda dengan tiga jenis sebelumnya yang cukup diuji pada aplikasi catatan, relasi banyak ke banyak memerlukan pivot table beserta migration tersendiri, sehingga contohnya tidak diimplementasikan pada proyek contoh jalur ini.
Mengakses relasi sebagai properti#
Metode relasi dipanggil sebagai properti untuk mengambil datanya, misalnya $kategori->notes dan $catatan->category. Contoh berikut menyiapkan dua catatan deterministik pada kategori Hobi, lalu mengakses relasinya dari kedua arah. Baris pembersihan di awal membuat keluaran tetap sama pada eksekusi ulang.
# menyiapkan data relasi lalu mengaksesnya dari kedua arah
cd ~/aplikasi-catatan
php artisan tinker --execute='
use App\Models\Category;
use App\Models\Note;
$hobi = Category::firstOrCreate(["nama" => "Hobi"]);
Note::where("category_id", $hobi->id)->delete();
$foto = Note::create([
"judul" => "Ide jadwal berburu foto",
"isi" => "Menandai waktu luang untuk praktik memotret di sekitar rumah.",
"category_id" => $hobi->id,
]);
$koleksi = Note::create([
"judul" => "Daftar buku bacaan",
"isi" => "Membagi daftar bacaan pribadi menjadi sudah dan belum dibaca.",
"category_id" => $hobi->id,
]);
echo $foto->category->nama . PHP_EOL;
echo $hobi->notes->pluck("judul")->implode(" | ") . PHP_EOL;
echo $hobi->notes->count() . PHP_EOL;
'Hobi
Ide jadwal berburu foto | Daftar buku bacaan
2Baris pertama keluaran memperlihatkan arah belongsTo, yaitu $foto->category mengembalikan satu objek model yang atribut nama-nya langsung terbaca. Dua baris berikutnya memperlihatkan arah hasMany, yaitu $hobi->notes mengembalikan collection yang dapat diproses dengan metode pluck() dan count(). Diagram berikut merangkum kedua arah tersebut beserta kolom penghubungnya.
# membedakan akses relasi sebagai properti dan sebagai metode
cd ~/aplikasi-catatan
php artisan tinker --execute='
use App\Models\Category;
$hobi = Category::firstOrCreate(["nama" => "Hobi"]);
echo get_class($hobi->notes) . PHP_EOL;
echo get_class($hobi->notes()) . PHP_EOL;
'Illuminate\Database\Eloquent\Collection
Illuminate\Database\Eloquent\Relations\HasManyAkses sebagai properti menjalankan query lalu mengembalikan Collection berisi model, sedangkan pemanggilan sebagai metode mengembalikan objek HasMany yang berperilaku seperti query dan masih dapat dirantai dengan where() atau orderBy() sebelum diakhiri get().
Kesalahan umum#
Kesalahan pertama adalah merantai metode query pada properti relasi, misalnya $kategori->notes->where(...). Properti sudah mengembalikan Collection, sehingga metode where() milik query tidak tersedia padanya dan pencarian seharusnya menggunakan $kategori->notes()->where(...)->get().
Kesalahan kedua adalah menamai kolom penghubung di luar konvensi tanpa memberi tahu Eloquent. Kolom bernama id_kategori tidak dikenali karena Eloquent menurunkan category_id, sehingga query relasi gagal dengan error kolom tidak ditemukan. Sebutkan nama kolomnya sebagai argumen kedua pada hasMany() maupun belongsTo().
Kesalahan ketiga adalah menaruh belongsTo pada model yang salah arah, misalnya mendeklarasikannya di Category. Model yang menyatakan belongsTo adalah pemilik kolom foreign key, sehingga dalam aplikasi catatan peran itu milik Note, sedangkan Category menyatakan hasMany. Relasi yang salah arah mengembalikan data kosong tanpa pesan error apa pun.
Ringkasan#
- Empat jenis relasi dasar adalah
hasOne,hasMany,belongsTo, danbelongsToMany, dan arahnya ditentukan oleh letak kolom foreign key. - Aplikasi catatan memuat sepasang relasi
Category::hasMany(Note)danNote::belongsTo(Category)yang menurunkan kolomcategory_idsecara otomatis. - Relasi diakses sebagai properti untuk mengambil datanya, berupa satu model untuk
belongsTodanCollectionuntukhasMany, sedangkan bentuk metodenya masih dapat dirantai dengan klausa query. - Relasi banyak ke banyak memerlukan pivot table dan dinyatakan di kedua model yang berelasi.
Referensi#
- Eloquent: Relationships — Laravel — Dokumentasi resmi hasOne, hasMany, belongsTo, dan belongsToMany beserta konvensi kuncinya.
- Eloquent: Collections — Laravel — Dokumentasi collection hasil relasi Eloquent dan metode pemrosesannya.