如何将两个查询合并在一起

时间:2014-01-16 作者:Howli

我试图通过先显示带有图像的帖子,然后最后显示没有图像的帖子来排序类别中的帖子。我已经通过运行两个查询来做到这一点,现在我想将这两个查询合并在一起。

我有以下几点:

<?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\\u merge强制转换为对象,但出现以下错误:

Fatal error: Call to undefined method stdClass::have_posts() in...
如何修复此错误?

4 个回复
SO网友:kaiser

一个单独的查询

再仔细考虑一下,就有可能使用一个单独的/主查询。或者换句话说:当您可以使用默认查询时,不需要另外两个查询。如果无法使用默认查询,则无论要拆分多少个循环,都只需要一个查询。

先决条件

首先,您需要在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过滤器迭代器,因此我们不必设置所有内容。代码注释得很好,但这里有一些注释:

accept() 方法允许定义允许或不允许循环项的条件WP_Query::the_post(), 因此,您可以简单地使用模板文件循环中的每个模板标记FilterIterator 规格:deny(). 这个方法特别方便,因为它只包含我们的“process or not”语句,我们可以在以后的类中轻松地覆盖它,而无需知道除了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() ) );
}
对其进行测试以下内容test plugin 作为GitHub上的Gist提供。只需上传并激活即可。它将每个循环帖子的ID作为回调输出/转储到loop_start 行动这意味着根据您的设置、帖子数量和配置,可能会得到相当多的输出。请添加一些中止语句并更改var_dump()在你想看的内容和你想看的地方的末尾。这只是概念的证明。

SO网友:s_ha_dum

虽然这不是解决此问题的最佳方法(@kaiser的答案是),但要直接回答问题,实际查询结果将为$loop->posts$loop2->posts, 所以

$mergedloops = array_merge($loop->posts, $loop2->posts);
。。。应该可以,但你需要使用foreach 循环而不是WP_Query 基于标准循环结构,像这样的合并查询将打破WP_Query 对象有关循环的“元数据”。

您也可以这样做:

$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’s是更好的方法WP_Query 能够处理必要的逻辑。

SO网友:Otto

实际上,您需要第三个查询来同时获取所有帖子。然后,将前两个查询更改为不返回帖子,而只返回可以使用的格式的帖子ID。

这个\'fields\'=>\'ids\' 参数将使查询实际返回匹配的帖子ID号数组。但我们不需要整个查询对象,因此我们使用get\\u posts来处理这些对象。

首先,获取我们需要的帖子ID:

$imageposts = get_posts( array(\'fields\'=>\'ids\', \'meta_key\' => \'_thumbnail_id\', \'cat\' => 1 ) );
$nonimageposts = get_posts( array(\'fields\'=>\'ids\', \'meta_key\' => \'\', \'cat\' => 1 ) );
$imageposts和$nonimageposts现在都是帖子ID号的数组,所以我们将它们合并

$mypostids = array_merge( $imageposts, $nonimageposts );
消除重复的ID号。。。

$mypostids = array_unique( $mypostids );
现在,按照指定的顺序进行查询以获取实际帖子:

$loop = new WP_Query( array(\'post__in\' => $mypostids, \'ignore_sticky_posts\' => true, \'orderby\' => \'post__in\' ) );
$loop变量现在是一个WP\\u查询对象,其中包含您的帖子。

SO网友:kaiser

事实上meta_query (或WP_Meta_Query) - 其中包含一个数组,您可以在其中搜索_thumbnail_id 行。如果您随后检查EXISTS, 您只能获得具有此字段的字段。将此与cat 参数,您将只获得分配给ID为的类别的帖子1 并附有缩略图。如果您按照meta_value_num, 然后,您将按缩略图ID从低到高(如orderASC). 您不必指定value 当您使用EXISTScompare 价值

$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
当然,您可以更聪明,只需更改其中的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;
}
这改变了主查询,因此我们将只获取附加缩略图的帖子。现在,我们可以(如上面的第一个查询所示)在主循环期间收集ID,然后添加第二个查询来显示其余的帖子(没有缩略图)。

除此之外,你还可以变得更聪明posts_clauses 并根据元值直接修改查询顺序。看看this answer 因为目前这只是一个起点。

结束

相关推荐

使用新的WP-Query()从循环中过滤后期格式;

嗨,我目前正在为我的博客构建一个主题。下面的代码指向最新的帖子(特色帖子)。因为这将有一个不同的风格比所有其他职位。然而我想过滤掉帖子格式:链接使用我在循环中定义的WP查询,因为它给我带来了更多的灵活性。我该怎么做呢? <?php $featured = new WP_Query(); $featured->query(\'showposts=1\'); ?> <?php while ($featured->have_post