Jedno zapytanie
Zastanowiłem się nad tym trochę więcej i istnieje szansa, że możesz przejść do pojedynczego / głównego zapytania. Lub innymi słowy: Nie trzeba dwóch dodatkowych zapytań, gdy można pracować z domyślnym. A jeśli nie możesz pracować z domyślnym, nie potrzebujesz więcej niż jednego zapytania, bez względu na to, ile pętli chcesz podzielić.
Wymagania wstępne
Najpierw musisz ustawić (jak pokazano w mojej innej odpowiedzi) potrzebne wartości w pre_get_posts
filtrze. Tam prawdopodobnie ustawisz posts_per_page
i cat
. Przykład bez pre_get_posts
filtru:
$catID = 1;
$catQuery = new WP_Query( array(
'posts_per_page' => -1,
'cat' => $catID,
) );
// Add a headline:
printf( '<h1>%s</h1>', number_format_i18n( $catQuery->found_posts )
.__( " Posts filed under ", 'YourTextdomain' )
.get_cat_name( $catID ) );
Budowanie bazy
Następna rzecz, której potrzebujemy, to mała niestandardowa wtyczka (lub po prostu umieść ją w functions.php
pliku, jeśli nie masz nic przeciwko przenoszeniu jej podczas aktualizacji lub zmian motywu):
<?php
/**
* Plugin Name: (#130009) Merge Two Queries
* Description: "Merges" two queries by using a <code>RecursiveFilterIterator</code> to divide one main query into two queries
* Plugin URl: http://wordpress.stackexchange.com/questions/130009/how-to-merge-two-queries-together
*/
class ThumbnailFilter extends FilterIterator implements Countable
{
private $wp_query;
private $allowed;
private $counter = 0;
public function __construct( Iterator $iterator, WP_Query $wp_query )
{
NULL === $this->wp_query AND $this->wp_query = $wp_query;
// Save some processing time by saving it once
NULL === $this->allowed
AND $this->allowed = $this->wp_query->have_posts();
parent::__construct( $iterator );
}
public function accept()
{
if (
! $this->allowed
OR ! $this->current() instanceof WP_Post
)
return FALSE;
// Switch index, Setup post data, etc.
$this->wp_query->the_post();
// Last WP_Post reached: Setup WP_Query for next loop
$this->wp_query->current_post === $this->wp_query->query_vars['posts_per_page'] -1
AND $this->wp_query->rewind_posts();
// Doesn't meet criteria? Abort.
if ( $this->deny() )
return FALSE;
$this->counter++;
return TRUE;
}
public function deny()
{
return ! has_post_thumbnail( $this->current()->ID );
}
public function count()
{
return $this->counter;
}
}
Ta wtyczka robi jedną rzecz: wykorzystuje PHP SPL (Standardową bibliotekę PHP) oraz jej interfejsy i iteratory. Teraz mamy to, FilterIterator
co pozwala nam wygodnie usuwać przedmioty z naszej pętli. Rozszerza PHP SPL Filter Iterator, więc nie musimy ustawiać wszystkiego. Kod jest dobrze skomentowany, ale oto kilka uwag:
accept()
Metoda pozwala określić kryteria, które pozwalają na pętli element - czy nie.
- Wewnątrz tej metody używamy
WP_Query::the_post()
, więc możesz po prostu użyć każdego znacznika szablonu w pętli plików szablonów.
- Monitorujemy również pętlę i przewijamy posty po dotarciu do ostatniego elementu. Pozwala to na przechodzenie przez nieskończoną liczbę pętli bez resetowania naszego zapytania.
- Jest jeden sposób niestandardowy, który nie jest częścią
FilterIterator
specyfikacji: deny()
. Ta metoda jest szczególnie wygodna, ponieważ zawiera tylko naszą informację „przetwarzaj lub nie” i możemy łatwo zastąpić ją w późniejszych klasach bez konieczności poznawania czegokolwiek poza tagami szablonów WordPress.
Jak zapętlić?
Dzięki tej nowej Iterator, nie potrzebujemy if ( $customQuery->have_posts() )
i while ( $customQuery->have_posts() )
już. Możemy zastosować proste foreach
oświadczenie, ponieważ wszystkie potrzebne kontrole są już dla nas wykonane. Przykład:
global $wp_query;
// First we need an ArrayObject made out of the actual posts
$arrayObj = new ArrayObject( $wp_query->get_posts() );
// Then we need to throw it into our new custom Filter Iterator
// We pass the $wp_query object in as second argument to keep track with it
$primaryQuery = new ThumbnailFilter( $arrayObj->getIterator(), $wp_query );
Wreszcie nie potrzebujemy nic więcej niż domyślnej foreach
pętli. Możemy nawet upuścić the_post()
i nadal używać wszystkich tagów szablonów. Obiekt globalny $post
zawsze będzie zsynchronizowany.
foreach ( $primaryQuery as $post )
{
var_dump( get_the_ID() );
}
Pętle pomocnicze
Zaletą jest to, że każdy późniejszy filtr zapytań jest dość łatwy w obsłudze: wystarczy zdefiniować deny()
metodę i możesz przejść do następnej pętli. $this->current()
zawsze będzie wskazywać nasz aktualnie zapętlony post.
class NoThumbnailFilter extends ThumbnailFilter
{
public function deny()
{
return has_post_thumbnail( $this->current()->ID );
}
}
Ponieważ zdefiniowaliśmy, że deny()
zapętlamy teraz każdy post z miniaturą, możemy natychmiast zapętlić wszystkie posty bez miniatury:
foreach ( $secondaryQuery as $post )
{
var_dump( get_the_title( get_the_ID() ) );
}
Sprawdź to.
Poniższa wtyczka testowa jest dostępna jako Gist w GitHub. Wystarczy go przesłać i aktywować. Wysyła / zrzuca identyfikator każdego zapętlonego postu jako wywołanie zwrotne dla loop_start
akcji. Oznacza to, że może uzyskać całkiem sporo danych wyjściowych w zależności od konfiguracji, liczby postów i konfiguracji. Dodaj kilka instrukcji przerywania i zmień var_dump()
s na końcu tego, co chcesz zobaczyć i gdzie chcesz to zobaczyć. To tylko dowód koncepcji.