Unit 13 · E-commerce and Web-Related Components

13.3Pagination

Showing 5000 products on one page is slow and unusable. Pagination splits results into pages using SQL LIMIT and OFFSET.

1234567891011121314151617181920page 1LIMIT 8 OFFSET 0page 2LIMIT 8 OFFSET 8page 3LIMIT 8 OFFSET 1620 matching products, 8 per page → ceil(20 / 8) = 3 pagesOFFSET = (page − 1) × perPage
Pagination: each page skips OFFSET rows and then takes LIMIT rows

products.php:

PHP
<?php
require 'config/db.php';

$perPage = 8;
$page    = max(1, (int)($_GET['page'] ?? 1));
$q       = trim($_GET['q'] ?? '');
$like    = '%' . $q . '%';

// 1. How many rows in total? (needed to know the number of pages)
$stmt = $pdo->prepare('SELECT COUNT(*) FROM products WHERE name LIKE ?');
$stmt->execute([$like]);
$totalRows  = (int)$stmt->fetchColumn();
$totalPages = max(1, (int)ceil($totalRows / $perPage));
$page       = min($page, $totalPages);
$offset     = ($page - 1) * $perPage;

// 2. Fetch only the rows for this page. LIMIT values are integers we computed, not user text.
$stmt = $pdo->prepare('SELECT * FROM products WHERE name LIKE ? ORDER BY id DESC LIMIT ' . $perPage . ' OFFSET ' . $offset);
$stmt->execute([$like]);
$products = $stmt->fetchAll();
?>

<!-- 3. Display products ... then the pager -->
<nav>
  <ul class='pagination'>
    <?php if ($page > 1): ?>
      <li class='page-item'><a class='page-link' href='?page=<?= $page - 1 ?>&q=<?= urlencode($q) ?>'>Previous</a></li>
    <?php endif; ?>

    <?php for ($i = 1; $i <= $totalPages; $i++): ?>
      <li class='page-item <?= $i === $page ? 'active' : '' ?>'>
        <a class='page-link' href='?page=<?= $i ?>&q=<?= urlencode($q) ?>'><?= $i ?></a>
      </li>
    <?php endfor; ?>

    <?php if ($page < $totalPages): ?>
      <li class='page-item'><a class='page-link' href='?page=<?= $page + 1 ?>&q=<?= urlencode($q) ?>'>Next</a></li>
    <?php endif; ?>
  </ul>
</nav>

Points to note: validate page as an integer; keep search and filter values in every page link; for very many pages show a window of numbers (current page plus or minus 2) with First and Last links; large tables perform better with keyset pagination (WHERE id < :lastId ORDER BY id DESC LIMIT 8), and infinite scroll or a Load more button uses the same query via AJAX.