Pendahuluan — dari CRUD buku ke pinjam
Artikel ini adalah #62 (ini) di Seri 5: Laravel Lanjutan (di roadmap sering disebut Framework-based). Domain tetap perpustakaan mini.
Di CRUD API Buku: Ubah & Hapus (#61) kamu sudah melengkapi CRUD buku. Capstone (#60) memberi baca + login + tambah. Sekarang rak buku saja belum cukup: perpustakaan butuh anggota dan catatan peminjaman yang saling terhubung.
Awam: relasi = hubungan antar data. Satu anggota bisa punya banyak pinjaman. Satu pinjaman menunjuk satu anggota dan satu buku. Belum Capstone pinjam-kembali penuh — fokusnya “menghubungkan tabel” dulu.
Prasyarat: sudah baca CRUD ubah & hapus (#61) dan Capstone (#60). Pakai Laravel 11+.
Spesifikasi fitur — apa yang kita bangun?
Daftar singkat yang bisa dijelaskan ke teman:
- Anggota — orang yang boleh meminjam (nama + ID).
- Peminjaman — catatan: siapa meminjam buku mana, status aktif atau sudah kembali.
- Hubungan — dari anggota lihat daftar pinjamannya; dari pinjaman lihat anggota dan buku.
Awam: bayangkan kartu anggota di meja loket, dan slip pinjam yang menempel nomor anggota + nomor buku. Eloquent membantu membaca slip itu tanpa menulis “cari manual” berkali-kali.
Istilah — ringkas untuk relasi
| Istilah | Arti awam | Catatan |
|---|---|---|
| Relasi | Hubungan antar baris data (anggota ↔ pinjam ↔ buku) | Bukan “teman di media sosial” |
| Kunci asing | Nomor di baris pinjam yang menunjuk anggota/buku | Di kode sering anggota_id, buku_id |
hasMany |
“satu punya banyak” — anggota punya banyak pinjaman | Nama fungsi Eloquent |
belongsTo |
“banyak milik satu” — pinjaman milik satu anggota | Kebalikan arah dari hasMany |
| Eloquent | Cara Laravel membaca/menulis tabel lewat model | Sudah muncul di Capstone & service |
Urutan belajar: data terpisah dulu -> tautkan dengan nomor -> baru bungkus Eloquent.
Kenapa PHP biasa dulu?
Ide “slip pinjam menyimpan nomor anggota dan nomor buku” lebih mudah dirasakan di array PHP. Kalau alurnya klik, cuplikan hasMany / belongsTo terasa bungkus yang sama.
<?php
// Mini: gabungkan pinjaman dengan nama anggota & judul buku.
$anggota = [
1 => ["nama" => "Siti"],
2 => ["nama" => "Budi"],
];
$buku = [
10 => ["judul" => "Dasar PHP"],
11 => ["judul" => "Belajar Laravel"],
];
$pinjaman = [
["id" => 1, "anggota_id" => 1, "buku_id" => 10, "status" => "aktif"],
["id" => 2, "anggota_id" => 1, "buku_id" => 11, "status" => "kembali"],
];
$idAnggota = 1;
$daftar = [];
foreach ($pinjaman as $p) {
if ($p["anggota_id"] !== $idAnggota) {
continue;
}
$daftar[] = [
"pinjam_id" => $p["id"],
"anggota" => $anggota[$p["anggota_id"]]["nama"],
"buku" => $buku[$p["buku_id"]]["judul"],
"status" => $p["status"],
];
}
echo json_encode(["ok" => true, "pinjaman" => $daftar], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), PHP_EOL;
Output (bentuknya mirip):
{
"ok": true,
"pinjaman": [
{
"pinjam_id": 1,
"anggota": "Siti",
"buku": "Dasar PHP",
"status": "aktif"
},
{
"pinjam_id": 2,
"anggota": "Siti",
"buku": "Belajar Laravel",
"status": "kembali"
}
]
}
Awam: anggota_id dan buku_id = nomor yang menempel di slip. Loop di atas = “cari semua slip milik Siti, lalu tulis nama + judul”. Eloquent nanti mengganti loop manual itu.
Alur baca pinjaman — PHP sederhana
Setelah data terhubung, kita bisa menjawab: “pinjaman aktif milik anggota berapa?”
<?php
$anggota = [1 => ["nama" => "Siti"]];
$buku = [10 => ["judul" => "Dasar PHP"]];
$pinjaman = [
["id" => 1, "anggota_id" => 1, "buku_id" => 10, "status" => "aktif"],
["id" => 2, "anggota_id" => 1, "buku_id" => 10, "status" => "kembali"],
];
function pinjamanAktifAnggota(int $anggotaId, array $pinjaman, array $anggota, array $buku): array
{
if (! isset($anggota[$anggotaId])) {
return ["status" => 404, "body" => ["pesan" => "Anggota tidak ketemu"]];
}
$hasil = [];
foreach ($pinjaman as $p) {
if ($p["anggota_id"] !== $anggotaId || $p["status"] !== "aktif") {
continue;
}
if (! isset($buku[$p["buku_id"]])) {
return ["status" => 404, "body" => ["pesan" => "Buku tidak ketemu di slip"]];
}
$hasil[] = [
"pinjam_id" => $p["id"],
"anggota" => $anggota[$anggotaId]["nama"],
"buku" => $buku[$p["buku_id"]]["judul"],
"status" => $p["status"],
];
}
return ["status" => 200, "body" => ["ok" => true, "pinjaman" => $hasil]];
}
$r = pinjamanAktifAnggota(1, $pinjaman, $anggota, $buku);
http_response_code($r["status"]);
echo json_encode($r["body"], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), PHP_EOL;
Awam: filter status === "aktif" = hanya slip yang belum dikembalikan. Kalau anggota tidak ada = 404 — sama seperti “buku tidak ketemu” di #61.
Laravel — cuplikan relasi Eloquent (bukan file mandiri)
Di proyek Laravel, ide yang sama ditulis di model. Cuplikan di bawah bukan dijalankan dengan php file.php:
<?php
// Cuplikan Laravel (bukan file mandiri) — model Anggota.
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Anggota extends Model
{
public function peminjaman(): HasMany
{
return $this->hasMany(Peminjaman::class);
}
}
Awam: hasMany = “satu anggota punya banyak baris peminjaman”. HasMany = tipe kembalian — boleh diabaikan dulu.
<?php
// Cuplikan Laravel — model Peminjaman mengarah ke anggota & buku.
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Peminjaman extends Model
{
public function anggota(): BelongsTo
{
return $this->belongsTo(Anggota::class);
}
public function buku(): BelongsTo
{
return $this->belongsTo(Buku::class);
}
}
Awam: belongsTo = “baris ini milik satu anggota / satu buku”. Setelah itu, di controller/service kamu bisa menulis gaya: $anggota->peminjaman atau $pinjam->buku — Eloquent yang mengisi dari kunci asing.
Pola Dasar — anggota & peminjaman
-
1
Rapikan CRUD buku dulu
Pastikan alur di #61 sudah “klik”. -
2
Buat tabel anggota & peminjaman
Migrasi (skrip buat/ubah tabel): kolomanggota_iddanbuku_iddi slip pinjam. -
3
Tulis
hasMany/belongsTo
Satu arah “punya banyak”, arah balik “milik satu”. -
4
Baca lewat relasi
Dari anggota ambil daftar pinjam; dari pinjam ambil buku. -
5
Jaga ID kosong
Anggota/buku tidak ketemu =404, jangan pura-pura sukses. -
6
Uji dua arah
Anggota tanpa pinjam · pinjam aktif · ID palsu.
Kode lengkap — demo mandiri relasi
Simpan sebagai laravel_eloquent_relasi_peminjaman_demo.php, lalu jalankan php laravel_eloquent_relasi_peminjaman_demo.php:
<?php
declare(strict_types=1);
$anggota = [
1 => ["nama" => "Siti"],
2 => ["nama" => "Budi"],
];
$buku = [
10 => ["judul" => "Dasar PHP"],
11 => ["judul" => "Belajar Laravel"],
];
$pinjaman = [
["id" => 1, "anggota_id" => 1, "buku_id" => 10, "status" => "aktif"],
["id" => 2, "anggota_id" => 1, "buku_id" => 11, "status" => "kembali"],
["id" => 3, "anggota_id" => 2, "buku_id" => 10, "status" => "aktif"],
];
function pinjamanAktifAnggota(int $anggotaId, array $pinjaman, array $anggota, array $buku): array
{
if (! isset($anggota[$anggotaId])) {
return ["status" => 404, "body" => ["pesan" => "Anggota tidak ketemu"]];
}
$hasil = [];
foreach ($pinjaman as $p) {
if ($p["anggota_id"] !== $anggotaId || $p["status"] !== "aktif") {
continue;
}
if (! isset($buku[$p["buku_id"]])) {
return ["status" => 404, "body" => ["pesan" => "Buku tidak ketemu di slip"]];
}
$hasil[] = [
"pinjam_id" => $p["id"],
"anggota" => $anggota[$anggotaId]["nama"],
"buku" => $buku[$p["buku_id"]]["judul"],
"status" => $p["status"],
];
}
return ["status" => 200, "body" => ["ok" => true, "pinjaman" => $hasil]];
}
function demo(string $judul, callable $aksi): void
{
echo "=== {$judul} ===", PHP_EOL;
$hasil = $aksi();
echo "status: ", $hasil["status"], PHP_EOL;
echo json_encode($hasil["body"], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), PHP_EOL, PHP_EOL;
}
demo("Anggota palsu -> 404", function () use ($pinjaman, $anggota, $buku) {
return pinjamanAktifAnggota(99, $pinjaman, $anggota, $buku);
});
demo("Siti pinjam aktif -> 200", function () use ($pinjaman, $anggota, $buku) {
return pinjamanAktifAnggota(1, $pinjaman, $anggota, $buku);
});
demo("Budi pinjam aktif -> 200", function () use ($pinjaman, $anggota, $buku) {
return pinjamanAktifAnggota(2, $pinjaman, $anggota, $buku);
});
Awam: demo(...) hanya membungkus output di terminal. callable = sesuatu yang bisa dipanggil seperti fungsi. declare(strict_types=1); membuat tipe lebih ketat — boleh diikuti, tidak wajib dihafal. Alur penting: ID palsu, Siti punya pinjam aktif, Budi punya pinjam aktif.
Kesalahan umum
| Gejala | Penyebab tipikal | Perbaikan awam |
|---|---|---|
| Pinjaman kosong padahal data ada | Salah anggota_id / lupa filter status |
Cetak ID di demo; cek “aktif” vs “kembali” |
Bingung hasMany vs belongsTo |
Membalik arah “punya banyak” vs “milik satu” | Anggota hasMany pinjam; pinjam belongsTo anggota |
| Judul buku kosong | buku_id tidak ketemu di katalog |
Jawab 404 atau perbaiki kunci asing |
| Daftar pinjam terasa lambat saat data banyak | Membaca buku/anggota satu-satu di dalam loop (sering disebut masalah N+1) | Nanti dilatih saat daftar panjang (pagination) — cukup tahu dulu |
Latihan singkat
- Ubah demo: tambah kasus “anggota tanpa pinjam aktif” dan pastikan daftar kosong tapi status tetap
200. - Jelaskan ke teman: beda
hasManydanbelongsTodengan analogi kartu anggota + slip pinjam. - Tulis satu kalimat: kenapa slip pinjam menyimpan nomor, bukan menyalin seluruh nama anggota.
FAQ singkat
Haruskah hafal semua jenis relasi Eloquent?
Belum. Untuk perpustakaan mini, hasMany dan belongsTo sudah cukup. Relasi “banyak-ke-banyak” bisa menyusul kalau dibutuhkan.
Kenapa belum Capstone pinjam-kembali?
Karena fondasi hubungan data harus jelas dulu. Alur pinjam + kembali penuh ada di akhir Seri 5.
Ke mana setelah ini?
Berikutnya alami: pagination, filter & pencarian — daftar pinjaman/buku yang panjang tetap nyaman dibaca. Belum perlu hardlink; tunggu artikel berikutnya LIVE.
Kesimpulan
Kamu sudah melangkah dari CRUD buku ke relasi: anggota dan peminjaman terhubung lewat nomor (anggota_id, buku_id). PHP array dulu; Eloquent hasMany / belongsTo adalah bungkus yang sama.
Seri 5 progress: langkah #62 (ini) · 2/8 Laravel Lanjutan · prasyarat: CRUD ubah & hapus (#61) LIVE. Berikutnya: Pagination, filter & pencarian — soft, belum hardlink.