Pendahuluan — daftar pinjam panjang butuh potongan

Artikel ini adalah #65 (ini) di Seri 5: Laravel Lanjutan. Setelah relasi anggota, buku, dan peminjaman selesai di Relasi Eloquent: Anggota & Peminjaman (#64), daftar slip pinjam di proyekmu mulai terasa panjang.

Satu respons yang memuat ratusan baris bikin lambat dan sulit dibaca. Hari ini kita belajar tiga gerakan dasar: filter status (aktif atau kembali), pencarian lewat ?q= pada judul buku atau nama anggota, lalu pagination supaya tiap halaman hanya menampilkan potongan kecil. Urutan yang benar: saring -> cari -> potong — jangan potong dulu baru saring.

Awam: bayangkan tumpukan slip pinjam di meja loket. Petugas tidak menyerahkan semua slip sekaligus. Dia memilih slip yang statusnya cocok, mencari nama atau judul buku yang kamu sebut, lalu hanya mengambil segenggam untuk halaman pertama. API daftar pinjam bekerja dengan logika yang sama; jawaban itu dibaca oleh pemanggil, yaitu aplikasi atau alat yang memanggil API.

Prasyarat: sudah selesai Relasi Eloquent: Anggota & Peminjaman (#64), 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. Pagination — daftar pinjam dipotong per halaman dengan page dan per_page.
  2. Filter status — hanya tampilkan slip aktif atau kembali lewat parameter status.
  3. Pencarian — cari judul buku atau nama anggota lewat ?q= (bisa juga disebut kata kunci pencarian; di roadmap kita pakai nama q).

Awam: selesai artikel ini, kamu belum membangun izin siapa boleh ubah pinjam. Kamu sedang membuat daftar panjang terasa rapi untuk pemanggil API: tidak semua baris dilontarkan sekaligus, tapi dipilih dan dipotong dengan aturan yang jelas.

Istilah — ringkas untuk daftar panjang

Istilah Arti awam Catatan
Pagination Potong daftar per halaman Halaman 1, halaman 2, dan seterusnya
page Nomor halaman yang diminta Mulai dari 1, bukan 0
per_page Jumlah baris per halaman Misalnya 3 atau 10 baris
Filter status Saring slip menurut kondisi pinjam aktif atau kembali
q Kata kunci pencarian Cocokkan judul buku atau nama anggota

Urutan belajar kita: filter status -> cari dengan q -> baru potong per halaman. Kalau urutan dibalik, hasil bisa salah karena baris yang tidak relevan ikut masuk ke potongan halaman.

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 untuk pengatur kode daftar pinjam.
  • 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 atau membuat pengatur kode. Contoh: notepad app\Http\Controllers\PeminjamanController.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_pagination_filter_pencarian_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 paginate() di Laravel, pemula sering bingung urutan kerja: saring, cari, potong. Maka kita mulai dari array PHP biasa supaya setiap langkah terlihat jelas sebelum dibungkus Eloquent.

<?php
$daftar = [
    ["id" => 100, "judul_buku" => "Dasar PHP", "nama_anggota" => "Budi", "status" => "aktif"],
    ["id" => 101, "judul_buku" => "Belajar Laravel", "nama_anggota" => "Siti", "status" => "kembali"],
    ["id" => 102, "judul_buku" => "Dasar PHP", "nama_anggota" => "Andi", "status" => "aktif"],
    ["id" => 103, "judul_buku" => "Matematika", "nama_anggota" => "Budi", "status" => "kembali"],
    ["id" => 104, "judul_buku" => "Biologi", "nama_anggota" => "Rina", "status" => "aktif"],
    ["id" => 105, "judul_buku" => "Fisika", "nama_anggota" => "Dewi", "status" => "kembali"],
];

Awam: ini daftar gabungan seperti hasil relasi di artikel sebelumnya: tiap baris sudah punya judul_buku, nama_anggota, dan status. Pagination hanya mengatur berapa baris yang ditampilkan dari daftar ini.

Alur daftar — saring, cari, potong

Gerakan yang benar selalu sama:

  1. Saring status — kalau status=aktif, buang slip yang sudah kembali.
  2. Cari dengan q — cocokkan kata kunci ke judul buku atau nama anggota.
  3. Potong per halaman — ambil segenggam baris sesuai page dan per_page.
<?php
// Salin ke file misalnya daftar-saring.php lalu jalankan: php daftar-saring.php
$daftar = [
    ["id" => 100, "judul_buku" => "Dasar PHP", "nama_anggota" => "Budi", "status" => "aktif"],
    ["id" => 101, "judul_buku" => "Belajar Laravel", "nama_anggota" => "Siti", "status" => "kembali"],
    ["id" => 102, "judul_buku" => "Dasar PHP", "nama_anggota" => "Andi", "status" => "aktif"],
    ["id" => 103, "judul_buku" => "Matematika", "nama_anggota" => "Budi", "status" => "kembali"],
    ["id" => 104, "judul_buku" => "Biologi", "nama_anggota" => "Rina", "status" => "aktif"],
    ["id" => 105, "judul_buku" => "Fisika", "nama_anggota" => "Dewi", "status" => "kembali"],
];

$hasil = $daftar;

// 1) saring status
$status = "aktif";
$hasil = array_values(array_filter($hasil, fn ($row) => $row["status"] === $status));

// 2) cari q
$q = "php";
$hasil = array_values(array_filter($hasil, function ($row) use ($q) {
    $needle = mb_strtolower($q);
    return str_contains(mb_strtolower($row["judul_buku"]), $needle)
        || str_contains(mb_strtolower($row["nama_anggota"]), $needle);
}));

// 3) potong halaman
$page = 1;
$perPage = 2;
$offset = ($page - 1) * $perPage;
$potongan = array_slice($hasil, $offset, $perPage);

echo json_encode($potongan, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), PHP_EOL;

Awam — cara menguji bagian ini: salin potongan di atas ke daftar-saring.php, lalu di terminal jalankan php daftar-saring.php. Kalau muncul JSON berisi slip aktif yang judulnya mengandung “php”, urutan saring-cari-potong sudah sehat. Kalau kamu memotong dulu baru menyaring, halaman 2 bisa berisi slip yang seharusnya tidak ikut — urutan saring -> cari -> potong menjaga setiap halaman konsisten.

Saring -> Cari q -> Potong halaman Semua slip daftar panjang Filter status aktif / kembali Cari q judul / nama Halaman page + per_page Jangan potong dulu — saring dan cari baru memotong supaya tiap halaman konsisten.
Urutan yang benar: filter status, lalu pencarian q, baru pagination memotong hasil.

Laravel — cuplikan pagination & filter

Di proyek Laravel, pengatur kode daftar pinjam bisa membaca parameter URL lalu membangun query Eloquent dengan urutan yang sama.

<?php
// Cuplikan Laravel (bukan file mandiri)
// app/Http/Controllers/PeminjamanController.php

$status = request("status");
$q = request("q");
$perPage = (int) request("per_page", 10);

$query = Peminjaman::query()->with(["buku", "anggota"]);

if ($status) {
    $query->where("status", $status);
}

if ($q) {
    $query->where(function ($builder) use ($q) {
        $builder->whereHas("buku", fn ($b) => $b->where("judul", "like", "%{$q}%"))
            ->orWhereHas("anggota", fn ($a) => $a->where("nama", "like", "%{$q}%"));
    });
}

$hasil = $query->paginate($perPage);

Awam: paginate() otomatis menghitung total, halaman aktif, dan potongan data. whereHas memakai relasi dari artikel sebelumnya: cari judul lewat tabel buku, cari nama lewat tabel anggota. Parameter q adalah nama standar di roadmap kita; kata cari kadang dipakai di tutorial lain, tapi di sini kita konsisten dengan q. Cuplikan ini bukan file mandiri — tempel ke PeminjamanController kalau rute daftar pinjam sudah ada. Kalau belum, kuasai demo PHP dulu; urutan saring-cari-potong tetap sama.

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 "http://127.0.0.1:8000/api/peminjaman?status=aktif&q=php&page=1&per_page=3"

Awam: respons JSON dari curl.exe adalah cara cepat melihat apakah filter, pencarian, dan pagination bekerja sebelum membuka browser. Kalau muncul 404, rute daftar pinjam mungkin belum dipasang — itu wajar; fokus dulu ke demo PHP yang sudah jalan di terminal.

Pola Dasar — daftar panjang yang rapi

  1. 1
    Terima parameter URL
    status, q, page, per_page dari pemanggil API.
  2. 2
    Saring status dulu
    Buang slip yang tidak cocok sebelum menghitung halaman.
  3. 3
    Cari dengan q
    Cocokkan judul buku atau nama anggota dari kata kunci.
  4. 4
    Potong per halaman
    Ambil segenggam baris sesuai page dan per_page.
  5. 5
    Kembalikan metadata
    page, per_page, total, dan data supaya pemanggil tahu posisi daftar.
  6. 6
    Tolak halaman rusak
    Kalau page nol atau negatif, jawab 422 — bukan halaman kosong diam-diam.

Kode lengkap — demo mandiri daftar panjang

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

<?php
declare(strict_types=1);

$daftar = [
    ["id" => 100, "judul_buku" => "Dasar PHP", "nama_anggota" => "Budi", "status" => "aktif"],
    ["id" => 101, "judul_buku" => "Belajar Laravel", "nama_anggota" => "Siti", "status" => "kembali"],
    ["id" => 102, "judul_buku" => "Dasar PHP", "nama_anggota" => "Andi", "status" => "aktif"],
    ["id" => 103, "judul_buku" => "Matematika", "nama_anggota" => "Budi", "status" => "kembali"],
    ["id" => 104, "judul_buku" => "Biologi", "nama_anggota" => "Rina", "status" => "aktif"],
    ["id" => 105, "judul_buku" => "Fisika", "nama_anggota" => "Dewi", "status" => "kembali"],
];

function daftarPinjam(
    array $rows,
    ?string $status = null,
    ?string $q = null,
    int $page = 1,
    int $perPage = 3
): array {
    if ($page < 1) {
        return [
            "status" => 422,
            "error" => "Halaman tidak valid",
        ];
    }

    $hasil = $rows;

    if ($status !== null && $status !== "") {
        $hasil = array_values(array_filter($hasil, fn ($row) => $row["status"] === $status));
    }

    if ($q !== null && $q !== "") {
        $needle = mb_strtolower($q);
        $hasil = array_values(array_filter($hasil, function ($row) use ($needle) {
            return str_contains(mb_strtolower($row["judul_buku"]), $needle)
                || str_contains(mb_strtolower($row["nama_anggota"]), $needle);
        }));
    }

    $total = count($hasil);
    $offset = ($page - 1) * $perPage;
    $data = array_slice($hasil, $offset, $perPage);

    return [
        "status" => 200,
        "page" => $page,
        "per_page" => $perPage,
        "total" => $total,
        "data" => $data,
    ];
}

function demo(string $judul, callable $aksi): void
{
    echo "=== {$judul} ===", PHP_EOL;
    echo json_encode($aksi(), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), PHP_EOL, PHP_EOL;
}

demo("Halaman rusak -> 422", function () use ($daftar) {
    return daftarPinjam($daftar, null, null, 0, 3);
});

demo("Status aktif + q php -> 200", function () use ($daftar) {
    return daftarPinjam($daftar, "aktif", "php", 1, 3);
});

demo("Semua status halaman 2 -> 200", function () use ($daftar) {
    return daftarPinjam($daftar, null, null, 2, 3);
});

Awam: tiga skenario di atas menunjukkan pola respons yang wajar: halaman invalid ditolak, filter + pencarian digabung, lalu pagination tanpa filter menampilkan halaman kedua. Fungsi daftarPinjam adalah inti logika; demo(...) hanya membungkus output agar mudah dibaca di terminal.

Kesalahan umum

Gejala Penyebab tipikal Perbaikan awam
Halaman 2 kosong tapi total besar Memotong dulu baru menyaring Ubah urutan: saring -> cari -> potong
Pencarian tidak menemukan nama anggota Hanya mencari di judul buku Cari juga di nama_anggota atau relasi anggota
page=0 mengembalikan data aneh Tidak memvalidasi nomor halaman Kembalikan 422 untuk halaman tidak valid
Filter status diabaikan Parameter URL tidak dibaca di pengatur kode Baca request("status") sebelum membangun query
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. Tambah satu baris baru ke array demo, lalu cek apakah total ikut berubah.
  2. Coba daftarPinjam($daftar, "kembali", "budi", 1, 2) dan jelaskan urutan saring-cari-potong yang terjadi.
  3. Tulis satu kalimat: kenapa q lebih fleksibel daripada hanya filter judul buku?

FAQ singkat

Kenapa tidak langsung policy atau resource?
Karena daftar harus rapi dulu sebelum membahas izin ubah pinjam atau format JSON yang lebih cantik. Artikel ini fokus pada pagination, filter, dan pencarian.

Beda q dan cari?
Keduanya bisa berarti kata kunci pencarian. Di roadmap Seri 5 kita pakai q supaya konsisten di artikel berikutnya.

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

Potongan sintaks diuji di mana?
Langkah tengah (saring-cari-potong) salin ke daftar-saring.php, lalu jalankan php daftar-saring.php. Demo lengkap diuji dengan php laravel_pagination_filter_pencarian_demo.php. Cuplikan Laravel ditempel ke app\Http\Controllers\PeminjamanController.php; kalau rute sudah ada, uji dengan curl.exe di terminal kedua.

Ke mana setelah ini?
Berikutnya alami: Authorization Policy — aturan izin siapa boleh mengubah catatan pinjam.

Kesimpulan

Kamu sudah belajar memotong daftar pinjam panjang dengan urutan yang benar: saring status -> cari dengan q -> potong per halaman. Mulai dari array PHP dulu, lalu pindah ke paginate() dan whereHas di Laravel. Setelah ini, daftar terasa rapi untuk pemanggil API sebelum kita membahas izin.

Seri 5 progress: langkah #65 (ini) · 2/7 Laravel Lanjutan · prasyarat: Relasi Eloquent: Anggota & Peminjaman (#64) LIVE. Berikutnya: Authorization Policy.