Pendahuluan — dari Capstone ke ubah & hapus

Artikel ini adalah #61 (ini) — pembuka Seri 5: Laravel Lanjutan (di roadmap sering disebut Framework-based). Bukan loncat stack baru: kita memperdalam pola yang sama di domain perpustakaan mini.

Di Capstone API Perpustakaan Laravel (#60) kamu sudah punya tiga pintu: baca katalog (publik), login staf, dan tambah buku (wajib bukti masuk). Sekarang kita lengkapi CRUD buku: ubah dan hapus.

Awam: CRUD = Create · Read · Update · Delete — buat, baca, ubah, hapus. Capstone sudah Create + Read. Artikel ini fokus Update + Delete. Masih satu rak buku; belum pinjam/kembali (itu nanti).

Prasyarat: sudah baca Capstone (#60) — paham route, bukti masuk, 401/422/201. Pakai Laravel 11+.

Spesifikasi fitur — apa yang kita bangun?

Daftar singkat yang bisa dijelaskan ke teman:

  1. Ubah buku — wajib bukti masuk. ID tidak ketemu = 404; isian kotor = 422; sukses = 200.
  2. Hapus buku — wajib bukti masuk. ID tidak ketemu = 404; sukses = 204 (atau 200 + pesan singkat).
  3. Katalog & tambah — tetap seperti Capstone (tidak diulang panjang di sini).

Awam: 404 = “tidak ketemu di rak”. Beda dengan 401 (belum diizinkan) dan 422 (isian belum rapi).

Istilah — ringkas untuk ubah & hapus

Istilah Arti awam Catatan
CRUD Empat kerja dasar data: buat · baca · ubah · hapus Bukan nama file Laravel
PUT / PATCH Metode HTTP untuk ubah data yang sudah ada Di sini kita pakai PUT sederhana
DELETE Metode HTTP untuk menghapus Wajib bukti masuk di Capstone-lanjutan ini
404 ID / buku tidak ketemu Bukan salah sandi
204 Sukses hapus; tubuh jawaban boleh kosong Boleh juga 200 + JSON — pilih satu gaya

Urutan tetap: cek bukti -> cari buku -> cek isian (untuk ubah) -> simpan/hapus -> jawab JSON.

Kenapa PHP biasa dulu?

Ide “ubah baris di rak” dan “buang baris dari rak” lebih mudah dirasakan tanpa framework. Kalau alurnya klik, cuplikan Laravel terasa bungkus yang sama.

<?php
// Mini ubah buku: tanpa bukti = 401; ID tidak ketemu = 404.
$buktiValid = "kartu-abc123";
$buktiDariHeader = "";
$katalog = [
    1 => ["judul" => "Dasar PHP", "tahun" => 2023],
];
$id = 1;
$isian = ["judul" => "Dasar PHP Revisi", "tahun" => 2024];

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;
}

if (! isset($katalog[$id])) {
    http_response_code(404);
    echo json_encode(["pesan" => "Buku tidak ketemu"], JSON_UNESCAPED_UNICODE), PHP_EOL;
    exit;
}

echo json_encode(["ok" => true, "catatan" => "siap ubah"], JSON_UNESCAPED_UNICODE), PHP_EOL;

Output:

{"pesan":"Belum diizinkan — bawa bukti masuk"}

Awam: contoh ini sengaja tanpa bukti agar kamu lihat pintu 401 dulu. Di demo lengkap nanti, bukti diisi.

CRUD: cek bukti -> cari ID -> ubah/hapus -> JSON Cek login bukti / 401 Cari ID 404 jika kosong Ubah / Hapus 422 jika kotor JSON 200 / 204 Ubah & hapus sensitif — jangan dibuka publik tanpa bukti masuk. Katalog baca tetap publik seperti Capstone.
Capstone sudah punya tambah; #61 (ini) menambah ubah dan hapus dengan urutan yang sama.

Alur ubah buku — PHP sederhana

Setelah login (lihat Capstone (#60)), pemanggil — aplikasi atau alat yang memanggil API — membawa bukti dan ID buku.

<?php
$buktiValid = "kartu-abc123";
$buktiDariHeader = "kartu-abc123";
$katalog = [
    1 => ["judul" => "Dasar PHP", "tahun" => 2023],
];
$id = 1;
$isian = ["judul" => "Dasar PHP Revisi", "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 (! isset($katalog[$id])) {
    http_response_code(404);
    echo json_encode(["pesan" => "Buku tidak ketemu"], JSON_UNESCAPED_UNICODE), PHP_EOL;
    exit;
}

$judul = trim((string) ($isian["judul"] ?? ""));
$tahun = $isian["tahun"] ?? null;
if ($judul === "" || ! is_int($tahun)) {
    http_response_code(422);
    echo json_encode(["pesan" => "Isian belum rapi"], JSON_UNESCAPED_UNICODE), PHP_EOL;
    exit;
}

$katalog[$id] = ["judul" => $judul, "tahun" => $tahun];
http_response_code(200);
echo json_encode(["ok" => true, "buku" => $katalog[$id]], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), PHP_EOL;

Output (bentuknya mirip):

{
    "ok": true,
    "buku": {
        "judul": "Dasar PHP Revisi",
        "tahun": 2024
    }
}

Awam: urutan penting. Cek bukti dulu, baru cari ID, baru validasi isian — supaya orang asing tidak “menguji” rak dan aturan isian.

Laravel — cuplikan ubah & hapus (bukan file mandiri)

Di proyek Laravel, ide yang sama tersebar di route + controller + Form Request + service. Cuplikan di bawah bukan dijalankan dengan php file.php:

<?php
// Cuplikan Laravel (bukan file mandiri) — ubah & hapus terlindungi.
use App\Http\Controllers\BukuController;
use Illuminate\Support\Facades\Route;

Route::put('/api/buku/{id}', [BukuController::class, 'update'])
    ->middleware('auth:sanctum'); // pemeriksa pintu

Route::delete('/api/buku/{id}', [BukuController::class, 'destroy'])
    ->middleware('auth:sanctum');

Awam:

  • update = fungsi ubah; dijaga pemeriksa pintu (middleware auth:sanctum)
  • destroy = fungsi hapus; nama umum di Laravel untuk “buang data”
  • {id} = nomor buku di URL — seperti nomor loker di rak
<?php
// Cuplikan Laravel — controller tipis memanggil service.
namespace App\Http\Controllers;

use App\Http\Requests\UpdateBookRequest;
use App\Services\BukuService;
use Illuminate\Http\JsonResponse;

class BukuController extends Controller
{
    public function __construct(private BukuService $bukuService)
    {
    }

    public function update(UpdateBookRequest $request, int $id): JsonResponse
    {
        $buku = $this->bukuService->ubah($id, $request->validated());

        if ($buku === null) {
            return response()->json(['pesan' => 'Buku tidak ketemu'], 404);
        }

        return response()->json(['ok' => true, 'buku' => $buku], 200);
    }

    public function destroy(int $id): JsonResponse
    {
        $ok = $this->bukuService->hapus($id);

        if (! $ok) {
            return response()->json(['pesan' => 'Buku tidak ketemu'], 404);
        }

        return response()->json(null, 204);
    }
}

Awam: UpdateBookRequest = penjaga isian untuk ubah. validated() = ambil isian yang sudah lolos. BukuService = pekerja yang menyimpan/menghapus (Eloquent di dalamnya). JsonResponse = tipe jawaban “ini JSON” — boleh diabaikan dulu. Baris private BukuService $bukuService = Laravel menyiapkan layanan otomatis (tidak perlu new manual).

Pola Dasar — ubah & hapus buku

  1. 1
    Mulai dari Capstone
    Pastikan baca + login + tambah sudah “klik” di Capstone (#60).
  2. 2
    Pasang route ubah & hapus
    PUT dan DELETE dengan pemeriksa pintu.
  3. 3
    Cari ID dulu
    Tidak ketemu = 404 — jangan pura-pura sukses.
  4. 4
    Jaga isian saat ubah
    Form Request / validasi — kotor = 422.
  5. 5
    Service tipis
    Controller minta pekerja; Eloquent di dalam service.
  6. 6
    Uji empat jalur
    Tanpa bukti · ID palsu · isian kotor · sukses ubah/hapus.

Kode lengkap — demo mandiri ubah & hapus

Simpan sebagai laravel_crud_buku_ubah_hapus_demo.php, lalu jalankan php laravel_crud_buku_ubah_hapus_demo.php:

<?php
declare(strict_types=1);

$buktiAktif = "kartu-abc123";
$katalog = [
    1 => ["judul" => "Dasar PHP", "tahun" => 2023],
    2 => ["judul" => "Belajar Laravel", "tahun" => 2024],
];

function ubahBuku(?string $buktiHeader, string $buktiAktif, int $id, array $isian, array &$katalog): array
{
    if ($buktiHeader === null || $buktiHeader === "" || $buktiHeader !== $buktiAktif) {
        return ["status" => 401, "body" => ["pesan" => "Belum diizinkan — bawa bukti masuk"]];
    }

    if (! isset($katalog[$id])) {
        return ["status" => 404, "body" => ["pesan" => "Buku tidak ketemu"]];
    }

    $judul = trim((string) ($isian["judul"] ?? ""));
    $tahun = $isian["tahun"] ?? null;
    if ($judul === "" || ! is_int($tahun)) {
        return ["status" => 422, "body" => ["pesan" => "Isian belum rapi"]];
    }

    $katalog[$id] = ["judul" => $judul, "tahun" => $tahun];

    return ["status" => 200, "body" => ["ok" => true, "buku" => $katalog[$id]]];
}

function hapusBuku(?string $buktiHeader, string $buktiAktif, int $id, array &$katalog): array
{
    if ($buktiHeader === null || $buktiHeader === "" || $buktiHeader !== $buktiAktif) {
        return ["status" => 401, "body" => ["pesan" => "Belum diizinkan — bawa bukti masuk"]];
    }

    if (! isset($katalog[$id])) {
        return ["status" => 404, "body" => ["pesan" => "Buku tidak ketemu"]];
    }

    unset($katalog[$id]);

    return ["status" => 204, "body" => null];
}

function demo(string $judul, callable $aksi): void
{
    echo "=== {$judul} ===", PHP_EOL;
    $hasil = $aksi();
    echo "status: ", $hasil["status"], PHP_EOL;
    if ($hasil["body"] === null) {
        echo "(tubuh kosong — cocok untuk 204)", PHP_EOL, PHP_EOL;
        return;
    }
    echo json_encode($hasil["body"], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), PHP_EOL, PHP_EOL;
}

demo("Ubah tanpa bukti -> 401", function () use ($buktiAktif, &$katalog) {
    return ubahBuku(null, $buktiAktif, 1, ["judul" => "X", "tahun" => 2025], $katalog);
});

demo("Ubah ID palsu -> 404", function () use ($buktiAktif, &$katalog) {
    return ubahBuku($buktiAktif, $buktiAktif, 99, ["judul" => "X", "tahun" => 2025], $katalog);
});

demo("Ubah isian kotor -> 422", function () use ($buktiAktif, &$katalog) {
    return ubahBuku($buktiAktif, $buktiAktif, 1, ["judul" => "", "tahun" => "dua"], $katalog);
});

demo("Ubah bersih -> 200", function () use ($buktiAktif, &$katalog) {
    return ubahBuku($buktiAktif, $buktiAktif, 1, ["judul" => "Dasar PHP Revisi", "tahun" => 2025], $katalog);
});

demo("Hapus bersih -> 204", function () use ($buktiAktif, &$katalog) {
    return hapusBuku($buktiAktif, $buktiAktif, 2, $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: tanpa bukti, ID palsu, isian kotor, sukses ubah, sukses hapus.

Kesalahan umum

Gejala Penyebab tipikal Perbaikan awam
Bingung 401 vs 404 Mencampur “belum diizinkan” dengan “tidak ketemu” 401 = identitas; 404 = ID kosong di rak
Ubah publik tanpa bukti Lupa pemeriksa pintu (middleware) Kunci PUT/DELETE seperti kunci POST tambah
Hapus sukses tapi data masih ada Salah ID atau belum dihapus di service (di PHP demo: unset = buang dari daftar) Uji ulang dengan demo; cek ID yang sama
Controller membengkak Validasi + cari + hapus digabung satu fungsi Form Request + Service + controller tipis

Latihan singkat

  1. Ubah demo: tambah kasus “hapus tanpa bukti” dan pastikan tetap 401.
  2. Jelaskan ke teman: beda 404 dan 422 dengan analogi rak buku.
  3. Tulis satu kalimat: kenapa ubah/hapus tidak boleh publik seperti baca katalog.

FAQ singkat

Haruskah pakai PUT atau PATCH?
Untuk belajar, PUT (ganti data buku yang dikirim) sudah cukup. PATCH biasanya untuk ubah sebagian — boleh dipelajari setelah alur ini jelas.

Kenapa 204 tubuhnya kosong?
Artinya “sukses, tidak ada JSON yang perlu dibaca”. Kalau kamu lebih nyaman 200 + {"ok": true}, itu juga valid — pilih satu gaya dan konsisten.

Ke mana setelah ini?
Berikutnya alami: relasi Eloquent (anggota & peminjaman) — masih Laravel lanjutan, belum Capstone pinjam-kembali. Belum perlu hardlink; tunggu artikel berikutnya LIVE.

Kesimpulan

Kamu sudah melengkapi CRUD buku: Capstone memberi baca + tambah; #61 (ini) menambah ubah dan hapus dengan urutan bukti -> cari ID -> validasi -> kerja service. Status baru yang penting: 404 dan 204.

Seri 5 progress: langkah #61 (ini) · 1/8 Laravel Lanjutan · prasyarat: Capstone (#60) LIVE. Berikutnya: Relasi Eloquent (anggota & peminjaman) — soft, belum hardlink.