I’m trying to query posts that contain ALL the supplied tags, with a URL like this:
/wp-json/wp/v2/posts?tags=22,23&tax_relation=AND
However, it still returns posts with any of the tags, not all of them.
I dug into WP’s code and found this from wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
:
private function prepare_tax_query( array $args, WP_REST_Request $request ) {
$relation = $request['tax_relation'];
if ( $relation ) {
$args['tax_query'] = array( 'relation' => $relation );
}
$taxonomies = wp_list_filter(
get_object_taxonomies( $this->post_type, 'objects' ),
array( 'show_in_rest' => true )
);
foreach ( $taxonomies as $taxonomy ) {
$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
$tax_include = $request[ $base ];
$tax_exclude = $request[ $base . '_exclude' ];
if ( $tax_include ) {
$terms = array();
$include_children = false;
$operator = 'IN';
if ( rest_is_array( $tax_include ) ) {
$terms = $tax_include;
} elseif ( rest_is_object( $tax_include ) ) {
$terms = empty( $tax_include['terms'] ) ? array() : $tax_include['terms'];
$include_children = ! empty( $tax_include['include_children'] );
if ( isset( $tax_include['operator'] ) && 'AND' === $tax_include['operator'] ) {
$operator = 'AND';
}
}
The bit that should be applying the AND
clause is this bit:
if ( isset( $tax_include['operator'] ) && 'AND' === $tax_include['operator'] ) {
$operator = 'AND';
}
But a few lines up it does this:
$tax_include = $request[ $base ];
And if I dump out the value of $tax_include
, I get this:
Array
(
[0] => 22
[1] => 23
)
So I don’t see how half of WP’s code here is supposed to work? The terms
and include_children
and operator
keys will never exist on $tax_include
as it’s just an array of IDs from the request.
If I manually add this in after the initial code chunk:
$operator = 'AND';
It works fine and matches all the tags.
Surely this is a bug in WP and just flat out doesn’t work, or am I misunderstanding something here?