Skip to content

Commit

Permalink
REST API/Editor: Support post formats in Query Block & Posts API.
Browse files Browse the repository at this point in the history
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.


git-svn-id: https://develop.svn.wordpress.org/trunk@59115 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information
peterwilsoncc committed Sep 30, 2024
1 parent 2cd811f commit b1db74a
Show file tree
Hide file tree
Showing 5 changed files with 426 additions and 6 deletions.
82 changes: 76 additions & 6 deletions src/wp-includes/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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'] ) ) {
Expand Down Expand Up @@ -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 )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,59 @@ public function get_items( $request ) {

$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;

Expand Down Expand Up @@ -2992,6 +3045,18 @@ public function get_collection_params() {
);
}

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.
*
Expand Down
133 changes: 133 additions & 0 deletions tests/phpunit/tests/blocks/wpBlock.php
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,134 @@ public function test_build_query_vars_from_query_block() {
);
}

/**
* @ticket 62014
*/
public function test_build_query_vars_from_query_block_standard_post_formats() {
$this->registry->register(
'core/example',
array( 'uses_context' => array( 'query' ) )
);

$parsed_blocks = parse_blocks( '<!-- wp:example {"ok":true} -->a<!-- wp:example /-->b<!-- /wp:example -->' );
$parsed_block = $parsed_blocks[0];
$context = array(
'query' => array(
'postType' => 'post',
'format' => array( 'standard' ),
),
);
$block = new WP_Block( $parsed_block, $context, $this->registry );
$query = build_query_vars_from_query_block( $block, 1 );

$this->assertSame(
array(
'post_type' => 'post',
'order' => 'DESC',
'orderby' => 'date',
'post__not_in' => array(),
'tax_query' => array(
'relation' => 'OR',
array(
'taxonomy' => 'post_format',
'field' => 'slug',
'operator' => 'NOT EXISTS',
),
),
),
$query
);
}

/**
* @ticket 62014
*/
public function test_build_query_vars_from_query_block_post_format() {
$this->registry->register(
'core/example',
array( 'uses_context' => array( 'query' ) )
);

$parsed_blocks = parse_blocks( '<!-- wp:example {"ok":true} -->a<!-- wp:example /-->b<!-- /wp:example -->' );
$parsed_block = $parsed_blocks[0];
$context = array(
'query' => array(
'postType' => 'post',
'format' => array( 'aside' ),
),
);
$block = new WP_Block( $parsed_block, $context, $this->registry );
$query = build_query_vars_from_query_block( $block, 1 );

$this->assertSame(
array(
'post_type' => 'post',
'order' => 'DESC',
'orderby' => 'date',
'post__not_in' => array(),
'tax_query' => array(
'relation' => 'OR',
array(
'taxonomy' => 'post_format',
'field' => 'slug',
'terms' => array( 'post-format-aside' ),
'operator' => 'IN',
),
),
),
$query
);
}
/**
* @ticket 62014
*/
public function test_build_query_vars_from_query_block_post_formats_with_category() {
$this->registry->register(
'core/example',
array( 'uses_context' => array( 'query' ) )
);

$parsed_blocks = parse_blocks( '<!-- wp:example {"ok":true} -->a<!-- wp:example /-->b<!-- /wp:example -->' );
$parsed_block = $parsed_blocks[0];
$context = array(
'query' => array(
'postType' => 'post',
'format' => array( 'standard' ),
'categoryIds' => array( 56 ),
),
);
$block = new WP_Block( $parsed_block, $context, $this->registry );
$query = build_query_vars_from_query_block( $block, 1 );

$this->assertSame(
array(
'post_type' => 'post',
'order' => 'DESC',
'orderby' => 'date',
'post__not_in' => array(),
'tax_query' => array(
'relation' => 'AND',
array(
array(
'taxonomy' => 'category',
'terms' => array( 56 ),
'include_children' => false,
),
),
array(
'relation' => 'OR',
array(
'taxonomy' => 'post_format',
'field' => 'slug',
'operator' => 'NOT EXISTS',
),
),
),
),
$query
);
}

/**
* @ticket 52991
*/
Expand All @@ -481,6 +609,7 @@ public function test_build_query_vars_from_query_block_no_context() {
'order' => 'DESC',
'orderby' => 'date',
'post__not_in' => array(),
'tax_query' => array(),
)
);
}
Expand Down Expand Up @@ -512,6 +641,7 @@ public function test_build_query_vars_from_query_block_first_page() {
'order' => 'DESC',
'orderby' => 'date',
'post__not_in' => array(),
'tax_query' => array(),
'offset' => 0,
'posts_per_page' => 2,
)
Expand Down Expand Up @@ -544,6 +674,7 @@ public function test_build_query_vars_from_query_block_page_no_offset() {
'order' => 'DESC',
'orderby' => 'date',
'post__not_in' => array(),
'tax_query' => array(),
'offset' => 10,
'posts_per_page' => 5,
)
Expand Down Expand Up @@ -576,6 +707,7 @@ public function test_build_query_vars_from_query_block_page_with_offset() {
'order' => 'DESC',
'orderby' => 'date',
'post__not_in' => array(),
'tax_query' => array(),
'offset' => 12,
'posts_per_page' => 5,
)
Expand Down Expand Up @@ -619,6 +751,7 @@ static function ( $query, $block, $page ) {
'order' => 'DESC',
'orderby' => 'title',
'post__not_in' => array(),
'tax_query' => array(),
)
);
}
Expand Down
Loading

0 comments on commit b1db74a

Please sign in to comment.