Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Постраничный вывод

В таблице могут лежать тысячи записей, но на странице показывают десять-двадцать. Остальные доступны по ссылкам «2», «3», «Далее». Для этого из базы запрашивают не всё, а одну порцию.

Примеры используют базу SQLite в памяти и здесь не запускаются — выполните их у себя.

LIMIT и OFFSET

LIMIT задаёт, сколько строк вернуть, OFFSET — сколько пропустить:

<?php
 
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE posts (id INTEGER PRIMARY KEY, title TEXT)');
 
for ($i = 1; $i <= 25; $i++) {
    $pdo->exec("INSERT INTO posts (title) VALUES ('Статья $i')");
}
 
$titles = $pdo->query('SELECT title FROM posts ORDER BY id LIMIT 3 OFFSET 0')
    ->fetchAll(PDO::FETCH_COLUMN);
 
echo implode(', ', $titles); // Статья 1, Статья 2, Статья 3

Смещение по номеру страницы

Чтобы получить нужную страницу, смещение считают по формуле: номер страницы минус один, умноженный на размер страницы:

<?php
 
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE posts (id INTEGER PRIMARY KEY, title TEXT)');
 
for ($i = 1; $i <= 25; $i++) {
    $pdo->exec("INSERT INTO posts (title) VALUES ('Статья $i')");
}
 
$page = 3;
$perPage = 10;
$offset = ($page - 1) * $perPage;
 
$statement = $pdo->prepare('SELECT title FROM posts ORDER BY id LIMIT :limit OFFSET :offset');
$statement->bindValue('limit', $perPage, PDO::PARAM_INT);
$statement->bindValue('offset', $offset, PDO::PARAM_INT);
$statement->execute();
 
echo implode(', ', $statement->fetchAll(PDO::FETCH_COLUMN));

На третьей странице окажутся статьи с 21-й по 25-ю.

Сколько всего страниц

Общее число записей делят на размер страницы и округляют вверх:

<?php
 
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE posts (id INTEGER PRIMARY KEY, title TEXT)');
 
for ($i = 1; $i <= 25; $i++) {
    $pdo->exec("INSERT INTO posts (title) VALUES ('Статья $i')");
}
 
$total = (int) $pdo->query('SELECT COUNT(*) FROM posts')->fetchColumn();
$pages = (int) ceil($total / 10);
 
echo "Записей: $total, страниц: $pages"; // Записей: 25, страниц: 3

Номер страницы из адреса

Номер приходит в адресе: /posts.php?page=2. Его приводят к числу и ограничивают:

<?php
// posts.php
 
$page = max(1, (int) ($_GET['page'] ?? 1));
$page = min($page, $pages);

Теперь page=abc, page=-5 и page=9999 не сломают запрос.

Ссылки

<?php
 
function pageLinks(int $current, int $pages): string
{
    $links = [];
 
    for ($page = 1; $page <= $pages; $page++) {
        $links[] = $page === $current
            ? "<b>$page</b>"
            : "<a href=\"?page=$page\">$page</a>";
    }
 
    return implode(' ', $links);
}
 
echo pageLinks(2, 3);

Текущая страница выводится жирным текстом, остальные — ссылками.

Большие таблицы

При большом смещении база всё равно читает пропускаемые строки: OFFSET 100000 работает медленно. Для длинных лент используют другой приём — запоминают идентификатор последней показанной записи и просят следующие за ней:

SELECT id, title FROM posts WHERE id > 120 ORDER BY id LIMIT 10;

Так устроена кнопка «Показать ещё».

Частая ошибка

В MySQL значения, переданные через execute([$perPage, $offset]), подставляются как строки, и запрос LIMIT '10' заканчивается ошибкой. Для LIMIT и OFFSET используйте bindValue() с PDO::PARAM_INT. И всегда указывайте ORDER BY: без сортировки порядок строк не определён, и запись может попасть на две страницы или не попасть ни на одну.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.