Na pewno spotkałeś się ze stronami lub motywami, które wyświetlając listę wpisów, używają dla nich różnego wyglądu. Na przykład pierwsze dwa artykuły wyświetlane są jako duże zdjęcie i tytuł, dwa następne jako tytuł, małe zdjęcie i zajawka, a pozostałe – jako same tytuły. Poniżej przykład takiego bloku listy artykułów z portalu gazeta.pl.
Taki układ artykułów stosowany jest dość często i niestety równie często implementowany jest w nieprawidłowy/niewydajny sposób, co prowadzi do wielu zbędnych zapytań do bazy danych i spowolnienia działania strony. A jak zrobić to poprawnie i przy użyciu tylko jednej pętli?
Jeśli spotkałeś już tego typu układ (a może sam tworzyłeś motyw z takim blokiem), to jest spora szansa, że kod go realizujący wyglądał podobnie do poniższego:
<?php
$query = new WP_Query( array(<PARAMETRY ZAPYTANIA>, 'posts_per_page' => 2) );
while ( $query->have_posts() ) : $query->the_post();
?>
<!-- Tu wypisujemy dwa pierwsze artykuły -->
<?php endwhile; ?>
<?php
$query = new WP_Query( array(<PARAMETRY ZAPYTANIA>, 'posts_per_page' => 1, 'offset' => 1) );
while ( $query->have_posts() ) : $query->the_post();
?>
<!-- Tu wypisujemy trzeci artykuł -->
<?php endwhile; ?>
<?php
$query = new WP_Query( array(<PARAMETRY ZAPYTANIA>, 'posts_per_page' => 9, 'offset' => 3) );
while ( $query->have_posts() ) : $query->the_post();
?>
<!-- Tu wypisujemy pozostałe artykuły -->
<?php endwhile; ?>
Co w tym złego? Przecież działa…
Owszem, trzeba przyznać – powyższy fragment kodu realizuje dokładnie to, czego chcieliśmy. Nie oznacza to jednak, że robi to w poprawny czy wydajny sposób.
Co zatem jest z nim nie tak? Zauważ, że trzykrotnie wykonuje on dokładnie to samo zapytanie (parametry zapytania przekazywane do WP_Query
są prawie identyczne i różnią się jedynie końcówką, w której określono, ile postów i z jakim przesunięciem należy wybrać). To znaczy, że kod ten aż trzy razy będzie odpytywał bazę danych o listę wpisów!
Jak zrobić to z użyciem tylko jednej pętli?
Narzućmy sobie założenie, że musimy ten blok zrealizować tylko jednym zapytaniem do bazy danych i jedną pętlą. Nasz kod powinien wyglądać mniej więcej tak:
<?php
$query = new WP_Query( array(<PARAMETRY ZAPYTANIA>, 'posts_per_page' => 12) );
while ( $query->have_posts() ) : $query->the_post();
?>
<?php if ( pierwszy lub drugi wpis ) : ?>
<!-- Tu wygląd pierwszego i drugiego -->
<?php elseif ( trzeci wpis ) : ?>
<!-- Tu wygląd trzeciego -->
<?php else : ?>
<!-- Tu wygląd pozostałych -->
<?php endif; ?>
<?php endwhile; ?>
A jak zrealizować sprawdzanie warunków, które w tym kodzie się znajdują? Skąd wiedzieć, że to akurat pierwszy wpis jest teraz wyświetlany?
Twórcy motywów bardzo często używają w tym celu własnego licznika. Tuż przed pętlą tworzą jakąś zmienną, której wartość następnie w każdym przebiegu pętli zwiększają o 1.
I wszystko byłoby super, gdyby nie fakt, że jest to zbędne i wynika z niewiedzy i nieznajomości kodu WordPressa. Otóż wyobraź sobie, że obiekt WP_Query
ma taki licznik wbudowany i spokojnie możemy z niego korzystać. Cytując za Codexem:
$current_post
(available during The Loop) Index of the post currently being displayed.
Łącząc to wszystko w całość, otrzymujemy następujący fragment kodu, który realizuje to, co chcieliśmy w wydajny sposób:
<?php
$query = new WP_Query( array(<PARAMETRY ZAPYTANIA>, 'posts_per_page' => 12) );
while ( $query->have_posts() ) : $query->the_post();
?>
<?php if ( $query->current_post < 2 ) : ?> // wpisy indeksowane są zaczynając od zera
<!-- Tu wygląd pierwszego i drugiego -->
<?php elseif ( $query->current_post < 3 ) : ?>
<!-- Tu wygląd trzeciego -->
<?php else : ?>
<!-- Tu wygląd pozostałych -->
<?php endif; ?>
<?php endwhile; ?>
Uwaga
W powyższym kodzie zakładam, że takich bloków chcemy wyświetlić wiele, albo że wyświetlamy go w stopce, sidebarze, itp. Gdyby kod miał wyświetlać wpisy z głównej pętli WordPressa (np. wpisy z danej kategorii), to wówczas nie powinniśmy w ogóle odpytywać przy użyciu własnej instancji WP_Query
, a kod oprzeć na zapytaniu dostarczonym przez WordPressa (natomiast struktura pętli, sprawdzane warunki, itd. pozostają bez zmian).