Pendahuluan — siapa boleh ubah catatan pinjam?

Artikel ini adalah #66 (ini) di Seri 5: Laravel Lanjutan. Setelah daftar pinjam panjang dirapikan di Pagination, Filter & Pencarian (#65), pertanyaan berikutnya muncul: siapa boleh mengubah slip pinjam orang lain?

Tanpa aturan izin, siapa saja bisa mengubah catatan pinjam milik anggota lain — berbahaya. Hari ini kita belajar policy (aturan izin): cek pemilik dulu, tolak dengan 403 kalau bukan pemilik, dan izinkan petugas (staf) lewat aturan sederhana.

Awam: bayangkan kartu anggota perpustakaan. Hanya pemilik kartu (atau petugas resmi di loket) yang boleh mengubah catatan pinjam miliknya. Itu inti aturan izin (policy). Pemanggil API — aplikasi atau alat yang memanggil API — harus dikenali dulu sebelum server mengizinkan ubah data.

Prasyarat: sudah selesai Pagination, Filter & Pencarian (#65), paham fondasi Instal PHP, Composer & Proyek Laravel (#56) / Struktur Folder, .env & Artisan Laravel (#57). Pakai Laravel 13+ — butuh PHP 8.3+.

Spesifikasi fitur — apa yang selesai hari ini?

Tiga hal ini yang kita kejar:

  1. Cek pemilik — sebelum ubah, pastikan callerId sama dengan anggota_id di catatan pinjam (atau pemanggil adalah staf).
  2. Jawaban jelas saat ditolak — status 403 dengan pesan awam “Tidak punya izin”.
  3. Aturan terpusat — logika “boleh/tidak” tidak tersebar di banyak tempat; di Laravel dipindah ke kelas Policy.

Awam: selesai artikel ini, kamu belum merapikan bentuk JSON jawaban. Kamu sedang mengunci siapa boleh ubah slip pinjam di proyek perpustakaan mini — pemilik atau staf saja. Format JSON yang cantik datang di artikel berikutnya tentang API Resource.

Istilah — ringkas untuk aturan izin

Istilah Arti awam Catatan
Aturan izin / policy Daftar “siapa boleh apa” Bukan kata sandi login
anggota_id ID anggota pemilik catatan pinjam Dari relasi di artikel relasi Eloquent sebelumnya
403 vs 401 / login 403 = sudah dikenali tapi tidak punya izin · 401 = belum login Pesan awam: “Tidak punya izin” untuk 403
authorize Perintah “cek aturan izin dulu” Di Laravel, sebelum aksi sensitif di pengatur kode
Kelas Policy File tempat aturan ditulis rapi Contoh: PeminjamanPolicy
Staf Petugas perpustakaan yang boleh mengubah catatan anggota Override sederhana: $isStaf === true

Urutan belajar kita: array PHP dulu -> cek pemilik dengan if -> baru bungkus Laravel Policy. Kalau loncat langsung ke Policy tanpa memahami anggota_id, pesan 403 sering terasa misterius.

Persiapan — alat yang kamu buka

Alat yang dipakai di artikel ini (fondasi dari Instal PHP, Composer & Proyek Laravel (#56) dan Struktur Folder, .env & Artisan Laravel (#57) — tidak ada unduhan Composer baru hari ini):

  • Explorer — cek folder proyek perpustakaan-api, lalu lihat app\Http\Controllers dan app\Policies untuk pengatur kode dan aturan izin.
  • Terminal — Laragon: menu Terminal · XAMPP: tombol Shell. Hindari CMD/PowerShell dari Start Menu kalau PATH PHP-mu belum rapi.
  • Editor teks — Notepad / VS Code — untuk membuka pengatur kode. Contoh: notepad app\Http\Controllers\PeminjamanController.php dan notepad app\Policies\PeminjamanPolicy.php.
  • Browser — opsional. Inti uji hari ini ada di terminal; browser berguna kalau kamu sudah menjalankan php artisan serve dan ingin uji lewat alamat URL.

Awam: untuk artikel ini satu terminal sebenarnya cukup — jalankan php laravel_policy_otorisasi_api_demo.php di folder proyek. Kalau php artisan serve dari artikel sebelumnya masih hidup, pakai terminal kedua untuk demo PHP dan perintah curl.exe saat menguji rute Laravel. Kalau butuh jendela kedua: Laragon — klik menu Terminal lagi · XAMPP — klik tombol Shell lagi, lalu cd ke folder proyek yang sama.

Buka terminal Laragon/Shell XAMPP, masuk ke folder proyek:

cd C:\laragon\www\perpustakaan-api

Di XAMPP biasanya: cd C:\xampp\htdocs\perpustakaan-api. Sesuaikan kalau foldermu beda.

Install-dari-nol: kalau php atau composer belum dikenali terminal, kembali dulu ke Instal PHP, Composer & Proyek Laravel (#56). Kalau struktur folder proyek masih membingungkan, ulangi Struktur Folder, .env & Artisan Laravel (#57).

Kenapa PHP biasa dulu?

Kalau langsung loncat ke kelas Policy di Laravel, pemula sering bingung: kenapa ditolak? Maka kita mulai dari array PHP biasa supaya cek anggota_id terlihat jelas sebelum dibungkus authorize.

<?php
// Mini: cek pemilik sebelum ubah status pinjam.
$pinjam = ["id" => 10, "anggota_id" => 1, "judul" => "Dasar PHP", "status" => "aktif"];
$callerId = 2; // bukan pemilik

if ($callerId !== $pinjam["anggota_id"]) {
    http_response_code(403);
    echo json_encode(["pesan" => "Tidak punya izin"], JSON_UNESCAPED_UNICODE), PHP_EOL;
    exit;
}

$pinjam["status"] = "kembali";
echo json_encode(["ok" => true, "data" => $pinjam], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), PHP_EOL;

Awam: anggota_id di catatan harus sama dengan siapa yang memanggil (callerId). Beda? Tolak dengan 403 — artinya “Tidak punya izin”, bukan “data hilang” (404). Login menjawab “siapa kamu”; aturan izin menjawab “apakah kamu boleh ubah baris ini”.

Alur izin — cek pemilik dulu

Gerakan yang benar selalu sama:

  1. Kenali pemanggil — siapa yang login / kartu anggota mana.
  2. Temukan catatan — pinjam ada? Kalau tidak, jawab 404.
  3. Bandingkan pemilikanggota_id catatan vs callerId, atau cek apakah pemanggil staf.
  4. Izinkan atau tolak — pemilik/staf boleh ubah (200), lainnya 403 “Tidak punya izin”.
<?php
// Salin ke file misalnya izin-cek.php lalu jalankan: php izin-cek.php
$pinjam = [
    ["id" => 10, "anggota_id" => 1, "judul" => "Dasar PHP", "status" => "aktif"],
    ["id" => 11, "anggota_id" => 2, "judul" => "Belajar Laravel", "status" => "aktif"],
];

function ubahStatusPinjam(array $pinjam, int $pinjamId, int $callerId, string $statusBaru, bool $isStaf = false): array
{
    $row = null;
    foreach ($pinjam as $p) {
        if ($p["id"] === $pinjamId) {
            $row = $p;
            break;
        }
    }
    if ($row === null) {
        return ["status" => 404, "body" => ["pesan" => "Catatan pinjam tidak ketemu"]];
    }
    if (! $isStaf && $row["anggota_id"] !== $callerId) {
        return ["status" => 403, "body" => ["pesan" => "Tidak punya izin"]];
    }

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

$r = ubahStatusPinjam($pinjam, 10, 2, "kembali");
http_response_code($r["status"]);
echo json_encode($r["body"], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), PHP_EOL;

Awam — cara menguji bagian ini: salin potongan di atas ke izin-cek.php, lalu di terminal jalankan php izin-cek.php. Kalau muncul JSON dengan "pesan": "Tidak punya izin" dan status 403, cek pemilik sudah sehat. Ubah $callerId ke 1 (pemilik) dan jalankan lagi — harus dapat 200 dengan ok: true.

Pemanggil -> Cek pemilik/staf -> Update / 403 Pemanggil callerId Cek pemilik/staf anggota_id cocok? Ya: update status 200 Tidak: 403 Tidak punya izin 403 = Tidak punya izin (bukan “belum login”). Staf boleh lewat aturan override sederhana.
Urutan yang benar: kenali pemanggil, cek pemilik atau staf, baru izinkan ubah atau tolak dengan 403.

Laravel — cuplikan Policy & authorize (bukan file mandiri)

Di proyek Laravel, aturan izin ditulis di kelas Policy, lalu dipanggil lewat authorize di pengatur kode sebelum aksi sensitif.

<?php
// Cuplikan Laravel (bukan file mandiri)
// app/Policies/PeminjamanPolicy.php

namespace App\Policies;

use App\Models\Peminjaman;
use App\Models\User;

class PeminjamanPolicy
{
    public function update(User $user, Peminjaman $peminjaman): bool
    {
        return $user->id === $peminjaman->anggota_id || $user->is_staf;
    }
}
<?php
// Cuplikan Laravel (bukan file mandiri)
// app/Http/Controllers/PeminjamanController.php

public function update(Request $request, Peminjaman $peminjaman)
{
    $this->authorize('update', $peminjaman);

    $peminjaman->update($request->only('status'));

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

Awam: PeminjamanPolicy::update = aturan “boleh ubah kalau pemilik sama atau staf”. authorize('update', $peminjaman) = jalankan aturan itu dulu; gagal -> Laravel otomatis jawab 403. Aturan di satu file Policy lebih mudah dirawat daripada if berulang di banyak tempat. Cuplikan ini bukan file mandiri — tempel ke proyek kalau rute ubah pinjam sudah ada.

Kalau php artisan serve sudah jalan di terminal pertama, uji di terminal kedua. Di Windows ketik curl.exe (bukan alias curl saja) supaya PowerShell tidak bingung:

curl.exe -X PUT "http://127.0.0.1:8000/api/peminjaman/10" -H "Content-Type: application/json" -d "{\"status\":\"kembali\"}"

Awam: respons JSON dari curl.exe adalah cara cepat melihat apakah aturan izin bekerja sebelum membuka browser. Kalau muncul 403 dengan pesan jelas, policy menolak dengan benar. Kalau 404, catatan pinjam mungkin tidak ada — beda masalah dari izin.

Pola Dasar — aturan izin yang rapi

  1. 1
    Kenali pemanggil
    Siapa yang login / kartu anggota mana — fondasi dari langkah sebelumnya.
  2. 2
    Temukan catatan
    Pinjam ada? Kalau tidak, jawab 404 jelas.
  3. 3
    Bandingkan pemilik
    anggota_id catatan vs callerId — PHP if dulu, termasuk cek staf.
  4. 4
    Tolak dengan 403
    Pesan awam “Tidak punya izin” — jangan biarkan orang lain ubah.
  5. 5
    Pindah ke Policy
    Tulis aturan di kelas PeminjamanPolicy; panggil authorize sebelum ubah.
  6. 6
    Uji tiga jalur
    Pemilik benar · bukan pemilik · catatan tidak ada · staf override.

Kode lengkap — demo mandiri

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

<?php
declare(strict_types=1);

$pinjam = [
    ["id" => 10, "anggota_id" => 1, "judul" => "Dasar PHP", "status" => "aktif"],
    ["id" => 11, "anggota_id" => 2, "judul" => "Belajar Laravel", "status" => "aktif"],
];

function ubahStatusPinjam(array $pinjam, int $pinjamId, int $callerId, string $statusBaru, bool $isStaf = false): array
{
    $row = null;
    foreach ($pinjam as $p) {
        if ($p["id"] === $pinjamId) {
            $row = $p;
            break;
        }
    }
    if ($row === null) {
        return ["status" => 404, "body" => ["pesan" => "Catatan pinjam tidak ketemu"]];
    }
    if (! $isStaf && $row["anggota_id"] !== $callerId) {
        return ["status" => 403, "body" => ["pesan" => "Tidak punya izin"]];
    }

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

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("Bukan pemilik -> 403", function () use ($pinjam) {
    return ubahStatusPinjam($pinjam, 10, 2, "kembali");
});

demo("Pemilik benar -> 200", function () use ($pinjam) {
    return ubahStatusPinjam($pinjam, 10, 1, "kembali");
});

demo("Catatan tidak ada -> 404", function () use ($pinjam) {
    return ubahStatusPinjam($pinjam, 99, 1, "kembali");
});

demo("Staf override -> 200", function () use ($pinjam) {
    return ubahStatusPinjam($pinjam, 10, 2, "kembali", true);
});

Awam: empat skenario di atas menunjukkan pola respons yang wajar: bukan pemilik ditolak, pemilik boleh, catatan hilang dapat 404, staf boleh ubah meski bukan pemilik. Fungsi ubahStatusPinjam adalah inti logika; demo(...) hanya membungkus output agar mudah dibaca di terminal.

Kesalahan umum

Gejala Penyebab tipikal Perbaikan awam
Siapa saja bisa ubah pinjam orang lain Lupa cek anggota_id vs callerId Cek pemilik sebelum ubah — salin pola dari izin-cek.php
403 tanpa pesan yang jelas Respons kosong atau teknis Tulis “Tidak punya izin” yang awam pahami
Bingung 403 vs 404 Keduanya dianggap “gagal” 404 = tidak ketemu · 403 = ada tapi tidak punya izin
403 padahal yakin pemilik callerId salah atau belum login Pastikan identitas pemanggil benar sebelum cek policy
Aturan tersebar di banyak file Copy-paste if berulang Kumpulkan di kelas PeminjamanPolicy
curl aneh atau error di PowerShell Alias curl di PowerShell bukan curl.exe Ketik curl.exe persis seperti contoh, atau uji lewat browser

Latihan singkat

  1. Ubah demo: tambah kasus “pemilik benar ubah pinjam id 11” dan bandingkan dengan kasus bukan pemilik.
  2. Jelaskan ke teman: beda 403 (Tidak punya izin) dengan 404 (tidak ketemu).
  3. Tulis satu kalimat: kenapa aturan izin lebih rapi di kelas Policy daripada if di banyak tempat.

FAQ singkat

Apakah Policy menggantikan login?
Tidak. Login menjawab “siapa kamu” (401 kalau belum). Policy menjawab “apakah kamu boleh melakukan ini pada baris ini” (403 kalau tidak).

Haruskah selalu pakai kelas Policy?
Untuk belajar, if PHP di izin-cek.php sudah cukup memahami ide. Di proyek Laravel nyata, Policy membantu merapikan aturan saat bertambah.

Tool apa yang dibuka dulu?
Explorer untuk memastikan folder proyek benar (Controllers + Policies), satu terminal untuk demo PHP, editor untuk pengatur kode. Kalau serve hidup, terminal kedua untuk curl.exe.

Potongan sintaks diuji di mana?
Langkah tengah (cek pemilik) salin ke izin-cek.php, lalu jalankan php izin-cek.php. Demo lengkap diuji dengan php laravel_policy_otorisasi_api_demo.php. Cuplikan Laravel ditempel ke app\Http\Controllers\PeminjamanController.php dan app\Policies\PeminjamanPolicy.php; kalau rute sudah ada, uji dengan curl.exe di terminal kedua.

Ke mana setelah ini?
Berikutnya alami: API Resource: Rapikan Bentuk JSON (#67) — rapikan bentuk JSON jawaban.

Kesimpulan

Kamu sudah mengunci siapa boleh ubah catatan pinjam: cek pemilik dengan if PHP dulu, lalu pindahkan ke aturan izin (Policy) dan authorize di Laravel. Status 403 = “Tidak punya izin” — jelas untuk pemanggil API. Staf boleh lewat override sederhana sebelum aturan makin rumit.

Seri 5 progress: langkah #66 (ini) · 3/7 Laravel Lanjutan · prasyarat: Pagination, Filter & Pencarian (#65) LIVE. Berikutnya: API Resource: Rapikan Bentuk JSON (#67).