Методы расчета страниц в PHP: от простых до продвинутых
Подсчет числа страниц в PHP: практические решения
Основной способ вычисления количества страниц
Для постраничного вывода данных чаще всего используется простая формула: общее количество записей делится на количество записей на одной странице, а результат округляется вверх до целого числа. Это гарантирует, что для оставшихся записей будет создана дополнительная страница.
<?php
$totalItems = 150; // общее число записей
$perPage = 20; // записей на страницу
$totalPages = (int)ceil($totalItems / $perPage); // 8
echo $totalPages; // 8
?>
Php сколько страниц (количество страниц php)
8
Функция ceil() поднимает дробное значение до ближайшего целого вверх. Если бы использовалось обычное деление без округления, результат был бы 7.5, и страница 8 пропала бы.
Типичные ошибки: забыть проверить $totalItems на ноль. Если записей нет, ceil(0 / 20) вернёт 0, что корректно, но некоторые разработчики вручную присваивают 1, создавая пустую страницу. Решение – использовать $totalPages = max(1, (int)ceil($totalItems / $perPage)); если нужно гарантировать хотя бы одну страницу.
Другая ошибка: деление на ноль при $perPage = 0. Обязательно проверять или использовать $perPage > 0.
Случаи применения:
- Любая постраничная навигация (списки товаров, статьи, комментарии).
- Вычисление количества страниц для отчётов или экспорта данных.
- Динамическое построение URL вида /page/{N}.
Как обработать случай, когда общее количество записей равно нулю?
Если записей нет, можно отобразить сообщение об отсутствии данных, а пагинацию скрыть. При этом $totalPages должен быть равен 0 или 1? Выбор зависит от логики интерфейса. Пример проверки:
<?php
$totalPages = 0;
if ($totalItems > 0) {
$totalPages = (int)ceil($totalItems / $perPage);
}
?>
В шаблоне можно вывести «Нет записей», если $totalPages === 0.
Проблема: некоторые ORM возвращают 0 для COUNT, даже если таблица пуста. Это нормально. Важно не принудительно устанавливать $totalPages = 1 при нулевых данных – это создаст пустую страницу, что дезориентирует пользователя.
Как подсчитать страницы, используя SQL запрос с COUNT?
Классический подход – сначала получить общее количество записей через COUNT(*), затем применить формулу. Это универсально для любых СУБД.
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->query('SELECT COUNT(*) FROM products');
$totalItems = (int)$stmt->fetchColumn();
$perPage = 10;
$totalPages = (int)ceil($totalItems / $perPage);
echo $totalPages;
?>
15 (при 150 записях)
Для пагинации с условиями (например, категория) используйте параметризованный запрос:
<?php
$categoryId = 5;
$stmt = $pdo->prepare('SELECT COUNT(*) FROM products WHERE category_id = ?');
$stmt->execute([$categoryId]);
$totalItems = (int)$stmt->fetchColumn();
?>
Типичная ошибка: выполнять SELECT COUNT(*) на каждой странице без кеширования. Для больших таблиц это создаёт лишнюю нагрузку. Решение – кешировать результат на короткое время (например, через Memcached или Redis).
Как вычислить число страниц, если навигация строится по смещению (OFFSET) и лимиту?
Часто в SQL используется LIMIT $perPage OFFSET $offset. Количество страниц при этом рассчитывается так же, но $offset = ($currentPage - 1) * $perPage. Важно проверять, чтобы номер текущей страницы не превышал общее количество страниц.
<?php
$totalItems = 285;
$perPage = 25;
$totalPages = (int)ceil($totalItems / $perPage); // 12
$currentPage = min($_GET['page'] ?? 1, $totalPages);
$offset = ($currentPage - 1) * $perPage;
echo "Страница $currentPage из $totalPages, смещение: $offset";
?>
Страница 1 из 12, смещение: 0
Проблема: если входной параметр page меньше 1 или нечисловой, возможно возникновение ошибок. Решение – приводить к целому и ограничивать диапазоном от 1 до $totalPages.
Как рассчитать количество страниц для пагинации с динамическим изменением размера страницы?
Иногда пользователь может выбрать количество записей на странице (10, 20, 50). Тогда $perPage – переменная, и общее число страниц пересчитывается.
<?php
$totalItems = 200;
$perPage = (int)($_GET['per_page'] ?? 20);
$allowed = [10, 20, 50];
if (!in_array($perPage, $allowed)) $perPage = 20;
$totalPages = (int)ceil($totalItems / $perPage);
echo $totalPages;
?>
10 (если выбрано 20), 20 (если 10), 4 (если 50)
Ошибка: не проверять допустимые значения $perPage. Пользователь может передать 0 или отрицательное число – это приведёт к делению на ноль или некорректному результату. Решение – явно задать список разрешённых значений и использовать значение по умолчанию.
Расширенные примеры подсчёта страниц в PHP
Пример 1: Класс для пагинации с кешированием общего числа записей
<?php
class Paginator {
private int $totalItems;
private int $perPage;
private int $totalPages;
private $cache;
public function __construct(int $totalItems, int $perPage, $cache = null) {
$this->totalItems = $totalItems;
$this->perPage = $perPage;
$this->cache = $cache;
$this->totalPages = $this->calculatePages();
}
private function calculatePages(): int {
if ($this->perPage <= 0) {
throw new InvalidArgumentException("perPage must be positive");
}
return (int)ceil($this->totalItems / $this->perPage);
}
public function getPages(): int {
return $this->totalPages;
}
public function getOffset(int $currentPage): int {
$page = max(1, min($currentPage, $this->totalPages));
return ($page - 1) * $this->perPage;
}
public function getPageNumbers(int $range = 2): array {
// Возвращает номера страниц для отображения с многоточием
$pages = [];
$total = $this->totalPages;
for ($i = 1; $i <= $total; $i++) {
if ($i == 1 || $i == $total || abs($i - $total) <= $range || abs($i - 1) <= $range) {
$pages[] = $i;
} elseif (end($pages) !== '...') {
$pages[] = '...';
}
}
return $pages;
}
}
// Использование
$items = 500;
$perPage = 20;
$paginator = new Paginator($items, $perPage);
echo $paginator->getPages(); // 25
?>
25
Пример 2: Подсчёт страниц в результатах поиска с фильтрами (использование подзапроса)
<?php
$search = 'ноутбук';
$stmt = $pdo->prepare("
SELECT COUNT(*)
FROM products
WHERE MATCH(name, description) AGAINST(:search IN BOOLEAN MODE)
");
$stmt->execute([':search' => $search]);
$totalItems = (int)$stmt->fetchColumn();
$perPage = 15;
$totalPages = (int)ceil($totalItems / $perPage);
echo "Найдено $totalItems товаров, страниц: $totalPages";
?>
Найдено 123 товаров, страниц: 9
Пример 3: Вычисление числа страниц с кешированием в Redis
<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$perPage = 20;
$cacheKey = 'total_items:products';
$totalItems = $redis->get($cacheKey);
if ($totalItems === false) {
$stmt = $pdo->query('SELECT COUNT(*) FROM products');
$totalItems = (int)$stmt->fetchColumn();
$redis->setex($cacheKey, 300, $totalItems); // кеш на 5 минут
}
$totalPages = (int)ceil($totalItems / $perPage);
echo $totalPages;
?>
85 (при 1700 записях)
Пример 4: Пагинация с AJAX и динамическим обновлением панели страниц
<?php
// server.php – возвращает JSON с данными и числом страниц
$page = (int)($_GET['page'] ?? 1);
$perPage = 10;
$stmt = $pdo->query('SELECT COUNT(*) FROM comments');
$totalItems = (int)$stmt->fetchColumn();
$totalPages = (int)ceil($totalItems / $perPage);
$stmt = $pdo->prepare('SELECT * FROM comments LIMIT ? OFFSET ?');
$stmt->execute([$perPage, ($page-1)*$perPage]);
$comments = $stmt->fetchAll();
echo json_encode([
'totalPages' => $totalPages,
'currentPage' => $page,
'comments' => $comments
]);
?>
{"totalPages":23,"currentPage":1,"comments":[...]}
Пример 5: Обработка дробных значений и корректировка при нестандартном лимите
<?php
$items = 0;
$perPage = 15;
$totalPages = $items > 0 ? (int)ceil($items / $perPage) : 0;
echo $totalPages; // 0
$items = 1;
$totalPages = (int)ceil($items / $perPage); // 1
$items = 14;
$totalPages = (int)ceil($items / $perPage); // 1 (14/15=0.93, ceil=1)
$items = 15;
$totalPages = (int)ceil($items / $perPage); // 1
$items = 16;
$totalPages = (int)ceil($items / $perPage); // 2
?>
0, 1, 1, 1, 2
Обратите внимание: для одной записи и лимита в 15 страница одна, что логично. При нуле записей возвращается 0 – это позволяет скрыть пагинацию.