Jak funguje WordPress uvnitř: loop

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.

Napsat komentář