Как реализовать вторую страницу в PHP пагинации
Постраничная навигация: переход на вторую страницу
Вопрос: Как обработать запрос второй страницы с помощью LIMIT и OFFSET?
Основной метод использует SQL параметры LIMIT и OFFSET. Для второй страницы с 10 записями на страницу смещение (offset) = (page - 1) * per_page = (2-1)*10 = 10. Пример кода:
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$perPage = 10;
$offset = ($page - 1) * $perPage;
$sql = "SELECT * FROM articles LIMIT $perPage OFFSET $offset";
Важно проверять, что $page положительное целое. Иначе может возникнуть ошибка SQL или пустой результат.
Проблема: отрицательное значение page
Если передать page=0 или отрицательное, offset станет отрицательным, что вызовет ошибку. Решение: принудительно устанавливать page=1, если ввод некорректен, например, через max(1, (int)$_GET['page']).
Ошибка: SQL инъекция при прямом встраивании переменной
Используйте подготовленные запросы PDO или безопасное приведение типов. Пример с PDO:
$stmt = $pdo->prepare("SELECT * FROM articles LIMIT :limit OFFSET :offset");
$stmt->execute(['limit' => $perPage, 'offset' => $offset]);
Вопрос: Как реализовать пагинацию без SQL, используя массив данных?
Для данных в памяти (например, из файла или сессии) применяется array_slice:
$items = range(1, 100); // 100 элементов
$page = max(1, (int)$_GET['page']);
$perPage = 10;
$offset = ($page - 1) * $perPage;
$pageItems = array_slice($items, $offset, $perPage);
Проблема: пустой массив на несуществующей странице
Если offset больше длины массива, array_slice вернет пустой массив. Нужно проверять, что offset меньше count($items), иначе устанавливать page=последняя страница.
Вопрос: Как сделать переход на вторую страницу через POST запрос?
Иногда пагинацию реализуют через POST (например, для формы с фильтрами). Но это менее распространено из-за проблем с историей браузера. Пример:
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$page = isset($_POST['page']) ? (int)$_POST['page'] : 1;
// дальнейшая обработка
}
Сложность: нельзя поделиться ссылкой на конкретную страницу. Рекомендуется использовать GET для пагинации.
Вопрос: Как реализовать ЧПУ для второй страницы (например, /page/2)?
С помощью URL rewriting (mod_rewrite) и разбора URL. Внутри скрипта извлекаем сегмент:
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
preg_match('/\/page\/(\d+)/', $path, $matches);
$page = isset($matches[1]) ? (int)$matches[1] : 1;
Правило .htaccess:
RewriteEngine On
RewriteRule ^page/([0-9]+)$ index.php?page=$1 [L,QSA]
Ошибка: конфликт с другими маршрутами. Решение: добавлять условие RewriteCond для исключения файлов.
Вопрос: Как подгрузить вторую страницу через AJAX без перезагрузки?
Используем JavaScript Fetch API, передавая page параметр. Пример на PHP:
if (isset($_GET['ajax']) && $_GET['ajax'] === '1') {
$page = (int)$_GET['page'];
$data = getPageData($page); // ваша функция
echo json_encode($data);
exit;
}
Проблема: дублирование кода для AJAX и обычного рендера. Решение: выделить логику получения данных в отдельную функцию.
Цели использования каждого варианта:
- LIMIT/OFFSET - для баз данных (MySQL, PostgreSQL), наиболее производительно при правильных индексах.
- array_slice - для небольших массивов, конфигов, тестовых данных.
- POST - редко, только если необходимо скрыть номер страницы или строго внутри формы.
- ЧПУ - для SEO, улучшения читаемости URL.
- AJAX - для динамических интерфейсов, SPA, ленты новостей.
Расширенные примеры реализации второй страницы
Полный скрипт пагинации с безопасными подготовленными запросами и генерацией ссылок:
// config.php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$perPage = 10;
// index.php
require 'config.php';
$page = max(1, (int)($_GET['page'] ?? 1));
$offset = ($page - 1) * $perPage;
// Получаем общее количество записей
$total = $pdo->query("SELECT COUNT(*) FROM articles")->fetchColumn();
$totalPages = ceil($total / $perPage);
// Проверяем границы
if ($page > $totalPages) $page = $totalPages;
$offset = ($page - 1) * $perPage;
$stmt = $pdo->prepare("SELECT * FROM articles ORDER BY id DESC LIMIT :limit OFFSET :offset");
$stmt->execute(['limit' => $perPage, 'offset' => $offset]);
$articles = $stmt->fetchAll();
// Вывод ссылок
for ($i = 1; $i <= $totalPages; $i++) {
$active = ($i == $page) ? 'class="fw-bold"' : '';
echo "$i ";
}
// Результат (для примера)
?>
(ссылки: 1 2 3 ... 10, активна страница 2, если page=2)
Обработка нескольких GET параметров (фильтры + пагинация):
$params = $_GET;
$page = max(1, (int)($params['page'] ?? 1));
unset($params['page']); // удаляем page, чтобы не дублировать
$queryString = http_build_query($params);
// Формируем ссылку с сохранением фильтров
$link = "?$queryString&page=";
// Генерация ссылок пагинации
for ($i = 1; $i <= $totalPages; $i++) {
$active = ($i == $page) ? ' class="fw-bold"' : '';
echo "$i ";
}
// Результат:
?category=php&page=2 (пример ссылки с фильтром)
Пагинация с использованием библиотеки Pagerfanta (популярное готовое решение):
use Pagerfanta\Pagerfanta;
use Pagerfanta\Adapter\ArrayAdapter;
$allItems = range(1, 100);
$adapter = new ArrayAdapter($allItems);
$pagerfanta = new Pagerfanta($adapter);
$pagerfanta->setMaxPerPage(10);
$pagerfanta->setCurrentPage($page);
foreach ($pagerfanta->getCurrentPageResults() as $item) {
echo "$item ";
}
echo $pagerfanta->renderer()->render($pagerfanta);
// Результат:
11 12 13 ... 20 (если вторая страница, по 10 элементов)
[1] [2] [3] ... [10] (навигация)
Пример с AJAX (jQuery) и PHP:
// PHP (page_data.php)
$page = (int)($_GET['page']);
$perPage = 10;
$offset = ($page - 1) * $perPage;
$data = [
'items' => range($offset+1, $offset+$perPage),
'page' => $page
];
header('Content-Type: application/json');
echo json_encode($data);
// JavaScript
fetch('page_data.php?page=2')
.then(response => response.json())
.then(data => {
console.log('Элементы второй страницы:', data.items);
});
// Результат в консоли:
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Распространенная ошибка: забыть привести page к целому числу, что может привести к типам данных с плавающей точкой. Проверка:
$page = (int)$_GET['page']; // если значение не число, будет 0
Проблема: символьная строка в параметре page (например, page=abc)
Решение: использовать filter_var с FILTER_VALIDATE_INT или ctype_digit.