Uma única consulta
Pense nisso um pouco mais e há uma chance de você escolher uma única consulta principal. Ou, em outras palavras: não há necessidade de duas consultas adicionais quando você pode trabalhar com o padrão. E, caso você não consiga trabalhar com um padrão, não será necessário mais do que uma única consulta, independentemente de quantos loops quiser dividir a consulta.
Pré-requisitos
Primeiro, você precisa definir (como mostrado na minha outra resposta) os valores necessários dentro de um filtro pre_get_posts
. É provável que você defina posts_per_page
e cat
. Exemplo sem o 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 ) );
Construindo uma base
A próxima coisa que precisamos é de um pequeno plugin personalizado (ou apenas colocá-lo no seu arquivo functions.php
se você não se importa em movê-lo durante atualizações ou mudanças 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 plugin faz uma coisa: Ele utiliza o PHP SPL (Biblioteca PHP Padrão) e suas Interfaces e Iteradores. O que temos agora é um FilterIterator
que nos permite remover itens do nosso loop. Ele estende o Iterador de Filtro PHP SPL para que não tenhamos que definir tudo. O código é bem comentado, mas aqui estão algumas notas:
- O método
accept()
permite definir critérios que permitem o loop do item - ou não. - Dentro desse método, usamos
WP_Query::the_post()
, portanto, você pode simplesmente usar todas as tags de modelo em seu loop de arquivos de modelo. - E também estamos monitorando o loop e rebobinando as postagens quando chegamos ao último item. Isso permite fazer loop por uma quantidade infinita de loops sem redefinir nossa consulta.
- Existe um método personalizado que não faz parte das especificações
FilterIterator
:deny()
. Este método é especialmente conveniente, pois contém apenas a nossa declaração "processo ou não" e podemos facilmente sobrescrevê-lo em classes posteriores sem precisar saber nada além das tags de template do WordPress.
Como fazer um loop?
Com esse novo Iterador, não precisamos mais de if ( $customQuery->have_posts() )
e while ( $customQuery->have_posts() )
. Podemos usar uma instrução foreach
simples, pois todas as verificações necessárias já foram feitas para nós. Exemplo:
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, não precisamos de nada além de um loop foreach
padrão. Podemos até mesmo descartar the_post()
e ainda usar todas as tags de modelo. O objeto global $post
sempre permanecerá em sincronia.
foreach ( $primaryQuery as $post )
{
var_dump( get_the_ID() );
}
Loops subsidiários
Agora, o bom é que cada filtro de consulta posterior é muito fácil de manusear: Simplesmente defina o método deny()
e você está pronto para o próximo ciclo. $this->current()
sempre apontará para nossa postagem atualmente em loop.
class NoThumbnailFilter extends ThumbnailFilter
{
public function deny()
{
return has_post_thumbnail( $this->current()->ID );
}
}
Como definimos que agora deny()
faz um loop em todas as postagens que têm uma miniatura, podemos fazer um loop instantâneo de todas as postagens sem uma miniatura:
foreach ( $secondaryQuery as $post )
{
var_dump( get_the_title( get_the_ID() ) );
}
Teste.
O seguinte plug-in de teste está disponível como Gist no GitHub. Basta fazer o upload e ativá-lo. Ele envia / copia o ID de todas as postagens em loop como retorno de chamada na ação loop_start
. Isso significa que pode ter uma saída bastante dependendo da configuração, do número de postagens e da configuração. Por favor, adicione algumas declarações de abortar e altere o var_dump()
s no final para o que você deseja ver e onde deseja vê-lo. É apenas uma prova de conceito.