Una singola query
Ci ho pensato un po 'di più e c'è la possibilità che tu possa andare con una singola / la query principale. O in altre parole: non è necessario per altre due query quando è possibile lavorare con quella predefinita. E nel caso in cui non sia possibile lavorare con uno predefinito, non sarà necessario più di una singola query, indipendentemente dal numero di loop che si desidera dividere la query.
Prerequisiti
Per prima cosa devi impostare (come mostrato nella mia altra risposta) i valori necessari all'interno di un pre_get_posts
filtro. Lì probabilmente avrai impostato posts_per_page
e cat
. Esempio senza il pre_get_posts
-Filter:
$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 ) );
Costruire una base
La prossima cosa di cui abbiamo bisogno è un piccolo plug-in personalizzato (o semplicemente inserirlo nel tuo functions.php
file se non ti dispiace spostarlo durante gli aggiornamenti o le modifiche del tema):
<?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;
}
}
Questo plugin fa una cosa: utilizza PHP SPL (Standard PHP Library) e le sue interfacce e iteratori. Ciò che abbiamo ora è un FilterIterator
che ci consente di rimuovere comodamente gli articoli dal nostro loop. Estende l'Iteratore filtro PHP SPL in modo da non dover impostare tutto. Il codice è ben commentato, ma ecco alcune note:
- Il
accept()
metodo consente di definire criteri che consentono il looping dell'articolo - oppure no.
- All'interno di quel metodo usiamo
WP_Query::the_post()
, quindi puoi semplicemente usare ogni tag template nel tuo ciclo di file template.
- E stiamo anche monitorando il loop e riavvolgendo i post quando raggiungiamo l'ultimo elemento. Ciò consente di eseguire il looping attraverso un numero infinito di loop senza ripristinare la nostra query.
- C'è un metodo personalizzato che non fa parte delle
FilterIterator
specifiche: deny()
. Questo metodo è particolarmente conveniente in quanto contiene solo la nostra dichiarazione "process or not" e possiamo facilmente sovrascriverlo nelle classi successive senza bisogno di sapere nulla a parte i tag dei template di WordPress.
Come eseguire il loop?
Con questa nuova Iterator, non abbiamo bisogno if ( $customQuery->have_posts() )
e while ( $customQuery->have_posts() )
più. Possiamo andare con una semplice foreach
dichiarazione poiché tutti i controlli necessari sono già stati eseguiti per noi. Esempio:
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 );
Finalmente non abbiamo bisogno di altro che un foreach
loop predefinito . Possiamo persino eliminare the_post()
e utilizzare ancora tutti i tag modello. L' $post
oggetto globale rimarrà sempre sincronizzato.
foreach ( $primaryQuery as $post )
{
var_dump( get_the_ID() );
}
Anelli secondari
Ora la cosa bella è che ogni filtro di query successivo è abbastanza facile da gestire: basta definire il deny()
metodo e sei pronto per il tuo prossimo ciclo. $this->current()
indicherà sempre il nostro post attualmente in loop.
class NoThumbnailFilter extends ThumbnailFilter
{
public function deny()
{
return has_post_thumbnail( $this->current()->ID );
}
}
Dato che abbiamo definito che ora eseguiamo il deny()
looping di tutti i post che hanno una miniatura, possiamo quindi eseguire immediatamente il loop di tutti i post senza una miniatura:
foreach ( $secondaryQuery as $post )
{
var_dump( get_the_title( get_the_ID() ) );
}
Provalo.
Il seguente plug-in di prova è disponibile come Gist su GitHub. Basta caricarlo e attivarlo. Emette / scarica l'ID di ogni post in loop come callback loop_start
sull'azione. Ciò significa che potrebbe ottenere un bel po 'di output a seconda della configurazione, del numero di post e della configurazione. Si prega di aggiungere alcune dichiarazioni di interruzione e modificare le var_dump()
s alla fine di ciò che si desidera vedere e dove si desidera vederlo. È solo una prova di concetto.