Une seule requête
J'ai réfléchi un peu plus à cela et il y a une chance que vous puissiez aller avec une seule / la requête principale. Ou en d'autres termes: pas besoin de deux requêtes supplémentaires lorsque vous pouvez travailler avec celle par défaut. Et dans le cas où vous ne pouvez pas travailler avec une valeur par défaut, vous n'aurez pas besoin de plus d'une seule requête, peu importe le nombre de boucles que vous souhaitez diviser.
Conditions préalables
Vous devez d'abord définir (comme indiqué dans mon autre réponse) les valeurs nécessaires dans un pre_get_posts
filtre. Là, vous aurez probablement mis posts_per_page
et cat
. Exemple sans 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 ) );
Construire une base
La prochaine chose dont nous avons besoin est un petit plugin personnalisé (ou simplement le mettre dans votre functions.php
fichier si cela ne vous dérange pas de le déplacer pendant les mises à jour ou les changements de thème):
<?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;
}
}
Ce plugin fait une chose: il utilise PHP SPL (Standard PHP Library) et ses interfaces et itérateurs. Ce que nous avons maintenant est un FilterIterator
qui nous permet de supprimer facilement les éléments de notre boucle. Il étend l'itérateur de filtre PHP SPL afin que nous n'ayons pas à tout régler. Le code est bien commenté, mais voici quelques notes:
- La
accept()
méthode permet de définir des critères permettant de boucler l'élément - ou non.
- À l'intérieur de cette méthode que nous utilisons
WP_Query::the_post()
, vous pouvez simplement utiliser chaque balise de modèle dans votre boucle de fichiers de modèle.
- Et nous surveillons également la boucle et rembobinons les messages lorsque nous atteignons le dernier élément. Cela permet de boucler à travers une quantité infinie de boucles sans réinitialiser notre requête.
- Il y a une méthode personnalisée qui ne fait pas partie des
FilterIterator
spécifications: deny()
. Cette méthode est particulièrement pratique car elle ne contient que notre déclaration "processus ou non" et nous pouvons facilement la remplacer dans les classes ultérieures sans avoir besoin de savoir quoi que ce soit en dehors des balises de modèle WordPress.
Comment boucler?
Avec ce nouveau Iterator, nous ne avons pas besoin if ( $customQuery->have_posts() )
et while ( $customQuery->have_posts() )
plus. Nous pouvons aller avec une simple foreach
déclaration car tous les contrôles nécessaires sont déjà effectués pour nous. Exemple:
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 );
Enfin, nous n'avons besoin que d'une foreach
boucle par défaut . Nous pouvons même supprimer the_post()
et toujours utiliser toutes les balises de modèle. L' $post
objet global restera toujours synchronisé.
foreach ( $primaryQuery as $post )
{
var_dump( get_the_ID() );
}
Boucles subsidiaires
Maintenant, la bonne chose est que chaque filtre de requête ultérieur est assez facile à gérer: définissez simplement la deny()
méthode et vous êtes prêt à passer à votre prochaine boucle. $this->current()
pointera toujours vers notre message actuellement en boucle.
class NoThumbnailFilter extends ThumbnailFilter
{
public function deny()
{
return has_post_thumbnail( $this->current()->ID );
}
}
Comme nous avons défini que nous deny()
bouclons maintenant chaque publication qui a une vignette, nous pouvons alors instantanément boucler toutes les publications sans vignette:
foreach ( $secondaryQuery as $post )
{
var_dump( get_the_title( get_the_ID() ) );
}
Essaye-le.
Le plugin de test suivant est disponible en tant que Gist sur GitHub. Téléchargez-le et activez-le simplement. Il génère / exporte l'ID de chaque message en boucle en tant que rappel sur l' loop_start
action. Cela signifie que cela pourrait obtenir un peu de sortie en fonction de votre configuration, du nombre de publications et de la configuration. Veuillez ajouter quelques instructions d'abandon et modifier le var_dump()
s à la fin pour ce que vous voulez voir et où vous voulez le voir. Ce n'est qu'une preuve de concept.