From 998a74eb6f2a37b2293a44247dc9ab40ab943488 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Mon, 30 Sep 2024 01:19:16 +0000 Subject: [PATCH] REST API/Editor: Support post formats in Query Block & Posts API. Introduces post format support for both the Query Block with the new parameter `format`. In the `build_query_vars_from_query_block()` function, this is converted to a `post_format` taxonomy query passed to `WP_Query`. Also introduces the `format` parameter to the REST API's Posts controller to support the feature in the Query block. The parameter type is an enumerated string accepted the post formats supported by each post type. Props poena, mukesh27, mamaduka, noisysocks, TimothyBlynJacobs. Fixes #62014. Built from https://develop.svn.wordpress.org/trunk@59115 git-svn-id: http://core.svn.wordpress.org/trunk@58511 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/blocks.php | 82 +++++++++++++++++-- .../class-wp-rest-posts-controller.php | 65 +++++++++++++++ wp-includes/version.php | 2 +- 3 files changed, 142 insertions(+), 7 deletions(-) diff --git a/wp-includes/blocks.php b/wp-includes/blocks.php index a479992faa..edcc68284a 100644 --- a/wp-includes/blocks.php +++ b/wp-includes/blocks.php @@ -2335,6 +2335,7 @@ function wp_migrate_old_typography_shape( $metadata ) { * * @since 5.8.0 * @since 6.1.0 Added `query_loop_block_query_vars` filter and `parents` support in query. + * @since 6.7.0 Added support for the `format` property in query. * * @param WP_Block $block Block instance. * @param int $page Current query's page. @@ -2347,6 +2348,7 @@ function build_query_vars_from_query_block( $block, $page ) { 'order' => 'DESC', 'orderby' => 'date', 'post__not_in' => array(), + 'tax_query' => array(), ); if ( isset( $block->context['query'] ) ) { @@ -2396,35 +2398,103 @@ function build_query_vars_from_query_block( $block, $page ) { } // Migrate `categoryIds` and `tagIds` to `tax_query` for backwards compatibility. if ( ! empty( $block->context['query']['categoryIds'] ) || ! empty( $block->context['query']['tagIds'] ) ) { - $tax_query = array(); + $tax_query_back_compat = array(); if ( ! empty( $block->context['query']['categoryIds'] ) ) { - $tax_query[] = array( + $tax_query_back_compat[] = array( 'taxonomy' => 'category', 'terms' => array_filter( array_map( 'intval', $block->context['query']['categoryIds'] ) ), 'include_children' => false, ); } if ( ! empty( $block->context['query']['tagIds'] ) ) { - $tax_query[] = array( + $tax_query_back_compat[] = array( 'taxonomy' => 'post_tag', 'terms' => array_filter( array_map( 'intval', $block->context['query']['tagIds'] ) ), 'include_children' => false, ); } - $query['tax_query'] = $tax_query; + $query['tax_query'] = array_merge( $query['tax_query'], $tax_query_back_compat ); } if ( ! empty( $block->context['query']['taxQuery'] ) ) { - $query['tax_query'] = array(); + $tax_query = array(); foreach ( $block->context['query']['taxQuery'] as $taxonomy => $terms ) { if ( is_taxonomy_viewable( $taxonomy ) && ! empty( $terms ) ) { - $query['tax_query'][] = array( + $tax_query[] = array( 'taxonomy' => $taxonomy, 'terms' => array_filter( array_map( 'intval', $terms ) ), 'include_children' => false, ); } } + $query['tax_query'] = array_merge( $query['tax_query'], $tax_query ); } + if ( ! empty( $block->context['query']['format'] ) && is_array( $block->context['query']['format'] ) ) { + $formats = $block->context['query']['format']; + /* + * Validate that the format is either `standard` or a supported post format. + * - First, add `standard` to the array of valid formats. + * - Then, remove any invalid formats. + */ + $valid_formats = array_merge( array( 'standard' ), get_post_format_slugs() ); + $formats = array_intersect( $formats, $valid_formats ); + + /* + * The relation needs to be set to `OR` since the request can contain + * two separate conditions. The user may be querying for items that have + * either the `standard` format or a specific format. + */ + $formats_query = array( 'relation' => 'OR' ); + + /* + * The default post format, `standard`, is not stored in the database. + * If `standard` is part of the request, the query needs to exclude all post items that + * have a format assigned. + */ + if ( in_array( 'standard', $formats, true ) ) { + $formats_query[] = array( + 'taxonomy' => 'post_format', + 'field' => 'slug', + 'operator' => 'NOT EXISTS', + ); + // Remove the `standard` format, since it cannot be queried. + unset( $formats[ array_search( 'standard', $formats, true ) ] ); + } + // Add any remaining formats to the formats query. + if ( ! empty( $formats ) ) { + // Add the `post-format-` prefix. + $terms = array_map( + static function ( $format ) { + return "post-format-$format"; + }, + $formats + ); + $formats_query[] = array( + 'taxonomy' => 'post_format', + 'field' => 'slug', + 'terms' => $terms, + 'operator' => 'IN', + ); + } + + /* + * Add `$formats_query` to `$query`, as long as it contains more than one key: + * If `$formats_query` only contains the initial `relation` key, there are no valid formats to query, + * and the query should not be modified. + */ + if ( count( $formats_query ) > 1 ) { + // Enable filtering by both post formats and other taxonomies by combining them with `AND`. + if ( empty( $query['tax_query'] ) ) { + $query['tax_query'] = $formats_query; + } else { + $query['tax_query'] = array( + 'relation' => 'AND', + $query['tax_query'], + $formats_query, + ); + } + } + } + if ( isset( $block->context['query']['order'] ) && in_array( strtoupper( $block->context['query']['order'] ), array( 'ASC', 'DESC' ), true ) diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index d1938872ed..c4db4871df 100644 --- a/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -346,6 +346,59 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { $args = $this->prepare_tax_query( $args, $request ); + if ( ! empty( $request['format'] ) ) { + $formats = $request['format']; + /* + * The relation needs to be set to `OR` since the request can contain + * two separate conditions. The user may be querying for items that have + * either the `standard` format or a specific format. + */ + $formats_query = array( 'relation' => 'OR' ); + + /* + * The default post format, `standard`, is not stored in the database. + * If `standard` is part of the request, the query needs to exclude all post items that + * have a format assigned. + */ + if ( in_array( 'standard', $formats, true ) ) { + $formats_query[] = array( + 'taxonomy' => 'post_format', + 'field' => 'slug', + 'operator' => 'NOT EXISTS', + ); + // Remove the `standard` format, since it cannot be queried. + unset( $formats[ array_search( 'standard', $formats, true ) ] ); + } + + // Add any remaining formats to the formats query. + if ( ! empty( $formats ) ) { + // Add the `post-format-` prefix. + $terms = array_map( + static function ( $format ) { + return "post-format-$format"; + }, + $formats + ); + + $formats_query[] = array( + 'taxonomy' => 'post_format', + 'field' => 'slug', + 'terms' => $terms, + 'operator' => 'IN', + ); + } + + // Enable filtering by both post formats and other taxonomies by combining them with `AND`. + if ( isset( $args['tax_query'] ) ) { + $args['tax_query'][] = array( + 'relation' => 'AND', + $formats_query, + ); + } else { + $args['tax_query'] = $formats_query; + } + } + // Force the post_type argument, since it's not a user input variable. $args['post_type'] = $this->post_type; @@ -2992,6 +3045,18 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { ); } + if ( post_type_supports( $this->post_type, 'post-formats' ) ) { + $query_params['format'] = array( + 'description' => __( 'Limit result set to items assigned one or more given formats.' ), + 'type' => 'array', + 'uniqueItems' => true, + 'items' => array( + 'enum' => array_values( get_post_format_slugs() ), + 'type' => 'string', + ), + ); + } + /** * Filters collection parameters for the posts controller. * diff --git a/wp-includes/version.php b/wp-includes/version.php index 0ef870e285..f16b23c464 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -16,7 +16,7 @@ * * @global string $wp_version */ -$wp_version = '6.7-alpha-59114'; +$wp_version = '6.7-alpha-59115'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.