Cara Membuat Paging (Pagination) Halaman dengan PHP dan MySQL

| | | PHP
Cara Membuat Paging (Pagination) Halaman dengan PHP dan MySQL

Paging atau pagination adalah teknik membagi data menjadi beberapa halaman agar tidak ditampilkan sekaligus. Ini sangat penting untuk website yang memiliki banyak data, karena menampilkan ratusan atau ribuan data sekaligus akan membuat halaman lambat dan sulit dinavigasi.

Dalam tutorial ini, kita akan membuat pagination dengan PHP dan MySQL dari yang paling sederhana sampai yang lebih advanced dengan AJAX dan infinite scroll.

Kenapa Pagination Penting?

  • Performa —加载少量 data lebih cepat
  • UX — pengguna bisa navigasi dengan mudah
  • SEO — search engine lebih suka halaman yang terstruktur
  • Resource — mengurangi beban server dan database

Persiapan Database

CREATE DATABASE pagination_db;
USE pagination_db;

CREATE TABLE mahasiswa (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nim VARCHAR(12) NOT NULL,
    nama VARCHAR(100) NOT NULL,
    jurusan VARCHAR(50) NOT NULL,
    angkatan INT NOT NULL,
    ipk DECIMAL(3,2) DEFAULT 0.00,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Insert 50 data contoh
INSERT INTO mahasiswa (nim, nama, jurusan, angkatan, ipk) VALUES
('2021001', 'Ahmad Fauzi', 'Teknik Informatika', 2021, 3.45),
('2021002', 'Budi Santoso', 'Sistem Informasi', 2021, 3.20),
('2021003', 'Citra Dewi', 'Teknik Informatika', 2021, 3.78),
('2021004', 'Dian Permata', 'Sistem Informasi', 2021, 3.55),
('2021005', 'Eko Prasetyo', 'Teknik Komputer', 2021, 3.10),
('2021006', 'Fitri Handayani', 'Teknik Informatika', 2021, 3.89),
('2021007', 'Gunawan Wibisono', 'Sistem Informasi', 2021, 2.95),
('2021008', 'Hana Sari', 'Teknik Informatika', 2021, 3.67),
('2021009', 'Irfan Hakim', 'Teknik Komputer', 2021, 3.33),
('2021010', 'Juli Astuti', 'Sistem Informasi', 2021, 3.42),
('2022001', 'Kurniawan', 'Teknik Informatika', 2022, 3.50),
('2022002', 'Lina Marlina', 'Sistem Informasi', 2022, 3.72),
('2022003', 'Muhammad Rizki', 'Teknik Informatika', 2022, 3.15),
('2022004', 'Nadia Putri', 'Teknik Komputer', 2022, 3.88),
('2022005', 'Oscar Firmansyah', 'Sistem Informasi', 2022, 2.80),
('2022006', 'Putri Rahayu', 'Teknik Informatika', 2022, 3.60),
('2022007', 'Qori Aulia', 'Teknik Komputer', 2022, 3.25),
('2022008', 'Rendi Saputra', 'Sistem Informasi', 2022, 3.40),
('2022009', 'Sinta Amelia', 'Teknik Informatika', 2022, 3.75),
('2022010', 'Taufik Hidayat', 'Teknik Komputer', 2022, 3.05);

Metode 1: Pagination Sederhana

Buat file index.php:

<?php
require_once 'config/database.php';

// ===== KONFIGURASI PAGINATION =====
$per_page = 10;                              // Jumlah data per halaman
$halaman_sekarang = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1;
$offset = ($halaman_sekarang - 1) * $per_page;

// ===== AMBIL DATA =====
// Hitung total data
$total_data = $pdo->query('SELECT COUNT(*) FROM mahasiswa')->fetchColumn();

// Hitung total halaman
$total_halaman = ceil($total_data / $per_page);

// Ambil data untuk halaman saat ini
$stmt = $pdo->prepare('SELECT * FROM mahasiswa ORDER BY id DESC LIMIT ? OFFSET ?');
$stmt->bindValue(1, $per_page, PDO::PARAM_INT);
$stmt->bindValue(2, $offset, PDO::PARAM_INT);
$stmt->execute();
$data = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Pagination PHP - Data Mahasiswa</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { font-family: 'Segoe UI', Arial, sans-serif; background: #f3f4f6; padding: 30px; }
        .container { max-width: 900px; margin: 0 auto; }
        h1 { color: #1e293b; margin-bottom: 5px; }
        .info { color: #6b7280; margin-bottom: 20px; }
        table { width: 100%; border-collapse: collapse; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
        th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #e5e7eb; }
        th { background: #1e293b; color: white; font-size: 13px; text-transform: uppercase; }
        tr:hover { background: #f9fafb; }
        .badge { padding: 3px 10px; border-radius: 12px; font-size: 12px; font-weight: 600; }
        .badge-ti { background: #dbeafe; color: #1d4ed8; }
        .badge-si { background: #dcfce7; color: #16a34a; }
        .badge-tk { background: #fef3c7; color: #d97706; }

        /* Pagination */
        .pagination { display: flex; justify-content: center; align-items: center; gap: 5px; margin-top: 25px; flex-wrap: wrap; }
        .pagination a, .pagination span {
            padding: 8px 14px;
            border: 1px solid #d1d5db;
            border-radius: 6px;
            text-decoration: none;
            color: #374151;
            font-size: 14px;
            transition: all 0.2s;
        }
        .pagination a:hover { background: #f3f4f6; border-color: #9ca3af; }
        .pagination .active { background: #3b82f6; color: white; border-color: #3b82f6; }
        .pagination .disabled { color: #d1d5db; pointer-events: none; }
        .page-info { color: #6b7280; font-size: 14px; margin-top: 10px; text-align: center; }
    </style>
</head>
<body>
    <div class="container">
        <h1>Data Mahasiswa</h1>
        <p class="info">Menampilkan <?= count($data) ?> dari <?= $total_data ?> data</p>

        <table>
            <thead>
                <tr>
                    <th>No</th>
                    <th>NIM</th>
                    <th>Nama</th>
                    <th>Jurusan</th>
                    <th>Angkatan</th>
                    <th>IPK</th>
                </tr>
            </thead>
            <tbody>
                <?php if (empty($data)): ?>
                    <tr><td colspan="6" style="text-align:center;padding:30px;color:#6b7280;">Tidak ada data</td></tr>
                <?php else: ?>
                    <?php foreach ($data as $i => $row): ?>
                        <tr>
                            <td><?= $offset + $i + 1 ?></td>
                            <td><?= htmlspecialchars($row['nim']) ?></td>
                            <td><?= htmlspecialchars($row['nama']) ?></td>
                            <td>
                                <?php
                                $badge_class = match(true) {
                                    str_contains($row['jurusan'], 'Informatika') => 'badge-ti',
                                    str_contains($row['jurusan'], 'Informasi') => 'badge-si',
                                    default => 'badge-tk'
                                };
                                ?>
                                <span class="badge <?= $badge_class ?>"><?= htmlspecialchars($row['jurusan']) ?></span>
                            </td>
                            <td><?= $row['angkatan'] ?></td>
                            <td><?= $row['ipk'] ?></td>
                        </tr>
                    <?php endforeach; ?>
                <?php endif; ?>
            </tbody>
        </table>

        <!-- Pagination Links -->
        <?php if ($total_halaman > 1): ?>
        <div class="pagination">
            <!-- Tombol Previous -->
            <?php if ($halaman_sekarang > 1): ?>
                <a href="?page=<?= $halaman_sekarang - 1 ?>">&laquo; Prev</a>
            <?php else: ?>
                <span class="disabled">&laquo; Prev</span>
            <?php endif; ?>

            <!-- Nomor Halaman -->
            <?php
            $start = max(1, $halaman_sekarang - 2);
            $end = min($total_halaman, $halaman_sekarang + 2);

            if ($start > 1): ?>
                <a href="?page=1">1</a>
                <?php if ($start > 2): ?><span>...</span><?php endif; ?>
            <?php endif;

            for ($i = $start; $i <= $end; $i++): ?>
                <?php if ($i === $halaman_sekarang): ?>
                    <span class="active"><?= $i ?></span>
                <?php else: ?>
                    <a href="?page=<?= $i ?>"><?= $i ?></a>
                <?php endif; ?>
            <?php endfor;

            if ($end < $total_halaman): ?>
                <?php if ($end < $total_halaman - 1): ?><span>...</span><?php endif; ?>
                <a href="?page=<?= $total_halaman ?>"><?= $total_halaman ?></a>
            <?php endif; ?>

            <!-- Tombol Next -->
            <?php if ($halaman_sekarang < $total_halaman): ?>
                <a href="?page=<?= $halaman_sekarang + 1 ?>">Next &raquo;</a>
            <?php else: ?>
                <span class="disabled">Next &raquo;</span>
            <?php endif; ?>
        </div>
        <p class="page-info">Halaman <?= $halaman_sekarang ?> dari <?= $total_halaman ?></p>
        <?php endif; ?>
    </div>
</body>
</html>

Penjelasan logika pagination:

  • $per_page = 10 — menampilkan 10 data per halaman
  • $halaman_sekarang — halaman yang sedang aktif, diambil dari URL ?page=X
  • $offset — posisi data pertama yang diambil (halaman 1 = offset 0, halaman 2 = offset 10)
  • LIMIT ? OFFSET ? — query MySQL untuk mengambil data sesuai halaman
  • ceil($total_data / $per_page) — menghitung total halaman dengan pembulatan ke atas
  • Ellipsis (...) — ditampilkan jika ada banyak halaman, supaya tidak terlalu panjang

Metode 2: Pagination dengan Search dan Filter

<?php
require_once 'config/database.php';

// Konfigurasi
$per_page = 10;
$halaman_sekarang = max(1, (int)($_GET['page'] ?? 1));
$offset = ($halaman_sekarang - 1) * $per_page;

// Parameter pencarian
$search = trim($_GET['search'] ?? '');
$jurusan = $_GET['jurusan'] ?? '';

// Build WHERE clause
$where = [];
$params = [];

if ($search) {
    $where[] = '(nama LIKE ? OR nim LIKE ?)';
    $params[] = "%$search%";
    $params[] = "%$search%";
}

if ($jurusan) {
    $where[] = 'jurusan = ?';
    $params[] = $jurusan;
}

$where_clause = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';

// Hitung total
$stmt_count = $pdo->prepare("SELECT COUNT(*) FROM mahasiswa $where_clause");
$stmt_count->execute($params);
$total_data = $stmt_count->fetchColumn();

$total_halaman = ceil($total_data / $per_page);

// Ambil data
$query = "SELECT * FROM mahasiswa $where_clause ORDER BY id DESC LIMIT ? OFFSET ?";
$stmt = $pdo->prepare($query);

$all_params = array_merge($params, [$per_page, $offset]);
foreach ($all_params as $i => $val) {
    $type = is_int($val) ? PDO::PARAM_INT : PDO::PARAM_STR;
    $stmt->bindValue($i + 1, $val, $type);
}
$stmt->execute();
$data = $stmt->fetchAll();

// Query string untuk pagination link
$query_string = http_build_query(array_filter([
    'search' => $search,
    'jurusan' => $jurusan
]));
?>

Dengan cara ini, parameter search dan filter tetap ada saat user pindah halaman.

Metode 3: Pagination dengan AJAX

Untuk pagination tanpa reload halaman, gunakan AJAX:

// File: script.js
function loadHalaman(page) {
    fetch('data.php?page=' + page)
        .then(response => response.json())
        .then(data => {
            // Update tabel
            let html = '';
            data.data.forEach((row, i) => {
                html += `<tr>
                    <td>${data.offset + i + 1}</td>
                    <td>${row.nim}</td>
                    <td>${row.nama}</td>
                    <td>${row.jurusan}</td>
                    <td>${row.ipk}</td>
                </tr>`;
            });
            document.querySelector('tbody').innerHTML = html;

            // Update pagination
            updatePagination(data.total_halaman, page);
        });
}

// Load halaman pertama saat halaman dimuat
document.addEventListener('DOMContentLoaded', () => loadHalaman(1));

Tips Pagination

1. Batasi nomor halaman yang ditampilkan Jangan tampilkan semua nomor jika halamannya banyak. Tampilkan maksimal 5-7 nomor di sekitar halaman aktif.

2. Gunakan URL yang SEO-friendly

// Biasa
?page=3&search=ahmad

// SEO-friendly (dengan .htaccess)
/mahasiswa/page/3?search=ahmad

3. Tambahkan rel=“prev” dan rel=“next” Untuk SEO, tambahkan tag di <head>:

<?php if ($halaman_sekarang > 1): ?>
    <link rel="prev" href="?page=<?= $halaman_sekarang - 1 ?>">
<?php endif; ?>
<?php if ($halaman_sekarang < $total_halaman): ?>
    <link rel="next" href="?page=<?= $halaman_sekarang + 1 ?>">
<?php endif; ?>

4. Cache hasil query Untuk data yang jarang berubah, cache hasil query COUNT untuk menghindari query berulang.

5. Gunakan cursor-based pagination untuk data sangat besar Untuk tabel dengan jutaan baris, OFFSET menjadi lambat. Gunakan cursor-based pagination:

-- Daripada OFFSET (lambat untuk data besar)
SELECT * FROM mahasiswa WHERE id > ? ORDER BY id LIMIT 10

-- Lebih cepat karena menggunakan index

Praktik Terbaik

Beberapa tips saat menulis kode PHP:

  1. Gunakan prepared statement — Hindari SQL injection dengan menggunakan PDO atau MySQLi prepared statement, jangan concatenate query secara langsung.
  2. Validasi input — Selalu validasi dan sanitasi data dari user menggunakan filter_var(), htmlspecialchars(), atau library validasi.
  3. Error reporting — Saat development, aktifkan error_reporting(E_ALL) dan display_errors untuk memudahkan debugging.
  4. Gunakan autoloading — Manfaatkan Composer autoload atau PSR-4 untuk mengelola file class secara otomatis.
  5. Pisahkan logic dan view — Gunakan pola MVC atau minimal pisahkan kode PHP dan HTML agar lebih rapi dan mudah dipelihara.

Dengan menerapkan praktik di atas, kode PHP Anda akan lebih aman, bersih, dan mudah dikembangkan.

Kesimpulan

Cara membuat paging halaman dengan PHP:

  1. Hitung total data dengan COUNT(*)
  2. Hitung total halaman dengan ceil(total / per_page)
  3. Ambil data per halaman dengan LIMIT dan OFFSET
  4. Tampilkan link navigasi dengan nomor halaman, prev, dan next
  5. Pertahankan parameter search/filter di link pagination

Pagination sederhana dengan LIMIT OFFSET sudah cukup untuk sebagian besar kasus. Untuk data yang sangat besar, pertimbangkan cursor-based pagination atau AJAX pagination.

Sigit N avatar
Tentang Sigit N
Full-stack developer & technical writer. Berpengalaman di PHP, Laravel, NodeJS, MySQL, dan Python. Aktif menulis tutorial pemrograman dan maintaining open-source projects di PemburuKode sejak 2021.