Una sola consulta
Pensé un poco más en esto y existe la posibilidad de que puedas ir con una sola consulta principal. O, en otras palabras, no es necesario realizar dos consultas adicionales cuando puede trabajar con la predeterminada. Y en caso de que no pueda trabajar con uno predeterminado, no necesitará más que una sola consulta, sin importar cuántos bucles desee dividir la consulta.
Requisitos previos
Primero debe establecer (como se muestra en mi otra respuesta) los valores necesarios dentro de un filtro pre_get_posts
. Allí es probable que establezca posts_per_page
y cat
. Ejemplo sin el 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 ) );
Construyendo una base
Lo siguiente que necesitamos es un pequeño complemento personalizado (o simplemente póngalo en su archivo functions.php
si no le importa moverlo durante las actualizaciones o los cambios de 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;
}
}
Este complemento hace una cosa: utiliza la PHP SPL (biblioteca estándar de PHP) y sus interfaces e iteradores. Lo que ahora tenemos es un FilterIterator
que nos permite eliminar artículos de nuestro bucle. Extiende el iterador de filtro PHP SPL para que no tengamos que configurar todo. El código está bien comentado, pero aquí hay algunas notas:
- El método
accept()
permite definir criterios que permiten hacer un bucle del elemento, o no.
- Dentro de ese método, usamos
WP_Query::the_post()
, así que simplemente puedes usar cada etiqueta de plantilla en tu bucle de archivos de plantilla.
- Y también estamos monitoreando el bucle y rebobinando las publicaciones cuando llegamos al último elemento. Esto permite realizar un bucle a través de una cantidad infinita de bucles sin restablecer nuestra consulta.
- Hay un método personalizado que no forma parte de las especificaciones
FilterIterator
: deny()
. Este método es especialmente conveniente ya que solo contiene nuestra declaración "proceso o no" y podemos sobrescribirlo fácilmente en clases posteriores sin necesidad de saber nada, aparte de las etiquetas de plantilla de WordPress.
¿Cómo hacer un bucle?
Con este nuevo Iterador, ya no necesitamos if ( $customQuery->have_posts() )
y while ( $customQuery->have_posts() )
. Podemos ir con una simple declaración foreach
ya que todas las comprobaciones necesarias ya están hechas para nosotros. Ejemplo:
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, no necesitamos nada más que un bucle foreach
predeterminado. Incluso podemos eliminar the_post()
y seguir utilizando todas las etiquetas de plantilla. El objeto global $post
siempre permanecerá sincronizado.
foreach ( $primaryQuery as $post )
{
var_dump( get_the_ID() );
}
Bucles subsidiarios
Ahora, lo bueno es que cada filtro de consulta posterior es bastante fácil de manejar: simplemente defina el método deny()
y estará listo para su próximo ciclo. $this->current()
siempre apuntará a nuestra publicación actual en bucle.
class NoThumbnailFilter extends ThumbnailFilter
{
public function deny()
{
return has_post_thumbnail( $this->current()->ID );
}
}
Como definimos que ahora deny()
aplicamos un bucle a cada publicación que tiene una miniatura, entonces instantáneamente podemos enlazar todas las publicaciones sin una miniatura:
foreach ( $secondaryQuery as $post )
{
var_dump( get_the_title( get_the_ID() ) );
}
Pruébalo.
El siguiente complemento de prueba está disponible como Gist en GitHub. Simplemente cárgalo y actívalo. Da salida / vuelca el ID de cada publicación en bucle como devolución de llamada en la acción loop_start
. Esto significa que puede obtener bastante salida dependiendo de su configuración, número de publicaciones y configuración. Agregue algunas instrucciones de cancelación y modifique el var_dump()
s al final de lo que desea ver y dónde desea verlo. Es solo una prueba de concepto.