Pokročilé stránkování v PHP

Sun, 21 Jun 2020 14:41:05 +1100

Za víc než 10 let, co svou stránkovací knihovnu používám, už prošla řadami změn. Od nepatrných, jako jsou bug fixy až po zásadní, jako například změna parametrů v konstruktoru. Některé nové funkce byly přidány, ale základní kostra je víceméně stejná.

Ano, už jste asi pochopili, že v následujícím článku vás nebudu učit, jak vypsat cyklem stránkování. Raději vám představím hotové řešení: takové, které na pár řádcích zařídí vše potřebné. Vy pak nemusíte řešit malichernosti a soustředit se na důležitější věci. Například optimalizace SQL dotazů.

Tak se jdeme podívat, jak na to. Článek byl poprvé publikován v roce 2010 a nyní, po rozsáhlé revizi stránkovací knihovny, ho znovu posouvám výš. Na PHP7 už to samozřejmě funguje.

Základní nasazení

Samotné nasazení jsem se snažil zredukovat na nutné minimum tak, abychom nemuseli volat hromady _set funkcí, a zároveň, aby šlo stále nastavit vše potřebné.

// SQL dotaz samozrejme upravim v souladu se svym frameworkem
$count = $mysqli->query("SELECT COUNT(*) FROM `tabulka`")->fetch_row();
$total = $count[0];

$paging_limit = 20;

// celkem | po kolika
$paging = new Paging($total, $paging_limit);
$paging->set_paging();

$paging_start = $paging->get_start();
$query = "SELECT * FROM `tabulka` LIMIT {$paging_start}, {$paging_limit}"; // -> assoc_list

// simple vypis
echo $paging->export_paging();

A to je vše. Jedním SQL dotazem spočítám záznamy, někde z nastavení vytáhnu stránkovací limit. Zbytek si knihovna zjistí sama. Výchozí proměnná pro stránkování je $_GET['page'], v případě potřeby jiné proměnné ji lze změnit v konstruktoru. Ukážu na dalším příkladu. Co se odkazů týče, vše je automatické. Ať už oddělovač ? nebo &, tak doplnění celého query stringu, pokud ho chceme předávat dál. Vstup je automaticky ošetřen: _GET parametr je přetypován na int, takže ani na tohle už myslet nemusíme.

Na výše uvedeném příkladu dostaneme defaultní výstup. Jak ho změnit si ukážeme v rozšířeném nastavení.

Rozšířené nastavení

Kromě základních parametrů můžeme knihovně předat i spoustu dalších informací. Nejčastěji využijeme přejmenovaní GET proměnné. Nechceme-l GET['page'], stačí nastavit třetí argument. Podporu má i jednoduché pole. Umíme i vícerozměrné, nicméně takové věci už podle mého názoru nemají v URL co dělat. Ale pro jistotu.

Další důležité metody jsou set_paging_mode(), set_output_mode() a set_around(). Ty definují, jaký typ stránkování dostaneme na výstupu. Jednotlivé příklady najdete na konci článku.

Generování odkazů je automatické, vezme se REQUEST_URI a zkopíruje se celý GET vyjma definované proměnné. Pokud nastavím set_base_path(), automatické generování přestane. Dostanu pouze to, co jsem třídě předal, tedy například www.treba.cz/?page=2. V takovém případě pak další z metod, set_drop_vars(), nemá význam.

Změnu generovaného HTML pomocí metody set_html_pattern() tu více rozvádět nebudu, ať mi jednoduchý návod nenakyne do obřích rozměrů. Na to se můžete podívat ve zdrojáku souboru.

A na závěr opět metoda set_paging, která si navíc umí pohlídat, jestli je volaná, kdy má.

$paging = new Paging($total, $paging_limit, 'filter[page]');
$paging->set_paging_mode(1)               // 1|0
       ->set_output_mode(1)               // 1|0
       ->set_around(2)                    // pri paging_mode=1
       ->set_valid_links(true)            // replace [] pri slozenych odkazech

       ->set_base_path($category_url)     // nechci generovat nic, predam zaklad odkazu
       ->set_default_page(2)              // kdyz neni GET[page] zacinam na strane 2
       ->set_drop_vars(array('ajax'))     // chci zkopirovat cely get krome ?ajax=
       ->disable_translate_function(true) // mam funkci __() a dela neco jineho, nez ze akceptuje 1 string a vraci 1 string => nebudu prekladat

       ->set_title_format($dt, $mb, $mn)  // desktop title, mobile back, mobile next
       ->set_html_patterns($pattern)      // @see protected $pattern
       ->set_paging();

Funkce __ (dvě podtržítka)

Jednou z metod, které můžete či nemusíte zavolat, je také "disable_translate_function()". Protože počítám i s lokalizací titulků, testuji existenci funkce dvě podtržítka. Můžete ji najít například ve Wordpressu, používá ji i Kohana framework. Pokud ovšem takovou funkci máte k něčemu jinému, přidal jsem i možnost její volání vypnout. Stejně tak pro případ, že si chcete přeložit texty ještě před zavoláním stránkovací knihovny. Proto ani neřeším žádné callbacky. Pokud tuto funkci nemáte vůbec, neřešíte, protože volání je vždy podmíněno function_existem.

Filosofická vsuvka

Na chvíli se ještě vrátím zpět k parsování QUERY STRINGu a celkové logice předávání parametrů. Jako správně by to knihovna vůbec neměla dělat. Měla by dostat URL, referenci na jeden klíč a podle toho se zařídit. Jenže... To bychom pak v systému neřešili nic jiného, než které proměnné předat a které ne. Takhle máme možnost buď vše anebo nic. Funkce set_drop_vars je výjimka z tohoto pravidla, ale tady jsem si prostě chtěl ušetřit práci. Raději zvolím řešení, díky kterému nemusím na nic myslet a vím, že to skript vyřeší za mě.

Výstup do šablony

Jak je zmíněno výše, máme celkem 4 způsoby, jak stránkování vypsat. A k tomu navíc pátý pro telefon. Pokud se tedy nespokojíme se základním echo $paging; můžeme si na výstupu podmínit i alternativní výpis právě pro mobilní zařízení: namísto hromady odkazů jenom tlačítko "Následující" respektive "Předchozí". Jakoukoli další modifikaci zařídí styly, protože každý z elementů lze snadno a unikátně zacílit.



		<?=$paging->export_paging();?>


		<?=$paging->export_mobile();?>



A pokud jsme ještě náročnější a ani tohle nám nestačí, pak nám pomůže poslední funkce a my si můžeme s výstupem dělat cokoli, co se nám zlíbí. Ale jak říkám, tohle je jedna z věcí, kterou člověk opravdu nechce zaplácat jednoduchou šablonu. Když se sami podíváte na funkci export_paging(), fakt není úplně jednoduchá. Ale proti gustu... :-)

<xmp>
<?php print_r($paging->export_data()); ?>
</xmp>

Varianty výstupu

export_paging();
1 ... 6 | 7 | 8  ... 50       (paging_mode = 1 | output_mode = 1) // vychozi
 <<  <  5  >  >>              (paging_mode = 1 | output_mode = 0 | vhodne dat set_around=1)
1 | 2 | 3 | 4 | 5             (paging_mode = 0 | output_mode = 0)
1-10 | 11-20 | 21-30 | 31-32  (paging_mode = 0 | output_mode = 1)

export_mobile();
< Predchazejici  Nasledujici >

Na zdroják se můžete podívat nebo si ho stáhnout. Člověk by si řekl - že maličkost - ale je to pěkných 600 řádků kódu.

class.Paging.php
class.Paging.zip