Cara Membuat Pagination PHP Tanpa Library Tambahan

Ketika tabel data sudah berisi ratusan baris, menampilkan semuanya sekaligus membuat halaman lambat dan sulit dibaca. Cara membuat pagination PHP yang paling sederhana adalah memakai LIMIT dan OFFSET pada query, lalu menghitung jumlah halaman dari total baris. Semua bisa dilakukan dengan PHP dan MySQL tanpa library tambahan.

Alur yang dipakai sama seperti aplikasi nyata: menghitung total data, menentukan halaman aktif, mengambil potongan data, lalu merender tautan navigasi.

Menghitung total halaman sebelum menampilkan data

Sebelum mengambil data, kamu perlu tahu berapa banyak baris yang tersedia. Query hitung memakai COUNT(*) tanpa LIMIT. Hasilnya dipakai untuk menentukan jumlah halaman.

$perPage = 10;
$sql = "SELECT COUNT(*) AS total FROM produk";
$hasil = $pdo->query($sql)->fetch(PDO::FETCH_ASSOC);
$totalData = (int) $hasil['total'];
$totalHalaman = (int) ceil($totalData / $perPage);

Angka $perPage menentukan berapa baris per halaman. Nilai 10 atau 20 biasanya cukup; jika barisnya pendek, 25 masih nyaman dibaca. Fungsi ceil() membulatkan ke atas supaya sisa baris tetap punya halaman sendiri. Kalau total data nol, $totalHalaman bernilai 0 dan navigasi tidak perlu ditampilkan sama sekali.

Menentukan halaman aktif dari parameter URL

Nomor halaman datang dari query string, misalnya ?page=3. Nilai ini berasal dari pengguna, jadi harus dibersihkan sebelum masuk ke query. Konversi ke integer lalu kunci pada rentang yang valid.

$page = isset($_GET['page']) ? (int) $_GET['page'] : 1;

if ($page < 1) {
    $page = 1;
}
if ($totalHalaman > 0 && $page > $totalHalaman) {
    $page = $totalHalaman;
}

$offset = ($page - 1) * $perPage;

Pengecekan ini mencegah offset negatif dan mencegah permintaan halaman yang tidak ada. Kalau pengguna mengetik ?page=999 padahal hanya ada 5 halaman, dia akan diarahkan ke halaman terakhir, bukan melihat tabel kosong.

Mengambil data per halaman dengan LIMIT dan OFFSET

Query utama menambahkan LIMIT dan OFFSET. Karena keduanya sudah berupa integer, kamu bisa memasukkannya langsung setelah casting, atau mengikatnya dengan bindValue() bertipe PDO::PARAM_INT.

$stmt = $pdo->prepare(
    "SELECT id, nama, harga FROM produk ORDER BY id ASC LIMIT :limit OFFSET :offset"
);
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$produk = $stmt->fetchAll(PDO::FETCH_ASSOC);

Selalu sertakan ORDER BY yang pasti. Tanpa urutan tetap, MySQL bisa mengembalikan baris dengan urutan berbeda di tiap query, sehingga satu baris muncul dua kali di halaman berbeda sementara baris lain hilang. Kolom id atau tanggal biasanya jadi pilihan aman.

Kalau kamu baru mulai membangun alur data, dasar mengambil dan menampilkan baris dibahas di cara menampilkan data dari database ke HTML dengan PHP. Setelah itu, pagination tinggal menambahkan pembatas jumlah baris.

Merender navigasi halaman

Navigasi cukup berisi tautan ke halaman sebelumnya, nomor halaman, dan halaman berikutnya. Simpan nomor halaman aktif dengan class khusus supaya bisa diberi gaya berbeda.

if ($totalHalaman > 1) {
    echo '<nav class="pagination">';

    if ($page > 1) {
        echo '<a href="?page=' . ($page - 1) . '">&laquo; Sebelumnya</a>';
    }

    for ($i = 1; $i <= $totalHalaman; $i++) {
        $kelas = ($i === $page) ? ' class="aktif"' : '';
        echo '<a href="?page=' . $i . '"' . $kelas . '>' . $i . '</a>';
    }

    if ($page < $totalHalaman) {
        echo '<a href="?page=' . ($page + 1) . '">Berikutnya &raquo;</a>';
    }

    echo '</nav>';
}

