From 2d6656d5f9095d70b9c4b809c088dd91e296d22c Mon Sep 17 00:00:00 2001 From: dchiamp Date: Fri, 27 Mar 2026 17:39:30 -0700 Subject: [PATCH] Fix posts query filtering by post_type and term_slug - term_slug no longer overwrites an explicit post_type param; taxonomy inference only runs when post_type is omitted - term_slug taxonomy search is now scoped to taxonomies registered for the requested post_type(s), preventing cross-CPT term collisions - post_type and term_slug both accept comma-separated values; multiple term slugs are matched with an OR tax_query grouped by taxonomy Co-Authored-By: Claude Sonnet 4.6 --- includes/class-rest-posts-controller.php | 3 +- includes/utils/class-post.php | 53 ++++++++++++++++++------ 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/includes/class-rest-posts-controller.php b/includes/class-rest-posts-controller.php index 296318b..1e15517 100644 --- a/includes/class-rest-posts-controller.php +++ b/includes/class-rest-posts-controller.php @@ -122,9 +122,8 @@ public function get_collection_params() { 'type' => 'integer', ), 'post_type' => array( - 'description' => __( 'Post type', 'fuxt-api' ), + 'description' => __( 'Post type, or comma-separated list of post types', 'fuxt-api' ), 'type' => 'string', - 'enum' => Utils::get_post_types(), ), 'fields' => array( 'description' => __( 'Additional fields to return. Comma separated string of fields.', 'fuxt-api' ), diff --git a/includes/utils/class-post.php b/includes/utils/class-post.php index 57b09f3..a9a7145 100644 --- a/includes/utils/class-post.php +++ b/includes/utils/class-post.php @@ -342,24 +342,46 @@ public static function get_posts( $params, $additional_fields ) { } if ( ! empty( $params['term_slug'] ) ) { + // If post_type is specified, limit taxonomy search to those registered for those post types. + if ( isset( $params['post_type'] ) ) { + $requested_types = array_map( 'trim', explode( ',', $params['post_type'] ) ); + $searchable_taxonomies = array_unique( array_merge( ...array_map( 'get_object_taxonomies', $requested_types ) ) ); + } else { + $searchable_taxonomies = get_taxonomies(); + } + + $term_slugs = array_map( 'trim', explode( ',', $params['term_slug'] ) ); + $terms = get_terms( array( - 'taxonomy' => get_taxonomies(), - 'slug' => $params['term_slug'], + 'taxonomy' => $searchable_taxonomies, + 'slug' => $term_slugs, ) ); if ( ! empty( $terms ) ) { - $query_params['tax_query'] = array( - array( - 'taxonomy' => $terms[0]->taxonomy, + // Group matched terms by taxonomy, then build one tax_query clause per taxonomy (OR relation). + $by_taxonomy = array(); + foreach ( $terms as $term ) { + $by_taxonomy[ $term->taxonomy ][] = $term->slug; + } + + $tax_query = array( 'relation' => 'OR' ); + foreach ( $by_taxonomy as $taxonomy => $slugs ) { + $tax_query[] = array( + 'taxonomy' => $taxonomy, 'field' => 'slug', - 'terms' => $terms[0]->slug, - ), - ); + 'terms' => $slugs, + 'operator' => 'IN', + ); + } + $query_params['tax_query'] = $tax_query; - $taxonomy = get_taxonomy( $terms[0]->taxonomy ); - $query_params['post_type'] = $taxonomy->object_type; + // Only infer post_type from taxonomy if not explicitly provided. + if ( ! isset( $params['post_type'] ) ) { + $taxonomy = get_taxonomy( array_key_first( $by_taxonomy ) ); + $query_params['post_type'] = $taxonomy->object_type; + } // Default order is menu_order for hierarchical post types such as page. if ( is_post_type_hierarchical( $parent_post->post_type ) ) { @@ -373,10 +395,17 @@ public static function get_posts( $params, $additional_fields ) { if ( ! isset( $query_params['post_type'] ) ) { if ( isset( $params['post_type'] ) ) { - if ( ! in_array( $params['post_type'], Utils::get_post_types() ) ) { + $requested_types = array_map( 'trim', explode( ',', $params['post_type'] ) ); + $valid_types = Utils::get_post_types(); + $validated_types = array_filter( $requested_types, fn( $t ) => in_array( $t, $valid_types ) ); + + if ( empty( $validated_types ) ) { return null; } - $query_params['post_type'] = $params['post_type']; + + $query_params['post_type'] = count( $validated_types ) === 1 + ? reset( $validated_types ) + : array_values( $validated_types ); } else { $query_params['post_type'] = 'post'; }