問題描述
我正在嘗試通過首先顯示圖片的帖子,然後最後發佈沒有圖像的帖子來排序類別中的帖子。我已經設法通過運行兩個查詢,現在我想將兩個查詢合併在一起。
我有以下幾點:
<?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_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 (Standard PHP Library)及其接口和迭代器。我們現在得到的是一個 FilterIterator,可以方便地從我們的循環中移除物品。它擴展了 PHP SPL Filter Iterator,所以我們不必設置所有內容。代碼很好評論,但這裏有一些註釋:
-
accept()方法允許定義允許循環該項目的標準 – 否則。 -
在這種方法中,我們使用
WP_Query::the_post(),因此您可以簡單地使用模板文件循環中的每個模板標籤。 -
當我們到達最後一個項目時,我們也監控循環和倒回帖子。這允許循環無限量的循環,而不重置我們的查詢。
-
有一種不是
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 從最低到最高 (如 order 和 ASC 所述) 。當您使用 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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。