Урок 31: Пагинация на PHP

В 31 уроке мы разберем, как реализовать пагинацию на PHP. Пагинация это механизм разделения данных на страницы. Представьте, у вас есть таблица с 1000 записей. Выводить все разом, плохая идея. Это замедлит загрузку страницы, перегрузит сервер и запутает пользователя. Пагинация решает эту проблему, разбивая данные на «порции» и позволяя переключаться между ними.

В этом уроке мы научимся:

  1. Рассчитывать общее количество страниц.
  2. Работать с параметрами LIMIT и OFFSET в SQL.
  3. Создавать навигационные ссылки для переключения страниц.
  4. Защищаться от некорректных значений.

Как работает пагинация?

Основные шаги:

  1. Получить общее количество записей из базы данных.
  2. Определить, сколько элементов показывать на странице (например, 10).
  3. Рассчитать общее количество страниц:
    $total_pages = ceil($total_records / $records_per_page);
  4. Получить номер текущей страницы из URL (например, ?page=2).
  5. Выбрать данные для текущей страницы с помощью SQL-запроса с LIMIT и OFFSET.

Реализация пагинации

Шаг 1. Подключение к базе данных

Для начала подключимся к базе данных. Я использую MySQLi, но вы можете выбрать PDO.

$servername = "localhost";
$username = "root";
$password = "";
$dbname = "test_db";

$conn = new mysqli($servername, $username, $password, $dbname);

if ($conn->connect_error) {
    die("Ошибка подключения: " . $conn->connect_error);
}

Шаг 2. Получаем общее количество записей

$sql = "SELECT COUNT(id) AS total FROM products";
$result = $conn->query($sql);
$row = $result->fetch_assoc();
$total_records = $row['total']; // Общее количество товаров

Шаг 3. Настраиваем пагинацию

$records_per_page = 10; // Товаров на странице
$current_page = isset($_GET['page']) ? (int)$_GET['page'] : 1; // Текущая страница

// Защита от отрицательных значений и превышения общего числа страниц
if ($current_page < 1) {
    $current_page = 1;
}

$total_pages = ceil($total_records / $records_per_page);

if ($current_page > $total_pages) {
    $current_page = $total_pages;
}

// Расчет OFFSET для SQL-запроса
$offset = ($current_page - 1) * $records_per_page;

Шаг 4. Запрос данных для текущей страницы

$sql = "SELECT * FROM products LIMIT $records_per_page OFFSET $offset";
$result = $conn->query($sql);

// Вывод данных
while ($row = $result->fetch_assoc()) {
    echo "<div>{$row['name']} - {$row['price']} руб.</div>";
}

Шаг 5. Вывод навигации

Создадим ссылки для переключения страниц:

echo "<div class='pagination'>";

// Кнопка "Назад"
if ($current_page > 1) {
    echo "<a href='?page=" . ($current_page - 1) . "'>Назад</a>";
}

// Нумерация страниц
for ($i = 1; $i <= $total_pages; $i++) {
    $active = ($i == $current_page) ? " class='active'" : "";
    echo "<a href='?page=$i'$active>$i</a>";
}

// Кнопка "Вперед"
if ($current_page < $total_pages) {
    echo "<a href='?page=" . ($current_page + 1) . "'>Вперед</a>";
}

echo "</div>";

Дополнительные советы

Защита от SQL-инъекций

Всегда проверяйте и фильтруйте входные данные. Например, номер страницы должен быть целым числом:

$current_page = filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT, ['options' => ['default' => 1, 'min_range' => 1]]);

Стилизация пагинации

Добавьте CSS, чтобы навигация выглядела привлекательно:

.pagination a {
    display: inline-block;
    padding: 8px 16px;
    margin: 0 4px;
    border: 1px solid #ddd;
    text-decoration: none;
    color: #333;
}

.pagination a.active {
    background-color: #007bff;
    color: white;
    border-color: #007bff;
}

Практические задачи

Задача 1. Реализуйте пагинацию для таблицы пользователей.

  • Показывать по 5 пользователей на странице.
  • Добавьте кнопки «В начало» и «В конец».

Решение для задачи 1:

// Кнопка "В начало"
if ($current_page > 1) {
    echo "<a href='?page=1'>В начало</a>";
}

// ... остальной код ...

// Кнопка "В конец"
if ($current_page < $total_pages) {
    echo "<a href='?page=$total_pages'>В конец</a>";
}

Задача 2. Модифицируйте код так, чтобы при отсутствии записей выводилось сообщение «Данные не найдены».

Пример полного кода

<?php
// Подключение к базе данных
$conn = new mysqli("localhost", "root", "", "test_db");

// Получение общего количества записей
$result = $conn->query("SELECT COUNT(id) AS total FROM products");
$total_records = $result->fetch_assoc()['total'];

// Настройки пагинации
$records_per_page = 10;
$current_page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1;
$total_pages = ceil($total_records / $records_per_page);

// Корректировка текущей страницы
if ($current_page > $total_pages) {
    $current_page = $total_pages;
}

// Запрос данных
$offset = ($current_page - 1) * $records_per_page;
$result = $conn->query("SELECT * FROM products LIMIT $records_per_page OFFSET $offset");

// Вывод данных
if ($result->num_rows > 0) {
    while ($row = $result->fetch_assoc()) {
        echo "<div>{$row['name']}</div>";
    }
} else {
    echo "Данные не найдены.";
}

// Пагинация
echo "<div class='pagination'>";
if ($current_page > 1) {
    echo "<a href='?page=1'>В начало</a>";
    echo "<a href='?page=" . ($current_page - 1) . "'>Назад</a>";
}

for ($i = 1; $i <= $total_pages; $i++) {
    $active = ($i == $current_page) ? " class='active'" : "";
    echo "<a href='?page=$i'$active>$i</a>";
}

if ($current_page < $total_pages) {
    echo "<a href='?page=" . ($current_page + 1) . "'>Вперед</a>";
    echo "<a href='?page=$total_pages'>В конец</a>";
}
echo "</div>";

$conn->close();
?>

Попробуйте усложнить задачи:

  • Добавьте AJAX-подгрузку данных без перезагрузки страницы.
  • Реализуйте пагинацию с использованием паттерна MVC.

Не забывайте тестировать код при разных сценариях, когда записей нет, когда страница некорректная и т.д.

Полный курс с уроками по PHP для начинающих доступен здесь: https://max-gabov.ru/php-dlya-nachinaushih

Не стесняйтесь задавать вопросы в комментариях к курсу.