Один запрос
Подумайте об этом немного больше, и есть шанс, что вы сможете выполнить один / главный запрос. Или другими словами: нет необходимости в двух дополнительных запросах, когда вы можете работать с заданным по умолчанию. И в случае, если вы не можете работать с заданным по умолчанию, вам не потребуется больше одного запроса, независимо от того, на сколько циклов вы хотите разбить запрос.
Предпосылки
Сначала вам нужно установить (как показано в моем другом ответе) необходимые значения внутри pre_get_posts
фильтра. Там вы, скорее всего, установите posts_per_page
и cat
. Пример без 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 ) );
Строительство базы
Следующее, что нам нужно, это небольшой пользовательский плагин (или просто поместите его в свой functions.php
файл, если вы не возражаете перемещать его во время обновлений или изменений темы):
<?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;
}
}
Этот плагин делает одну вещь: он использует PHP SPL (стандартную библиотеку PHP) и его интерфейсы и итераторы. То, что мы теперь получили, это то, FilterIterator
что позволяет нам удобно удалять элементы из нашего цикла. Он расширяет PHP SPL Filter Iterator, поэтому нам не нужно все устанавливать. Код хорошо прокомментирован, но вот некоторые замечания:
accept()
Метод позволяет определить критерии, позволяющие Зацикливание элемента - или нет.
- Внутри этого метода мы используем
WP_Query::the_post()
, так что вы можете просто использовать каждый тег шаблона в цикле файлов шаблона.
- А также мы отслеживаем цикл и перематываем сообщения, когда достигаем последнего пункта. Это позволяет выполнять бесконечное количество циклов без сброса нашего запроса.
- Там в один пользовательский метод , который не является частью
FilterIterator
спецификации: deny()
. Этот метод особенно удобен, так как он содержит только наше «обработано или нет» заявление, и мы можем легко перезаписать его в последующих классах без необходимости знать что-либо кроме тегов шаблона WordPress.
Как зациклить?
С этим новым итератора, нам не нужно if ( $customQuery->have_posts() )
и while ( $customQuery->have_posts() )
больше. Мы можем пойти с простым foreach
заявлением, поскольку все необходимые проверки уже сделаны для нас. Пример:
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 );
Наконец, нам не нужно ничего, кроме foreach
цикла по умолчанию . Мы можем даже удалить the_post()
и по-прежнему использовать все теги шаблонов. Глобальный $post
объект всегда будет синхронизирован.
foreach ( $primaryQuery as $post )
{
var_dump( get_the_ID() );
}
Вспомогательные петли
Теперь хорошо то, что каждый последующий фильтр запросов довольно прост в обращении: просто определите deny()
метод, и вы готовы перейти к следующему циклу. $this->current()
всегда будет указывать на наш зацикленный пост.
class NoThumbnailFilter extends ThumbnailFilter
{
public function deny()
{
return has_post_thumbnail( $this->current()->ID );
}
}
Поскольку мы определили, что теперь мы deny()
зацикливаем каждый пост с миниатюрой, мы можем мгновенно зациклить все посты без миниатюры:
foreach ( $secondaryQuery as $post )
{
var_dump( get_the_title( get_the_ID() ) );
}
Попробуй это.
Следующий тестовый плагин доступен как Gist на GitHub. Просто загрузите и активируйте его. Он выводит / сбрасывает идентификатор каждого зацикленного сообщения в качестве обратного вызова для loop_start
действия. Это означает, что в зависимости от настроек, количества постов и конфигурации может получиться совсем немного. Пожалуйста, добавьте некоторые операторы отмены и var_dump()
в конце измените то, что вы хотите увидеть и где вы хотите это увидеть. Это просто доказательство концепции.