问题描述
我正在尝试通过首先显示图片的帖子,然后最后发布没有图像的帖子来排序类别中的帖子。我已经设法通过运行两个查询,现在我想将两个查询合并在一起。
我有以下几点:
<?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 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。