Как объединить два запроса вместе

Я пытаюсь заказать сообщения в категории путем показа сообщений с изображениями сначала и затем отправляю без изображений в последний раз. Мне удалось сделать это путем выполнения двух запросов, и теперь я хочу объединить два запроса вместе.

У меня есть следующее:

<?php
$loop = new WP_Query( array('meta_key' => '_thumbnail_id', 'cat' => 1 ) );
$loop2 = new WP_Query( array('meta_key' => '', 'cat' => 1 ) );
$mergedloops = array_merge($loop, $loop2);

while($mergedloops->have_posts()): $mergedloops->the_post(); ?>

Но когда я пытаюсь просмотреть страницу затем, я получаю следующую ошибку:

 Fatal error: Call to a member function have_posts() on a non-object in...

Я затем пытался бросить array_merge к объекту, но я получил следующую ошибку:

Fatal error: Call to undefined method stdClass::have_posts() in...

Как я могу зафиксировать эту ошибку?

10
16.01.2014, 17:12
4 ответа

Единый запрос

Мысль об этом немного больше и существует шанс, что можно пойти с единственным / основным запросом. Или другими словами: Никакая потребность в двух дополнительных запросах, когда можно работать с по умолчанию. И в случае, если Вы не можете работать с по умолчанию, Вам не будут нужны больше, чем единый запрос, неважно, для того, сколько циклов Вы хотите разделить запрос.

Предпосылки

Сначала необходимо установить (как показано в моем другом ответе) необходимые значения внутри a 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;
    }
}

Этот плагин делает одну вещь: Это использует SPL PHP (Стандартная Библиотека PHP) и ее Интерфейсы и Итераторы. То, что мы теперь получили, является a FilterIterator это позволяет нам удобно удалять объекты из нашего цикла. Это расширяет Итератор Фильтра SPL PHP, таким образом, мы не должны устанавливать все. Код хорошо прокомментирован, но здесь является некоторыми примечаниями:

  1. accept() метод позволяет определять критерии, которые позволяют цикличному выполнению объект - или нет.
  2. В том методе мы используем WP_Query::the_post(), таким образом, можно просто использовать каждый тег шаблона в шаблонном цикле файлов.
  3. И также мы контролирующий цикл и перематывающий сообщения, когда мы достигаем последнего объекта. Это позволяет циклично выполнять канавку бесконечная сумма циклов, не сбрасывая наш запрос.
  4. Существует один пользовательский метод, который не является частью 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() ) );
}

Протестируйте его.

Следующий тестовый плагин доступен как Суть на GitHub. Просто загрузите и активируйте его. Это производит/выводит идентификатор каждого циклично выполненного сообщения как обратный вызов на loop_start действие. Это означает, что это могло бы получить вполне немного вывода в зависимости от Вашей установки, количества сообщений и конфигурации. Добавьте некоторые операторы преждевременного завершения и изменитесь var_dump()s в конце тому, что Вы хотите видеть и где Вы хотите видеть его. Это - просто подтверждение концепции.

8
19.02.2020, 22:00

На самом деле существует meta_query (или WP_Meta_Query) - который берет массив массивов - где можно искать _thumbnail_id строки. Если Вы затем проверяете на EXISTS, Вы можете получить только тех, которые имеют это поле. Объединение этого с cat аргумент, Вы только получите сообщения, которые присвоены категории с идентификатором 1 и этому присоединили миниатюру. Если Вы затем заказываете им meta_value_num, затем Вы на самом деле закажете им идентификатором миниатюры, самым низким к самому высокому (как указано с order и ASC). Вы не должны указывать value когда Вы используете EXISTS как compare значение.

$thumbsUp = new WP_Query( array( 
    'cat'        => 1,
    'meta_query' => array( 
        array(
            'key'     => '_thumbnail_id',
            'compare' => 'EXISTS',
        ),
    ),
    'orderby'    => 'meta_value_num',
    'order'      => 'ASC',
) );

Теперь, когда канавка цикличного выполнения их, можно собрать все идентификаторы и использовать их в эксклюзивном операторе для вспомогательного запроса:

$postsWithThumbnails = array();
if ( $thumbsUp->have_posts() )
{
    while ( $thumbsUp->have_posts() )
    {
        $thumbsUp->the_post();

        // collect them
        $postsWithThumbnails[] = get_the_ID();

        // do display/rendering stuff here
    }
}

Теперь можно добавить второй запрос. Никакая потребность в wp_reset_postdata() здесь - все находится в переменной а не основном запросе.

