Один запит
Подумайте про це трохи більше, і є шанс, що ви можете перейти з одним / основним запитом. Або іншими словами: Немає необхідності у двох додаткових запитах, коли можна працювати з типовим. І якщо ви не можете працювати з типовим, вам не знадобиться більше одного запиту, незалежно від того, на скільки циклів ви хочете розділити запит.
Передумови
Спочатку потрібно встановити (як показано в іншій моїй відповіді) необхідні значення всередині pre_get_posts
фільтра. Там ви, швидше за все, встановите posts_per_page
і cat
. Приклад без pre_get_posts
-фільтра:
$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, тому нам не потрібно встановлювати все. Код добре прокоментований, але ось кілька зауважень:
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 on GitHub. Просто завантажте та активуйте його. Він виводить / скидає ідентифікатор кожного циклового повідомлення як зворотний виклик loop_start
дії. Це означає, що ви можете отримати досить невеликий вихід, залежно від налаштувань, кількості повідомлень та конфігурації. Будь-ласка, додайте кілька заяв про переривання і змініть var_dump()
s в кінці того, що ви хочете бачити і де ви хочете його бачити. Це просто доказ концепції.