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.

Przykład rozbitej pętli artykułów z serwisu 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).