$noThumbnails = new WP_Query( array(
    'cat'          => 1,
    'post__not_in' => $postsWithThumbnails
) );
// Loop through this posts

Конечно, можно быть намного более умными и просто изменить SQL-оператор внутри pre_get_posts не потратить впустую основной запрос. Вы могли также просто сделать первый запрос ($thumbsUp выше) внутри a pre_get_posts обратный вызов фильтра.

add_filter( 'pre_get_posts', 'wpse130009excludeThumbsPosts' );
function wpse130009excludeThumbsPosts( $query )
{
    if ( $query->is_admin() )
        return $query;

    if ( ! $query->is_main_query() )
        return $query;

    if ( 'post' !== $query->get( 'post_type' ) )
        return $query;

    // Only needed if this query is for the category archive for cat 1
    if (
        $query->is_archive() 
        AND ! $query->is_category( 1 )
    )
        return $query;

    $query->set( 'meta_query', array( 
        array(
            'key'     => '_thumbnail_id',
            'compare' => 'EXISTS',
        ),
    ) );
    $query->set( 'orderby', 'meta_value_num' );

    // In case we're not on the cat = 1 category archive page, we need the following:
    $query->set( 'category__in', 1 );

    return $query;
}

Это изменило основной запрос, таким образом, мы только получим сообщения, которым присоединили миниатюру. Теперь мы можем (как показано в 1-м запросе выше), собирают идентификаторы во время основного цикла и затем добавляют второй запрос, который отображает остальную часть сообщений (без миниатюры).

Кроме этого можно стать еще более умными и измениться posts_clauses и измените запрос, непосредственно заказывают значением meta. Смотрите на этот ответ, поскольку текущий является просто начальной точкой.

3
19.02.2020, 22:00

То, в чем Вы нуждаетесь, является на самом деле третьим запросом для получения всех сообщений сразу. Затем Вы изменяете свои первые два запроса для не возврата сообщений, но только идентификаторов сообщения в формате, с которым можно работать.

'fields'=>'ids' параметр заставит запрос на самом деле возвратить массив соответствия Идентификационным номерам сообщения. Но мы не хотим целый объект запроса, таким образом, мы используем get_posts для них вместо этого.

Во-первых, получите идентификаторы сообщения, в которых мы нуждаемся:

$imageposts = get_posts( array('fields'=>'ids', 'meta_key' => '_thumbnail_id', 'cat' => 1 ) );
$nonimageposts = get_posts( array('fields'=>'ids', 'meta_key' => '', 'cat' => 1 ) );

$imageposts и $nonimageposts теперь оба будут массивом Идентификационных номеров сообщения, таким образом, мы объединим их

$mypostids = array_merge( $imageposts, $nonimageposts );

Устраните дублированные Идентификационные номера...

$mypostids = array_unique( $mypostids );

Теперь, сделайте запрос для получения фактических сообщений в указанном порядке:

$loop = new WP_Query( array('post__in' => $mypostids, 'ignore_sticky_posts' => true, 'orderby' => 'post__in' ) );

Переменная $loop является теперь объектом WP_Query с Вашими сообщениями в нем.

3
19.02.2020, 22:00
  • 1
    Спасибо за это. Найденный этим, чтобы быть наименее сложным решением для хранения одной циклической структуры и несложных вычислений разбиения на страницы. –  Jay Neely 07.03.2016, 19:46

В то время как это не лучший способ решить эту проблему (ответ @kaiser), для ответа на вопрос непосредственно, фактические результаты запроса будут в $loop->posts и $loop2->posts, так...

$mergedloops = array_merge($loop->posts, $loop2->posts);

... должен работать, но необходимо было бы использовать a foreach цикл а не WP_Query основанная стандартная циклическая структура как объединяющиеся запросы как этот повредится WP_Query возразите "meta" данным о цикле.

Можно также сделать это:

$loop = new WP_Query( array('fields' => 'ids','meta_key' => '_thumbnail_id', 'cat' => 1 ) );
$loop2 = new WP_Query( array('fields' => 'ids','meta_key' => '', 'cat' => 1 ) );
$ids = array_merge($loop->posts, $loop2->posts);
$merged = new WP_Query(array('post__in' => $ids,'orderby' => 'post__in'));

Конечно, те решения представляют несколько запросов, который является, почему @Kaiser является лучшим подходом для случаев как это где WP_Query может обработать необходимую логику.

6
19.02.2020, 22:00

Теги

Похожие вопросы