問題描述

我正在嘗試透過首先顯示圖片的帖子,然後最後釋出沒有影像的帖子來排序類別中的帖子。我已經設法透過執行兩個查詢,現在我想將兩個查詢合併在一起。

我有以下幾點:

<?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...

如何解決這個錯誤?

最佳解決方案

單個查詢

想一想,有一個機會,你可以去單/主查詢。或者換句話說:當您可以使用預設值時,不需要另外兩個查詢。如果您無法使用預設值,則不需要單個查詢,無論您要分割查詢的迴圈數為多少。

Prerequisites

首先,您需要在 pre_get_posts 過濾器中設定所需的值 (如我的其他答案所示) 。在那裡你可能會設定 posts_per_pagecat 。沒有 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 (Standard PHP Library)及其介面和迭代器。我們現在得到的是一個 FilterIterator,可以方便地從我們的迴圈中移除物品。它擴充套件了 PHP SPL Filter Iterator,所以我們不必設定所有內容。程式碼很好評論,但這裡有一些註釋:

  1. accept()方法允許定義允許迴圈該專案的標準 – 否則。

  2. 在這種方法中,我們使用 WP_Query::the_post(),因此您可以簡單地使用模板檔案迴圈中的每個模板標籤。

  3. 當我們到達最後一個專案時,我們也監控迴圈和倒回帖子。這允許迴圈無限量的迴圈,而不重置我們的查詢。

  4. 有一種不是 FilterIterator 規範的自定義方法:deny()。這種方法特別方便,因為它只包含我們的 「process or not」-statement,我們可以輕鬆地在後面的類中覆蓋它,而不需要知道除 WordPress 模板標籤之外的任何東西。

如何迴圈?

有了這個新的 Iterator,我們不再需要 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() ) );
}

測試它

以下 test plugin 可作為 GitHub 上的 Gist 使用。只需上傳並啟用它。它在 loop_start 操作上輸出/轉儲每個迴圈的帖子的 ID 作為回撥。這意味著根據您的設定,帖子數量和配置可能會產生相當多的輸出。請新增一些中止語句,並將 var_dump()更改為要檢視的內容以及您想要檢視的內容。這只是一個概念證明。

次佳解決方案

雖然這不是解決這個問題的最好辦法 (@凱撒的答案是),要直接回答問題,實際的查詢結果將在 $loop->posts$loop2->posts 中,所以…

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

… 應該工作,但是您需要使用 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'));

當然,這些解決方案代表了多個查詢,這就是為什麼 @凱撒對於 WP_Query 可以處理所需邏輯的更好的方法。

第三種解決方案

實際上有 meta_query(或 WP_Meta_Query) – 它可以搜尋_thumbnail_id 行陣列。如果您檢查 EXISTS,您只能獲得那些具有此欄位的那些。將其與 cat 引數相結合,您將只會收到分配給 ID 為 1 並附有縮圖的類別的帖子。如果您由 meta_value_num 訂購,那麼您實際上將按縮圖 ID 從最低到最高 (如 orderASC 所述) 。當您使用 EXISTS 作為 compare 值時,不必指定 value

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

現在,當迴圈使用它們時,您可以收集所有的 ID,並在輔助查詢的獨家宣告中使用它們:

$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

當然,你可以更聰明,簡單地改變 pre_get_posts 中的 SQL 語句,不浪費主查詢。您也可以簡單地在 pre_get_posts 過濾器回撥中執行第一個查詢 ($thumbsUp) 。

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;
}

這改變了主查詢,所以我們只會收到附有縮圖的帖子。現在我們可以 (如上面的第一個查詢所示) 在主迴圈中收集 ID,然後新增顯示其餘帖子 (沒有縮圖) 的第二個查詢。

除此之外,您可以更智慧地更改 posts_clauses,並透過元值直接修改查詢。看看 this answer,因為目前只是一個起點。

參考文獻

注:本文內容整合自 Google/Baidu/Bing 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。