PHP Manual
/
Zpracování dat

Paginator a stránkování výpisu výsledků v PHP

22. 08. 2019

Když máme na výpis mnoho dat, tak je slušnost je rozdělit na více stránek. Tento článek neřeší praktickou implementaci předávání čísel stránek a vypisování výsledků, pouze teoretické získání hodnot a výpočet optimálního číselníku tak, aby bylo procházení velkého množství stránek co nejvíce uživatelsky přívětivé.

Kolik máme k dispozici výsledků

Na začátku musíme zjistit, kolik máme vůbec k dispozici výsledků. Pokud data pochází z databáze, tak je lze velice efektivně spočítat následujícím SQL příkazem:

SELECT COUNT(*) FROM tabulka

Výpočet to je velice rychlý, protože si databáze vede statistiky v pomocném souboru, proto na data vůbec nesahá.

Pokud data pochází odjinud (a máme je například v poli), tak je lze spočítat funkcí count():

$cisla = [3, 1, 4, 1, 5, 9, 2];
echo 'Pole obsahuje ' . count($cisla) . ' čísel.';

Omezení počtu výsledků

Další problém je omezení počtu výsledků. Pokud jsou data v databázi, tak stačí do SQL příkazu vložit parametr LIMIT:

SELECT * FROM tabulka WHERE (cokoli) LIMIT 10

Tímto příkazem dostaneme vždy maximálně 10 výsledků, zároveň bude také dotaz rychlejší, protože databáze nebude muset procházet celé datové soubory.

Pokud máme data z jiného zdroje (zase opět pole), tak můžeme výsledky omezit také na úrovni PHP s použití pomocné proměnné $iterator:

$pole = [...];
$iterator = 0;
$limit = 10;
foreach ($pole as $prvek) {
// tady se vypisují data
$iterator++;
if ($iterator >= $limit) {
break; // Zastaví cykl, až proběhne 10x
}
}

Přeskočení prvních X výsledků

Když jsme na první stránce, je to celkem jednoduché, to stačí pouze za pomoci LIMITu omezit počet výsledků. Ale co když jsem na třetí stránce? Pak musíme prvních X výsledků přeskočit.

V SQL na to máme opět elegantní zápis:

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

Přeskočí prvních 20 výsledků a další výpis omezí na 10 výsledků, vypíše proto tedy interval <21 - 30>.

V čistém PHP se toto řeší dvěma způsoby.

Pokud známe indexy pole, tak jej můžeme začít číst až od určitého místa (což je velice rychlé):

$pole = [...];
$start = 20;
$limit = 10;
for ($i = $start; ($i <= $start + $limit && isset($pole[$i])); $i++) {
// tady se vypisují data
}

Ovšem u neznámého pole musíme opět použít iterátor a položky přeskočit:

$pole = [...];
$iterator = 0;
$start = 20;
$limit = 10;
foreach ($pole as $prvek) {
if ($iterator < $start) {
$iterator++;
continue;
}
// tady se nějak vypisují data
$iterator++;
if ($iterator >= $start + $limit) break;
}

Zobrazení optimálního paginatoru / krokovače

Dejme tomu, že známe celkový počet položek, počet položek na stránce a číslo aktuální stránky. Nyní chceme vykreslit lištu, která umožní rychlé procházení všech stránek s výsledky hledání. Protože je ovšem stránek hodně (řádově tisíce), tak nemůžeme vypsat všechny najednou, musíme si proto inteligentně vybrat je nějaké reprezentativní, které nejlépe vystihují rozmezí mezi stránkami.

Vypadat to může třeba takto:

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

Zadání:

Jsem na stránce 36 ze 72, jak optimálně rozmístit čísla stránek? No přeci přes posloupnost.

Tip: Praktickým pozorováním jsem zjistil, že levou část Paginatoru je vhodné počítat přes aritmetickou posloupnost (abych se mohl lineárně přesouvat o stejný počet kroků) a pravou část přes geometrickou posloupnost, která zase umožňuje snadno udělat velký krok. Pokud se tedy chci dostat na nějakou konkrétní stránku, tak nejprve přeskočím velké množství nepotřebných položek a poté zpřesňuji volbu vracením směrem doleva.

Teorie aritmetické posloupnosti (stále přičítáme stejné číslo):

$d = 10; // velikost kroku
$a[1] = 1; // první prvek
$a[2] = $a[1] + $d; // druhý prvek
$a[3] = $a[1] + 2 * $d;
$a[3] = $a[2] + $d;
$a[$n] = $a[1] + ($n - 1) * $d; // n-tý prvek
function getAritmeticItem(int $start, int $step, int $n): int
{
return $start + ($n - 1) * $step;
}

Teorie geometrické posloupnosti (stále násobíme stejným číslem):

$q = 10; // velikost kroku
$a[1] = 1; // první prvek
$a[2] = $a[1] * $q; // druhý prvek
$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-tý prvek
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.

Potřebujete poradit s PHP?

Nabízím trénink vývojářů, konzultace, školení a analýzu návrhových vzorů. Osobně v Praze nebo online.

Napište mi, pokud si nevíte rady.

Lektor: Jan Barášek

Status:
All systems normal.
2024