PHP Manual

Пагінатор і пагінація результатів в PHP

22. 08. 2019

Коли ми маємо багато даних, які потрібно скинути, ввічливо розбити їх на кілька сторінок. В даній статті не розглядається практична реалізація передачі номерів сторінок та результатів лістингу, а лише теоретичне вилучення значень та розрахунок оптимального кодового дерева для того, щоб зробити перегляд великої кількості сторінок максимально зручним для користувача.

Скільки результатів ми маємо

Для початку треба з'ясувати, скільки взагалі маємо результатів. Якщо дані надходять з бази даних, їх можна дуже ефективно підрахувати за допомогою наступного SQL-запиту:

SELECT COUNT(*) FROM tabulka

Розрахунок відбувається дуже швидко, оскільки база даних зберігає статистику у допоміжному файлі, тому вона взагалі не торкається даних.

Якщо дані надходять звідкись ще (а у нас вони в масиві, наприклад), то їх можна порахувати за допомогою функції count():

$cisla = [3, 1, 4, 1, 5, 9, 2];
echo 'Поле містить' . count($cisla) . 'цифри.';

Обмеження кількості результатів

Ще однією проблемою є обмеження кількості результатів. Якщо дані знаходяться в базі даних, просто поставте параметр LIMIT в інструкції SQL:

SELECT * FROM tabulka WHERE (cokoli) LIMIT 10

Ця команда завжди отримає максимум 10 результатів, а також зробить запит швидшим, оскільки базі даних не доведеться перебирати всі файли даних.

Якщо у нас є дані з іншого джерела (знову ж таки масив), ми також можемо обмежити результати на рівні PHP за допомогою допоміжної змінної $iterator:

$pole = [...];
$iterator = 0;
$limit = 10;
foreach ($pole as $prvek) {
// сюди скидаються дані
$iterator++;
if ($iterator >= $limit) {
break; // Зупиняє цикл після виконання 10 разів
}
}

Пропуск перших Х результатів

Коли ми знаходимося на першій сторінці, все досить просто, потрібно лише обмежити кількість результатів за допомогою LIMIT. А якщо я на третій сторінці? Тоді ми повинні пропустити перші результати "Х".

У SQL для цього знову ж таки є елегантна нотація:

SELECT * FROM tabulka WHERE (cokoli) LIMIT 10 OFFSET 20

Він пропускає перші 20 результатів і обмежує наступний вивід 10 результатами, тобто виводить інтервал <21 - 30>.

У чистому PHP це обробляється двома способами.

Якщо ми знаємо індекси масиву, то можемо почати його читання з певної точки (що дуже швидко):

$pole = [...];
$start = 20;
$limit = 10;
for ($i = $start; ($i <= $start + $limit && isset($pole[$i])); $i++) {
// сюди скидаються дані
}

Однак, для невідомого поля доводиться знову використовувати ітератор і пропускати елементи:

$pole = [...];
$iterator = 0;
$start = 20;
$limit = 10;
foreach ($pole as $prvek) {
if ($iterator < $start) {
$iterator++;
continue;
}
// якимось чином дані скидаються сюди
$iterator++;
if ($iterator >= $start + $limit) break;
}

Відображення оптимального пагінатора/степлера

Нехай нам відома загальна кількість елементів, кількість елементів на сторінці та номер поточної сторінки. Тепер ми хочемо відобразити панель, яка дозволить швидко переглядати всі сторінки з результатами пошуку. Однак, оскільки сторінок багато (порядку тисячі), ми не можемо перерахувати їх всі одразу, тому мусимо розумно вибрати кілька репрезентативних, які найкраще представляють діапазон між сторінками.

Це може виглядати так:

1 | 15 | 30 | 36 | 45 | 60 | 72

Завдання:

Я на сторінці 36 з 72, як оптимально розмістити номери сторінок? Ну, через послідовність.

Порада: Шляхом практичних спостережень я з'ясував, що ліву частину пагінатора слід обчислювати через арифметичну послідовність (так я можу рухатися лінійно на однакову кількість кроків), а праву частину через геометричну послідовність, що в свою чергу дозволяє легко зробити великий крок. Тому, якщо я хочу потрапити на певну сторінку, я спочатку пропускаю велику кількість непотрібних елементів, а потім уточнюю вибір, повертаючись назад вліво.

Теорія арифметичних послідовностей (продовжуємо додавати одне і те ж число):

$d = 10; // розмір кроку
$a[1] = 1; // перший елемент
$a[2] = $a[1] + $d; // другий елемент
$a[3] = $a[1] + 2 * $d;
$a[3] = $a[2] + $d;
$a[$n] = $a[1] + ($n - 1) * $d; // n-й елемент
function getAritmeticItem(int $start, int $step, int $n): int
{
return $start + ($n - 1) * $step;
}

Теорія геометричних послідовностей (завжди множити на одне і те ж число):

$q = 10; // розмір кроку
$a[1] = 1; // перший елемент
$a[2] = $a[1] * $q; // другий елемент
$a[3] = $a[1] * $q * $q;
$a[3] = $a[1] * pow($q, 2);
$a[3] = $a[2] * $q;
$a[$n] = $a[1] * pow($q, $n - 1); // n-й елемент
function getGeometricItem(int $start, int $step, int $q): int
{
return $start * pow($q, $step - 1);
}

$start = 1;
$current = 36;
$end = 72;

Jan Barášek   Více o autorovi

Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.

Rád vám pomůžu:

Související články

1.
Status:
All systems normal.
2024