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: |
|
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)
This ticket was mentioned in Slack in #core by sirlouen. View the logs.
3 months ago
#4
@
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.
Additional remark: when I comment out the code from core I mentioned, the correct number of posts is returned.