Perhatikan bahwa output tautan memakai htmlspecialchars() bila ada parameter lain yang ikut dibawa. Kalau halaman punya filter pencarian, sertakan parameternya di setiap tautan, misalnya ?page=2&cari=kopi. Tanpa itu, filter akan hilang setiap kali pengguna pindah halaman.

Memotong nomor halaman yang terlalu banyak

Kalau data mencapai ribuan baris, menampilkan 100 nomor halaman berturut-turut merusak tampilan. Tampilkan jendela kecil di sekitar halaman aktif, misalnya dua nomor ke kiri dan dua ke kanan, lalu tambahkan elipsis di ujungnya.

  • Hitung batas awal: $mulai = max(1, $page - 2).
  • Hitung batas akhir: $akhir = min($totalHalaman, $page + 2).
  • Loop hanya dari $mulai sampai $akhir.
  • Tambahkan tautan ke halaman 1 dan halaman terakhir di luar jendela itu.

Menyesuaikan query saat ada pencarian

Pagination hampir selalu berdampingan dengan filter. Struktur yang rapi: susun klausa WHERE sekali, pakai untuk query hitung dan query data, lalu kirim parameter yang sama ke keduanya.

$where = '';
$params = [];

if (!empty($_GET['cari'])) {
    $where = ' WHERE nama LIKE :cari';
    $params[':cari'] = '%' . $_GET['cari'] . '%';
}

$totalData = (int) $pdo
    ->prepare("SELECT COUNT(*) FROM produk" . $where)
    ->execute($params) ? $pdo->query('SELECT FOUND_ROWS()')->fetchColumn() : 0;

Potongan di atas hanya contoh arah; pada praktiknya lebih bersih menjalankan statement yang sudah di-prepare, memanggil execute($params), lalu fetchColumn() dari statement yang sama. Yang penting, klausa WHERE dan nilainya identik antara query hitung dan query data. Kalau berbeda sedikit saja, jumlah halaman tidak akan cocok dengan isi tabel.

Untuk alur form yang mengirim data pencarian, teknik validasi dan pengiriman input dibahas lebih lengkap di cara mengirim data form ke database dengan PHP dan validasi.

Kesalahan yang sering terjadi

Beberapa masalah muncul berulang saat orang pertama kali menulis pagination:

  1. Offset negatif karena ?page=0 tidak dicek. Selalu kunci nilai minimum ke 1.
  2. Data dobel atau hilang karena query tanpa ORDER BY yang unik.
  3. Parameter filter hilang saat berpindah halaman karena tautan hanya membawa page.
  4. Query hitung dan query data tidak sinkron, biasanya karena WHERE ditulis dua kali dengan isi berbeda.
  5. LIMIT diikat sebagai string sehingga MySQL menolak atau mengabaikannya. Gunakan PDO::PARAM_INT.

Untuk tabel yang sangat besar, OFFSET besar juga mulai lambat karena MySQL tetap membaca baris yang dilewati. Solusinya memakai keyset pagination, yaitu mengambil data setelah id terakhir di halaman sebelumnya (WHERE id > :lastId ORDER BY id LIMIT 10). Cara ini tidak mendukung lompat ke halaman tertentu, jadi pilih sesuai kebutuhan.

Ringkasan

Pagination PHP terdiri dari empat langkah: hitung total baris, tentukan halaman aktif dari URL dengan batas yang aman, ambil data memakai LIMIT dan OFFSET dengan ORDER BY yang pasti, lalu render navigasi sambil membawa parameter filter. Semua langkah memakai PDO dan fungsi bawaan PHP, tanpa dependensi tambahan.

Pertanyaan yang Sering Diajukan

Apakah pagination harus memakai AJAX?

Tidak. Tautan biasa dengan query string sudah cukup dan lebih sederhana. AJAX berguna kalau kamu ingin tabel berubah tanpa memuat ulang halaman, tetapi logika hitung halaman dan LIMIT OFFSET tetap sama.

Berapa jumlah baris per halaman yang ideal?

Tergantung kepadatan data. Untuk daftar produk atau artikel, 10 sampai 20 baris biasanya nyaman. Untuk data yang hanya satu kolom, 50 baris masih terbaca. Ukur waktu muat halaman, jangan menebak.

Bagaimana cara memberi nomor urut yang berlanjut antar halaman?

Gunakan offset sebagai dasar: $no = $offset + 1, lalu tambah satu di setiap iterasi loop. Dengan begitu halaman kedua mulai dari nomor 11 jika per halaman berisi 10 baris.