Как реализовать вторую страницу в PHP пагинации

Раздел: Веб-программирование на 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.

Вторая страница PHP - comments

En
Php page 2 (php)