Make WordPress Core

Opened 6 years ago

Last modified 3 months ago

#48047 new defect (bug)

Querying (non-CPT) posts from more than one (native) category only returning posts from first category

Reported by: lialyn's profile lialyn Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 5.2.3
Component: Query Keywords: 2nd-opinion close
Focuses: template Cc:

Description

I tried selecting posts from two categories using this code:

$wpQuery = new WP_Query([
    'post_type'      => 'post',
    'posts_per_page' => 30,
    'post_status'    => 'publish',
    'category__in'   => [5, 6],
]);

$posts = $wpQuery->get_posts();

This code only returns posts from the first category. I tracked this back to the query variables category_name and cat being set to the first queried category in the get_posts() method. Code from core, class-wp-query.php, line 2173:

if ('category' === $queried_taxonomy) {

    $the_cat = get_term_by($queried_items['field'], $queried_items['terms'][0], 'category');
    if ($the_cat) {
        $this->set('cat', $the_cat->term_id);
        $this->set('category_name', $the_cat->slug);
    }
    unset($the_cat);
}

It was the same with any equivalent of the category__in option, such as using a comma separated list as an argument ('category_name' => 'news,video') or writing my own tax_query.

When I access the posts property on the query object ($wpQuery->posts), the correct number of posts is returned. The problem only surfaces when get_posts() is called.

The bug persists on the Twenty Ninetheen theme without any plugins enabled.

Why is category_name/cat even set to the first category term? If there is a reason, it would make sense to filter out any "duplicate" category query vars present before the SQL query is built, because both category__in and category_name/cat don't make sense.

I'm looking forward to insights on this.

Change History (4)

#1 @lialyn
6 years ago

Additional remark: when I comment out the code from core I mentioned, the correct number of posts is returned.

This ticket was mentioned in Slack in #core by sirlouen. View the logs.


3 months ago

#3 @SirLouen
3 months ago

  • Component changed from Posts, Post Types to Query

#4 @mindctrl
3 months ago

  • Keywords 2nd-opinion close added

Hi @lialyn, thanks for the report. I believe this to be intentional, due to the code you showed from get_posts() internals. I think the suggested way to "get" these posts, is to loop through using the methods like this example:

$args = array(
	'post_type' => 'post',
	'posts_per_page' => 30,
	'post_status' => 'publish',
	'category__in' => [4,5],
);

$query = new WP_Query($args);

if ( $query->have_posts() ) {
	while ( $query->have_posts() ) {
		$query->the_post();
		// Output your post content here
		the_title( '<h2>', '</h2>' );
		the_content();
	}
	wp_reset_postdata();
} else {
	echo 'No posts found.';
}

You can also access them the way you mentioned, via the public $query->posts property.

Another way is to use the global get_posts() function, which should give you the full array of WP_Post objects:

$posts = get_posts( $args );

I don't think there's a bug here we can fix, due to backwards compatibility, but curious to hear other opinions.

Note: See TracTickets for help on using tickets.