Hlavním motorem, který zajišťuje, že se načte a zobrazí požadovaná stránka webu, je loop. Je to cyklus, který probíhá v několika fázích. Pokud některá z nich chybí nebo je špatně upravená, stránka nebude fungovat správně. Při modifikaci loopu se často používají hooky a v zobrazovací části samozřejmě šablony.
Informace si představíme obráceným postupem od výsledku k zadání, od nejjednoduššího ke složitějšímu, aby bylo vše lépe srozumitelné.
Zobrazování dat: Jak vypadá loop
Loop se spouští v každém skriptu šablony, který má na starosti vykreslení celé stránky – tedy typicky index.php, page.php, single.php nebo category.php. Nespouští se v dílčích skriptech, jako jsou header.php, footer.php, search.php nebo dalších, protože ty všechny jsou vkládány do hlavních skriptů pomocí nějaké funkce (v následující ukázce je touto funkcí get_template_part
).
while ( have_posts() ) : the_post(); // Include the page content template. get_template_part( 'template-parts/content', 'page' ); // End of the loop. endwhile;
Jak vidíte, je to triviální. Pomocí get_template_part
se načítá kód, který zobrazí tělo stránky. Klíčové jsou funkce have_posts a the_post. Ta první zjišťuje, jestli jsou v interní paměti dostupné další záznamy (třeba články) načtené z databáze, tedy zda je stále co zobrazovat, ta druhá kopíruje tato data do globální proměnné $post, s níž pracuje většina funkcí. Proměnná pak obsahuje detaily záznamu (třeba článku) – název, adresu, obsah, datum, autora atd. Neobsahuje ale custom fields, ty je potřeba načítat explicitně.
Loop má několik fází – příprava dat, načtení dat, zobrazení dat. První dvě nejsou vidět a pracují se standardními hodnotami. Zabývat se jí potřebujete až tehdy, kdy chcete tyto hodnoty měnit. Zobrazení dat zajišťuje šablona, jak vidíte na výpisu výše.
Načítání dat: WP_Query
Tou interní pamětí, kterou zmiňujeme výše, je proměnná WP_Query. Jde o objekt obsahující data načtená z databáze. Hlavně jsou to záznamy (třeba příspěvky), které byly z databáze načteny podle určitých kritérií a pak jsou to informace o samotném dotazu. Stejně jako proměnná $post je také tento objekt určen jen pro čtení.
Často narazíte na situace, kdy potřebujete v rámci jedné šablony zobrazit různé příspěvky, tedy potřebujete spustit druhý nebo další loop. Existuje zde několik řešení – s ohledem na to, že hlavní loop nesmíte nijak násilím měnit. Nejčistším řešením je vytvořit si vlastní loop do vlastní proměnné:
$my_query = new WP_Query($args); while ( $my_query->have_posts() ) : $my_query->the_post(); // Include the page content template. get_template_part( 'template-parts/content', 'page' ); // End of the loop. endwhile; wp_reset_postdata();
Poslední funkce resetuje data proměnné $post na hodnotu z hlavního cyklu. Jinak vám v proměnné zůstanou data vašeho podcyklu a web přestane korektně fungovat.
Načítání dat: get_posts
Jinou situací pak je, kdy potřebujete pouze načíst seznam příspěvků z databáze a nevyžadujete, aby procházely loopem, tzn. nebudou se chovat dynamicky, nebudou zpracovány šablonami. K tomu stačí použít některou z funkcí get_posts($args) nebo get_pages($args). Vrací pole příspěvků/stránek, které vyhovují zadaným kritériím.
Rozdíl mezi WP_Query a get_posts/get_pages. První přístup je postaven na probíhajícím cyklu (loopu). V rámci jeho běhu se můžete dynamicky přesunovat ve frontě nalezených příspěvků (tedy požádat o předchozí/následující článek, stránkování), kritéria pro vyhledání musíte měnit hookem (viz dále), pracuje se s globální proměnnou $post, která obsahuje vždy aktuální článek (aktuální ve smyslu „načtený a vyhovující kritériím“, nikoliv „dnešní“). Ostatně většina funkcí vykreslující části obsahu pracuje s proměnnou $post. Zatímco druhý přístup je pouhý jednorázový statický dotaz do databáze a následně práce s načtenými daty, s globálními proměnnými se nepracuje.
Příprava dat: Kritéria pro WP_Query
Na příkladu z mé oblíbené GeneratePress, kterou doporučuji jako šablonu pro budování webu, si ukážeme celý loop, jde o skript archive.php:
<?php if ( ! defined( 'ABSPATH' ) ) exit; get_header(); ?> <section id="primary" <?php generate_content_class(); ?>> <main id="main" <?php generate_main_class(); ?>> <?php do_action('generate_before_main_content'); ?> <?php if ( have_posts() ) : ?> <?php do_action( 'generate_archive_title' ); ?> <?php /* Start the Loop */ ?> <?php while ( have_posts() ) : the_post(); ?> <?php /* Include the Post-Format-specific template for the content. * If you want to override this in a child theme, then include a file * called content-___.php (where ___ is the Post Format name) and that will be used instead. */ get_template_part( 'content', get_post_format() ); ?> <?php endwhile; ?> <?php generate_content_nav( 'nav-below' ); ?> <?php else : ?> <?php get_template_part( 'no-results', 'archive' ); ?> <?php endif; ?> <?php do_action('generate_after_main_content'); ?> </main><!-- #main --> </section><!-- #primary --> <?php do_action('generate_sidebars'); get_footer(); ?>
Strukturu loopu jsme si vysvětlili, funkce začínající názvem generate_ a končící slovem _class vracejí podle kontextu CSS třídy pro odlišení jednotlivých kontejnerů. Příkazy do_action s parametry spouštějí obsluhu hooků (připomeňte si, jak hooky ve WordPressu fungují). Takže třeba hook generate_archive_title zajistí zobrazení titulku této stránky – a vy ho podle potřeby můžete měnit.
Samotný dotaz WP_Query se provádí ještě před zobrazením stránky. Kde ale zadat parametry tomuto dotazu, když se ke slovu dostáváme až v zobrazovací fázi, když už byl dotaz proveden a výsledky můžeme jen zobrazit?
Dostali jsme se do neviditelné hloubky, do abstraktní úrovně, kde pomocí hooku ovlivníme, jaké výsledky nám WP_Query vrátí.
Pokud byste chtěli v archivu pro rok 2017 vypisovat dvanáct posledních příspěvků, zatímco v ostatních archivech (tedy v kategoriích, výpisech při stránkování) jen čtyři, pak byste museli upravit dotaz pro tento konkrétní archiv. Zatímco ty čtyři příspěvky se dají nastavit ve standardní administraci (Nastavení > Zobrazování), těch dvanáct si musíte nastavit na míru. Slouží k tomu hook jménem pre_get_posts. Na vstupu obsahuje data připravená pro WP_Query, která můžete změnit.
function change_query($query) { if ( !is_admin() && $query->is_main_query() && is_archive('2017') ) { $query->set('posts_per_page', 12 ); } } add_action('pre_get_posts','change_query');
Jak bylo řečeno, loop se spouští velmi brzy po vytvoření stránky. Pokud jste na výsledné webové stránce (front-end), natahuje se minimální verze WordPressu, pokud jste v administraci (back-endu), pak se načítá celý WordPress. Jinými slovy – hlavní loop je jenom jeden. Je společný jak front-endu, tak back-endu. Proto je velmi vhodné testovat, jestli se nacházíte v administraci – pokud byste tak neučinili, vaše úprava WP_Query ovlivní i výpis příspěvků v administraci (což je super, ale pouze tehdy, je-li to záměr).
Další podmínka se týká zjištění, zda se nacházíme v hlavním loopu, nebo sekundárním (viz výše příklad s new WP_Query). A poslední podmínka už zjišťuje přítomnost konkrétní stránky, tedy v tomto případě archivu příspěvků.
Pokud jsou podmínky splněny, nacházíme-li se tedy na frontendu ve výpisu archivu roku 2017, pak do loopu předáme parametr, že jedna stránka má obsahovat 12 příspěvků. Proměnná $query je do obslužné funkce pro pre_get_posts předávána odkazem, takže není potřeba z funkce vracet žádnou hodnotu, nastavení probíhá ihned a přímo.
Výhody a rizika
Tento mechanimus – hooky, WP_Query – je perfektně vymyšlený, není v principu komplikovaný a je velmi efektivní. Na druhou stranu je abstraktní a špatně se ladí, protože v detailu komplikovaný být může. Představte si ale, že potřebujete v archivu zobrazit jen ty příspěvky, které mají v custom field uloženou nějakou hodnotu (třeba status==“zaplaceno“) a uživatel, který si je chce zobrazit, musí mít určitou roli (třeba „student“).
Na to první stačí dobře napsaná obsluha pre_get_posts a na to druhé jednoduchá podmínka v šabloně. Kód kolem hooků a WP_Query je perfektně udržitelný a pomocí něj můžete chování WordPressu významně upravovat.