Pendahuluan — Capstone: satukan semua
Artikel ini adalah #60 (ini) — Capstone Seri 4. Capstone artinya penutup yang merangkai langkah sebelumnya jadi satu cerita utuh, bukan ide baru yang loncat jauh.
Kamu sudah punya potongan di Routing & JSON (#56), Request & Form Request (#57), Controller, Service & Eloquent (#58), dan Auth API Dasar (#59). Sekarang kita satukan: API perpustakaan mini yang bisa dibaca publik, tapi menambah buku hanya setelah login + bukti masuk.
Awam: bayangkan perpustakaan yang pintunya sudah dipasang: peta jalan (route), penjaga isian, loket + pekerja + rak (controller/service/Eloquent), lalu kartu anggota (auth). Capstone = menjalankan semuanya dalam satu alur.
Prasyarat: sudah baca Auth API Dasar (#59) — paham login, bukti masuk, dan status
401. Domain tetap perpustakaan mini. Pakai Laravel 11+ — ide merangkai di sini berlaku di versi modern.
Spesifikasi fitur — apa yang kita bangun?
Jangan mulai dari “proyek besar”. Mulai dari daftar singkat yang bisa dijelaskan ke teman:
- Baca katalog — publik (tanpa bukti masuk). Jawaban JSON daftar buku.
- Login staf — email + sandi. Sukses = bukti masuk; gagal =
401. - Tambah buku — wajib bukti masuk. Isian kotor =
422; tanpa bukti =401; sukses =201.
Awam: tiga pintu itu cukup untuk merasakan “API utuh”. Fitur pinjam/kembali/admin penuh bisa jadi latihan lanjut — bukan syarat Capstone ini.
Istilah — ringkas untuk Capstone
| Istilah | Arti awam | Dari artikel |
|---|---|---|
| Route | Peta: URL + metode HTTP ke fungsi yang menangani | Routing & JSON (#56) |
| Form Request | Penjaga isian sebelum masuk ke pengatur kode | Request & Form Request (#57) |
| Controller / Service / Eloquent | Loket · pekerja · cara simpan ke database | Controller, Service & Eloquent (#58) |
| Bukti masuk + 401 | Kartu anggota; tanpa kartu = belum diizinkan | Auth API Dasar (#59) |
Jangan hafal ulang semua. Cukup ingat urutan: pintu -> cek siapa kamu -> cek isian -> simpan -> jawab JSON.
Kenapa belum langsung satu proyek Laravel besar?
Karena ide “satukan alur” lebih mudah dirasakan di PHP biasa dulu. Kalau alurnya sudah “klik”, cuplikan Laravel terasa seperti bungkus yang sama — bukan sihir baru.
<?php
// Mini alur Capstone (bukan database sungguhan): tanpa bukti = 401.
$buktiValid = "kartu-abc123";
$buktiDariHeader = ""; // kosong
header("Content-Type: application/json; charset=utf-8");
if ($buktiDariHeader === "" || $buktiDariHeader !== $buktiValid) {
http_response_code(401);
echo json_encode(["pesan" => "Belum diizinkan — bawa bukti masuk"], JSON_UNESCAPED_UNICODE), PHP_EOL;
exit;
}
echo json_encode(["ok" => true], JSON_UNESCAPED_UNICODE), PHP_EOL;
Output:
{"pesan":"Belum diizinkan — bawa bukti masuk"}
Awam: ini hanya pintu cek bukti (auth). Di Capstone, setelah pintu terbuka baru validasi isian dan simpan buku — seperti antrean loket yang rapi.
Alur tambah buku — dari bukti sampai JSON
Setelah login (lihat Auth API Dasar (#59)), pemanggil — aplikasi atau alat yang memanggil API — membawa bukti. Di PHP sederhana, urutannya seperti ini:
<?php
$buktiValid = "kartu-abc123";
$buktiDariHeader = "kartu-abc123";
$isian = ["judul" => "Belajar PHP", "tahun" => 2024];
header("Content-Type: application/json; charset=utf-8");
if ($buktiDariHeader !== $buktiValid) {
http_response_code(401);
echo json_encode(["pesan" => "Belum diizinkan — bawa bukti masuk"], JSON_UNESCAPED_UNICODE), PHP_EOL;
exit;
}
if (($isian["judul"] ?? "") === "" || ! is_int($isian["tahun"] ?? null)) {
http_response_code(422);
echo json_encode(["pesan" => "Isian belum rapi"], JSON_UNESCAPED_UNICODE), PHP_EOL;
exit;
}
http_response_code(201);
echo json_encode(["ok" => true, "buku" => $isian], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), PHP_EOL;
Output (bentuknya mirip):
{
"ok": true,
"buku": {
"judul": "Belajar PHP",
"tahun": 2024
}
}
Awam: urutan penting. Kalau validasi dulu tanpa auth, orang asing bisa “menguji” isian. Banyak API cek bukti dulu, baru isian — seperti di diagram.
Laravel — merangkai cuplikan (bukan file mandiri)
Di proyek Laravel, ide yang sama biasanya tersebar di beberapa file. Cuplikan di bawah bukan dijalankan dengan php file.php:
<?php
// Cuplikan Laravel (bukan file mandiri) — route publik + terlindungi.
use App\Http\Controllers\BukuController;
use App\Http\Controllers\AuthController;
use Illuminate\Support\Facades\Route;
Route::get('/api/buku', [BukuController::class, 'index']); // publik
Route::post('/api/login', [AuthController::class, 'login']);
Route::post('/api/buku', [BukuController::class, 'store'])
->middleware('auth:sanctum'); // wajib bukti masuk
Awam:
index= baca daftar (katalog publik)login= keluarkan bukti masuk (dari Auth API Dasar (#59))store= fungsi tambah; dijaga pemeriksa pintu (middlewareauth:sanctum)- Isian
storebiasanya lewat Form Request (dari Request & Form Request (#57)), lalu Service + Eloquent (dari Controller, Service & Eloquent (#58))
<?php
// Cuplikan Laravel — controller tipis memanggil service.
namespace App\Http\Controllers;
use App\Http\Requests\StoreBookRequest;
use App\Services\BukuService;
use Illuminate\Http\JsonResponse;
class BukuController extends Controller
{
public function __construct(private BukuService $bukuService)
{
}
public function store(StoreBookRequest $request): JsonResponse
{
$buku = $this->bukuService->tambah($request->validated());
return response()->json(['ok' => true, 'buku' => $buku], 201);
}
}
Awam: StoreBookRequest = penjaga isian. validated() = ambil isian yang sudah lolos penjaga. BukuService = pekerja yang menyimpan (Eloquent di dalamnya). JsonResponse = tipe jawaban “ini JSON” — boleh diabaikan dulu kalau masih asing. Baris private BukuService $bukuService di konstruktor = Laravel menyiapkan layanan otomatis (tidak perlu new manual). Controller tetap tipis: terima yang sudah bersih, minta service bekerja, balas JSON.
Pola Dasar — Capstone API
-
1
Tulis spesifikasi singkat Katalog publik · login · tambah buku terlindungi.
-
2
Pasang route Peta URL dari Routing & JSON (#56).
-
3
Kunci yang sensitif Auth + bukti masuk dari Auth API Dasar (#59).
-
4
Jaga isian Form Request / validasi dari Request & Form Request (#57) — kotor =
422. -
5
Simpan lewat service Controller tipis + Service + Eloquent dari Controller, Service & Eloquent (#58).
-
6
Uji tiga jalur Tanpa bukti · isian kotor · sukses 201. Baru kembangkan fitur lain.
Kode lengkap — demo mandiri Capstone
Simpan sebagai laravel_capstone_perpustakaan_demo.php, lalu jalankan php laravel_capstone_perpustakaan_demo.php:
<?php
declare(strict_types=1);
$anggota = [
"email" => "staf@perpustakaan.test",
"sandi" => "rahasia123",
];
$buktiAktif = null;
$katalog = [
["judul" => "Dasar PHP", "tahun" => 2023],
];
function login(array $input, array $anggota): array
{
if (($input["email"] ?? "") !== $anggota["email"] || ($input["sandi"] ?? "") !== $anggota["sandi"]) {
return ["status" => 401, "body" => ["pesan" => "Belum diizinkan — email atau sandi salah"]];
}
$bukti = "kartu-".bin2hex(random_bytes(4));
return ["status" => 200, "body" => ["ok" => true, "bukti_masuk" => $bukti]];
}
function katalog(array $katalog): array
{
return ["status" => 200, "body" => ["ok" => true, "data" => $katalog]];
}
function tambahBuku(?string $buktiHeader, ?string $buktiAktif, array $isian, array &$katalog): array
{
if ($buktiHeader === null || $buktiHeader === "" || $buktiHeader !== $buktiAktif) {
return ["status" => 401, "body" => ["pesan" => "Belum diizinkan — bawa bukti masuk"]];
}
$judul = trim((string) ($isian["judul"] ?? ""));
$tahun = $isian["tahun"] ?? null;
if ($judul === "" || ! is_int($tahun)) {
return ["status" => 422, "body" => ["pesan" => "Isian belum rapi"]];
}
$buku = ["judul" => $judul, "tahun" => $tahun];
$katalog[] = $buku;
return ["status" => 201, "body" => ["ok" => true, "buku" => $buku]];
}
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("Katalog publik -> 200", function () use ($katalog) {
return katalog($katalog);
});
demo("Login kotor -> 401", function () use ($anggota) {
return login(["email" => "staf@perpustakaan.test", "sandi" => "salah"], $anggota);
});
demo("Login bersih -> 200 + bukti", function () use ($anggota, &$buktiAktif) {
$hasil = login(["email" => "staf@perpustakaan.test", "sandi" => "rahasia123"], $anggota);
if (($hasil["body"]["bukti_masuk"] ?? null) !== null) {
$buktiAktif = $hasil["body"]["bukti_masuk"];
}
return $hasil;
});
demo("Tambah tanpa bukti -> 401", function () use (&$buktiAktif, &$katalog) {
return tambahBuku(null, $buktiAktif, ["judul" => "Belajar Laravel", "tahun" => 2024], $katalog);
});
demo("Tambah isian kotor -> 422", function () use (&$buktiAktif, &$katalog) {
return tambahBuku($buktiAktif, $buktiAktif, ["judul" => "", "tahun" => "dua ribu"], $katalog);
});
demo("Tambah bersih -> 201", function () use (&$buktiAktif, &$katalog) {
return tambahBuku($buktiAktif, $buktiAktif, ["judul" => "Belajar Laravel", "tahun" => 2024], $katalog);
});
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: katalog publik, login gagal/sukses, tambah tanpa bukti, isian kotor, lalu sukses.
Kesalahan umum
| Gejala | Penyebab tipikal | Perbaikan awam |
|---|---|---|
| Semua pintu API 401 | Katalog publik ikut dikunci pemeriksa pintu (middleware) | Kunci hanya yang sensitif (misalnya POST tambah) |
| Bingung 401 vs 422 | Mencampur “belum diizinkan” dengan “data kotor” | 401 = identitas; 422 = isian |
| Controller membengkak | Validasi + simpan + JSON digabung di satu fungsi | Form Request + Service + controller tipis |
| Capstone terasa “belum selesai” | Target terlalu besar (pinjam, denda, admin…) | Kunci dulu 3 fitur inti; sisanya latihan |
Latihan singkat
- Ubah demo: tambah kasus “bukti palsu” dan pastikan tetap
401. - Jelaskan ke teman (tanpa jargon): urutan auth -> validasi -> simpan dengan analogi loket.
- Tulis satu kalimat: beda pintu publik (
GET /api/buku) dan pintu terlindungi (POST /api/buku).
FAQ singkat
Apakah Capstone harus satu folder proyek Laravel lengkap di laptop?
Idealnya ya, tapi artikel ini menekankan alur. Demo PHP mandiri sudah cukup untuk “merasakan” satunya pintu. Cuplikan Laravel menunjukkan tempat masing-masing potongan.
Haruskah pakai Sanctum sekarang?
Sanctum = paket Laravel untuk mengeluarkan dan memeriksa bukti masuk API. Untuk alur Capstone, itu pilihan umum di Laravel modern. Detail pasang-pasangnya bisa dipelajari setelah alurnya sudah jelas (lihat juga Auth API Dasar (#59)).
Ke mana setelah Seri 4?
Berikutnya: CRUD API Buku: Ubah & Hapus (#61) — pembuka Seri 5 Laravel Lanjutan. Memperdalam pola yang sama: ubah dan hapus buku lewat API.
Indeks Seri 4 lengkap
- Mengenal OOP PHP (#53)
- Property, Method & Constructor (#54)
- Visibility & Composition (#55)
- Laravel Routing & JSON (#56)
- Request & Form Request (#57)
- Controller, Service & Eloquent (#58)
- Auth API Dasar (#59)
- Capstone: API Perpustakaan Laravel (#60 (ini)) — artikel ini
Kesimpulan
Kamu sudah menutup Seri 4 dengan Capstone: route, auth, validasi, dan controller/service/Eloquent digabung jadi API perpustakaan mini. Publik boleh baca; staf membawa bukti masuk untuk menambah buku. Alur dari Auth API Dasar (#59) tetap dipakai — sekarang dalam satu cerita utuh.
Seri 4 progress: langkah #60 (ini) · 8/8 Capstone Laravel selesai · stack Laravel 5/5 · prasyarat: Auth API Dasar (#59) LIVE. Berikutnya: CRUD API Buku: Ubah & Hapus (#61) — Seri 5 Laravel Lanjutan.