Make WordPress Core

Changeset 59115


Ignore:
Timestamp:
09/30/2024 01:17:40 AM (2 years ago)
Author:
peterwilsoncc
Message:

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.

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/blocks.php

    r59101 r59115  
    23362336 * @since 5.8.0
    23372337 * @since 6.1.0 Added `query_loop_block_query_vars` filter and `parents` support in query.
     2338 * @since 6.7.0 Added support for the `format` property in query.
    23382339 *
    23392340 * @param WP_Block $block Block instance.
    … …  
    23482349                'orderby'      => 'date',
    23492350                'post__not_in' => array(),
     2351                'tax_query'    => array(),
    23502352        );
    23512353
    … …  
    23972399                // Migrate `categoryIds` and `tagIds` to `tax_query` for backwards compatibility.
    23982400                if ( ! empty( $block->context['query']['categoryIds'] ) || ! empty( $block->context['query']['tagIds'] ) ) {
    2399                         $tax_query = array();
     2401                        $tax_query_back_compat = array();
    24002402                        if ( ! empty( $block->context['query']['categoryIds'] ) ) {
    2401                                 $tax_query[] = array(
     2403                                $tax_query_back_compat[] = array(
    24022404                                        'taxonomy'         => 'category',
    24032405                                        'terms'            => array_filter( array_map( 'intval', $block->context['query']['categoryIds'] ) ),
    … …  
    24062408                        }
    24072409                        if ( ! empty( $block->context['query']['tagIds'] ) ) {
    2408                                 $tax_query[] = array(
     2410                                $tax_query_back_compat[] = array(
    24092411                                        'taxonomy'         => 'post_tag',
    24102412                                        'terms'            => array_filter( array_map( 'intval', $block->context['query']['tagIds'] ) ),
    … …  
    24122414                                );
    24132415                        }
    2414                         $query['tax_query'] = $tax_query;
     2416                        $query['tax_query'] = array_merge( $query['tax_query'], $tax_query_back_compat );
    24152417                }
    24162418                if ( ! empty( $block->context['query']['taxQuery'] ) ) {
    2417                         $query['tax_query'] = array();
     2419                        $tax_query = array();
    24182420                        foreach ( $block->context['query']['taxQuery'] as $taxonomy => $terms ) {
    24192421                                if ( is_taxonomy_viewable( $taxonomy ) && ! empty( $terms ) ) {
    2420                                         $query['tax_query'][] = array(
     2422                                        $tax_query[] = array(
    24212423                                                'taxonomy'         => $taxonomy,
    24222424                                                'terms'            => array_filter( array_map( 'intval', $terms ) ),
    … …  
    24252427                                }
    24262428                        }
    2427                 }
     2429                        $query['tax_query'] = array_merge( $query['tax_query'], $tax_query );
     2430                }
     2431                if ( ! empty( $block->context['query']['format'] ) && is_array( $block->context['query']['format'] ) ) {
     2432                        $formats = $block->context['query']['format'];
     2433                        /*
     2434                         * Validate that the format is either `standard` or a supported post format.
     2435                         * - First, add `standard` to the array of valid formats.
     2436                         * - Then, remove any invalid formats.
     2437                         */
     2438                        $valid_formats = array_merge( array( 'standard' ), get_post_format_slugs() );
     2439                        $formats       = array_intersect( $formats, $valid_formats );
     2440
     2441                        /*
     2442                         * The relation needs to be set to `OR` since the request can contain
     2443                         * two separate conditions. The user may be querying for items that have
     2444                         * either the `standard` format or a specific format.
     2445                         */
     2446                        $formats_query = array( 'relation' => 'OR' );
     2447
     2448                        /*
     2449                         * The default post format, `standard`, is not stored in the database.
     2450                         * If `standard` is part of the request, the query needs to exclude all post items that
     2451                         * have a format assigned.
     2452                         */
     2453                        if ( in_array( 'standard', $formats, true ) ) {
     2454                                $formats_query[] = array(
     2455                                        'taxonomy' => 'post_format',
     2456                                        'field'    => 'slug',
     2457                                        'operator' => 'NOT EXISTS',
     2458                                );
     2459                                // Remove the `standard` format, since it cannot be queried.
     2460                                unset( $formats[ array_search( 'standard', $formats, true ) ] );
     2461                        }
     2462                        // Add any remaining formats to the formats query.
     2463                        if ( ! empty( $formats ) ) {
     2464                                // Add the `post-format-` prefix.
     2465                                $terms = array_map(
     2466                                        static function ( $format ) {
     2467                                                return "post-format-$format";
     2468                                        },
     2469                                        $formats
     2470                                );
     2471                                $formats_query[] = array(
     2472                                        'taxonomy' => 'post_format',
     2473                                        'field'    => 'slug',
     2474                                        'terms'    => $terms,
     2475                                        'operator' => 'IN',
     2476                                );
     2477                        }
     2478
     2479                        /*
     2480                         * Add `$formats_query` to `$query`, as long as it contains more than one key:
     2481                         * If `$formats_query` only contains the initial `relation` key, there are no valid formats to query,
     2482                         * and the query should not be modified.
     2483                         */
     2484                        if ( count( $formats_query ) > 1 ) {
     2485                                // Enable filtering by both post formats and other taxonomies by combining them with `AND`.
     2486                                if ( empty( $query['tax_query'] ) ) {
     2487                                        $query['tax_query'] = $formats_query;
     2488                                } else {
     2489                                        $query['tax_query'] = array(
     2490                                                'relation' => 'AND',
     2491                                                $query['tax_query'],
     2492                                                $formats_query,
     2493                                        );
     2494                                }
     2495                        }
     2496                }
     2497
    24282498                if (
    24292499                        isset( $block->context['query']['order'] ) &&
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php

    r59064 r59115  
    346346
    347347                $args = $this->prepare_tax_query( $args, $request );
     348
     349                if ( ! empty( $request['format'] ) ) {
     350                        $formats = $request['format'];
     351                        /*
     352                         * The relation needs to be set to `OR` since the request can contain
     353                         * two separate conditions. The user may be querying for items that have
     354                         * either the `standard` format or a specific format.
     355                         */
     356                        $formats_query = array( 'relation' => 'OR' );
     357
     358                        /*
     359                         * The default post format, `standard`, is not stored in the database.
     360                         * If `standard` is part of the request, the query needs to exclude all post items that
     361                         * have a format assigned.
     362                         */
     363                        if ( in_array( 'standard', $formats, true ) ) {
     364                                $formats_query[] = array(
     365                                        'taxonomy' => 'post_format',
     366                                        'field'    => 'slug',
     367                                        'operator' => 'NOT EXISTS',
     368                                );
     369                                // Remove the `standard` format, since it cannot be queried.
     370                                unset( $formats[ array_search( 'standard', $formats, true ) ] );
     371                        }
     372
     373                        // Add any remaining formats to the formats query.
     374                        if ( ! empty( $formats ) ) {
     375                                // Add the `post-format-` prefix.
     376                                $terms = array_map(
     377                                        static function ( $format ) {
     378                                                return "post-format-$format";
     379                                        },
     380                                        $formats
     381                                );
     382
     383                                $formats_query[] = array(
     384                                        'taxonomy' => 'post_format',
     385                                        'field'    => 'slug',
     386                                        'terms'    => $terms,
     387                                        'operator' => 'IN',
     388                                );
     389                        }
     390
     391                        // Enable filtering by both post formats and other taxonomies by combining them with `AND`.
     392                        if ( isset( $args['tax_query'] ) ) {
     393                                $args['tax_query'][] = array(
     394                                        'relation' => 'AND',
     395                                        $formats_query,
     396                                );
     397                        } else {
     398                                $args['tax_query'] = $formats_query;
     399                        }
     400                }
    348401
    349402                // Force the post_type argument, since it's not a user input variable.
    … …  
    29933046                }
    29943047
     3048                if ( post_type_supports( $this->post_type, 'post-formats' ) ) {
     3049                        $query_params['format'] = array(
     3050                                'description' => __( 'Limit result set to items assigned one or more given formats.' ),
     3051                                'type'        => 'array',
     3052                                'uniqueItems' => true,
     3053                                'items'       => array(
     3054                                        'enum' => array_values( get_post_format_slugs() ),
     3055                                        'type' => 'string',
     3056                                ),
     3057                        );
     3058                }
     3059
    29953060                /**
    29963061                 * Filters collection parameters for the posts controller.
  • trunk/tests/phpunit/tests/blocks/wpBlock.php

    r58160 r59115  
    465465
    466466        /**
     467         * @ticket 62014
     468         */
     469        public function test_build_query_vars_from_query_block_standard_post_formats() {
     470                $this->registry->register(
     471                        'core/example',
     472                        array( 'uses_context' => array( 'query' ) )
     473                );
     474
     475                $parsed_blocks = parse_blocks( '<!-- wp:example {"ok":true} -->a<!-- wp:example /-->b<!-- /wp:example -->' );
     476                $parsed_block  = $parsed_blocks[0];
     477                $context       = array(
     478                        'query' => array(
     479                                'postType' => 'post',
     480                                'format'   => array( 'standard' ),
     481                        ),
     482                );
     483                $block         = new WP_Block( $parsed_block, $context, $this->registry );
     484                $query         = build_query_vars_from_query_block( $block, 1 );
     485
     486                $this->assertSame(
     487                        array(
     488                                'post_type'    => 'post',
     489                                'order'        => 'DESC',
     490                                'orderby'      => 'date',
     491                                'post__not_in' => array(),
     492                                'tax_query'    => array(
     493                                        'relation' => 'OR',
     494                                        array(
     495                                                'taxonomy' => 'post_format',
     496                                                'field'    => 'slug',
     497                                                'operator' => 'NOT EXISTS',
     498                                        ),
     499                                ),
     500                        ),
     501                        $query
     502                );
     503        }
     504
     505        /**
     506         * @ticket 62014
     507         */
     508        public function test_build_query_vars_from_query_block_post_format() {
     509                $this->registry->register(
     510                        'core/example',
     511                        array( 'uses_context' => array( 'query' ) )
     512                );
     513
     514                $parsed_blocks = parse_blocks( '<!-- wp:example {"ok":true} -->a<!-- wp:example /-->b<!-- /wp:example -->' );
     515                $parsed_block  = $parsed_blocks[0];
     516                $context       = array(
     517                        'query' => array(
     518                                'postType' => 'post',
     519                                'format'   => array( 'aside' ),
     520                        ),
     521                );
     522                $block         = new WP_Block( $parsed_block, $context, $this->registry );
     523                $query         = build_query_vars_from_query_block( $block, 1 );
     524
     525                $this->assertSame(
     526                        array(
     527                                'post_type'    => 'post',
     528                                'order'        => 'DESC',
     529                                'orderby'      => 'date',
     530                                'post__not_in' => array(),
     531                                'tax_query'    => array(
     532                                        'relation' => 'OR',
     533                                        array(
     534                                                'taxonomy' => 'post_format',
     535                                                'field'    => 'slug',
     536                                                'terms'    => array( 'post-format-aside' ),
     537                                                'operator' => 'IN',
     538                                        ),
     539                                ),
     540                        ),
     541                        $query
     542                );
     543        }
     544        /**
     545         * @ticket 62014
     546         */
     547        public function test_build_query_vars_from_query_block_post_formats_with_category() {
     548                $this->registry->register(
     549                        'core/example',
     550                        array( 'uses_context' => array( 'query' ) )
     551                );
     552
     553                $parsed_blocks = parse_blocks( '<!-- wp:example {"ok":true} -->a<!-- wp:example /-->b<!-- /wp:example -->' );
     554                $parsed_block  = $parsed_blocks[0];
     555                $context       = array(
     556                        'query' => array(
     557                                'postType'    => 'post',
     558                                'format'      => array( 'standard' ),
     559                                'categoryIds' => array( 56 ),
     560                        ),
     561                );
     562                $block         = new WP_Block( $parsed_block, $context, $this->registry );
     563                $query         = build_query_vars_from_query_block( $block, 1 );
     564
     565                $this->assertSame(
     566                        array(
     567                                'post_type'    => 'post',
     568                                'order'        => 'DESC',
     569                                'orderby'      => 'date',
     570                                'post__not_in' => array(),
     571                                'tax_query'    => array(
     572                                        'relation' => 'AND',
     573                                        array(
     574                                                array(
     575                                                        'taxonomy'         => 'category',
     576                                                        'terms'            => array( 56 ),
     577                                                        'include_children' => false,
     578                                                ),
     579                                        ),
     580                                        array(
     581                                                'relation' => 'OR',
     582                                                array(
     583                                                        'taxonomy' => 'post_format',
     584                                                        'field'    => 'slug',
     585                                                        'operator' => 'NOT EXISTS',
     586                                                ),
     587                                        ),
     588                                ),
     589                        ),
     590                        $query
     591                );
     592        }
     593
     594        /**
    467595         * @ticket 52991
    468596         */
    … …  
    482610                                'orderby'      => 'date',
    483611                                'post__not_in' => array(),
     612                                'tax_query'    => array(),
    484613                        )
    485614                );
    … …  
    513642                                'orderby'        => 'date',
    514643                                'post__not_in'   => array(),
     644                                'tax_query'      => array(),
    515645                                'offset'         => 0,
    516646                                'posts_per_page' => 2,
    … …  
    545675                                'orderby'        => 'date',
    546676                                'post__not_in'   => array(),
     677                                'tax_query'      => array(),
    547678                                'offset'         => 10,
    548679                                'posts_per_page' => 5,
    … …  
    577708                                'orderby'        => 'date',
    578709                                'post__not_in'   => array(),
     710                                'tax_query'      => array(),
    579711                                'offset'         => 12,
    580712                                'posts_per_page' => 5,
    … …  
    620752                                'orderby'      => 'title',
    621753                                'post__not_in' => array(),
     754                                'tax_query'    => array(),
    622755                        )
    623756                );
  • trunk/tests/phpunit/tests/rest-api/rest-posts-controller.php

    r59036 r59115  
    197197                                'context',
    198198                                'exclude',
     199                                'format',
    199200                                'include',
    200201                                'modified_after',
    … …  
    54985499
    54995500        /**
     5501         * Test the REST API support for the standard post format.
     5502         *
     5503         * @ticket 62014
     5504         *
     5505         * @covers WP_REST_Posts_Controller::get_items
     5506         */
     5507        public function test_standard_post_format_support() {
     5508                $initial_theme_support = get_theme_support( 'post-formats' );
     5509                add_theme_support( 'post-formats', array( 'aside', 'gallery', 'link', 'image', 'quote', 'status', 'video', 'audio', 'chat' ) );
     5510
     5511                $post_id = self::factory()->post->create(
     5512                        array(
     5513                                'post_type'   => 'post',
     5514                                'post_status' => 'publish',
     5515                        )
     5516                );
     5517                set_post_format( $post_id, 'aside' );
     5518
     5519                $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     5520                $request->set_param( 'format', array( 'standard' ) );
     5521                $request->set_param( 'per_page', REST_TESTS_IMPOSSIBLY_HIGH_NUMBER );
     5522
     5523                $response = rest_get_server()->dispatch( $request );
     5524
     5525                /*
     5526                 * Restore the initial post formats support.
     5527                 *
     5528                 * This needs to be done prior to the assertions to avoid unexpected
     5529                 * results for other tests should an assertion fail.
     5530                 */
     5531                if ( $initial_theme_support ) {
     5532                        add_theme_support( 'post-formats', $initial_theme_support[0] );
     5533                } else {
     5534                        remove_theme_support( 'post-formats' );
     5535                }
     5536
     5537                $this->assertCount( 3, $response->get_data(), 'The response should only include standard post formats' );
     5538        }
     5539
     5540        /**
     5541         * Test the REST API support for post formats.
     5542         *
     5543         * @ticket 62014
     5544         *
     5545         * @covers WP_REST_Posts_Controller::get_items
     5546         */
     5547        public function test_post_format_support() {
     5548                $initial_theme_support = get_theme_support( 'post-formats' );
     5549                add_theme_support( 'post-formats', array( 'aside', 'gallery', 'link', 'image', 'quote', 'status', 'video', 'audio', 'chat' ) );
     5550
     5551                $post_id = self::factory()->post->create(
     5552                        array(
     5553                                'post_type'   => 'post',
     5554                                'post_status' => 'publish',
     5555                        )
     5556                );
     5557                set_post_format( $post_id, 'aside' );
     5558
     5559                $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     5560                $request->set_param( 'format', array( 'aside' ) );
     5561
     5562                $response_aside = rest_get_server()->dispatch( $request );
     5563
     5564                $request->set_param( 'format', array( 'invalid_format' ) );
     5565                $response_invalid = rest_get_server()->dispatch( $request );
     5566
     5567                /*
     5568                 * Restore the initial post formats support.
     5569                 *
     5570                 * This needs to be done prior to the assertions to avoid unexpected
     5571                 * results for other tests should an assertion fail.
     5572                 */
     5573                if ( $initial_theme_support ) {
     5574                        add_theme_support( 'post-formats', $initial_theme_support[0] );
     5575                } else {
     5576                        remove_theme_support( 'post-formats' );
     5577                }
     5578
     5579                $this->assertCount( 1, $response_aside->get_data(), 'Only one post is expected to be returned.' );
     5580                $this->assertErrorResponse( 'rest_invalid_param', $response_invalid, 400, 'An invalid post format should return an error' );
     5581        }
     5582
     5583        /**
     5584         * Test the REST API support for multiple post formats.
     5585         *
     5586         * @ticket 62014
     5587         *
     5588         * @covers WP_REST_Posts_Controller::get_items
     5589         */
     5590        public function test_multiple_post_format_support() {
     5591                $initial_theme_support = get_theme_support( 'post-formats' );
     5592                add_theme_support( 'post-formats', array( 'aside', 'gallery', 'link', 'image', 'quote', 'status', 'video', 'audio', 'chat' ) );
     5593
     5594                $post_id = self::factory()->post->create(
     5595                        array(
     5596                                'post_type'   => 'post',
     5597                                'post_status' => 'publish',
     5598                        )
     5599                );
     5600                set_post_format( $post_id, 'aside' );
     5601
     5602                $post_id_2 = self::factory()->post->create(
     5603                        array(
     5604                                'post_type'   => 'post',
     5605                                'post_status' => 'publish',
     5606                        )
     5607                );
     5608                set_post_format( $post_id_2, 'gallery' );
     5609
     5610                $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     5611                $request->set_param( 'format', array( 'aside', 'gallery' ) );
     5612
     5613                $response = rest_get_server()->dispatch( $request );
     5614
     5615                /*
     5616                 * Restore the initial post formats support.
     5617                 *
     5618                 * This needs to be done prior to the assertions to avoid unexpected
     5619                 * results for other tests should an assertion fail.
     5620                 */
     5621                if ( $initial_theme_support ) {
     5622                        add_theme_support( 'post-formats', $initial_theme_support[0] );
     5623                } else {
     5624                        remove_theme_support( 'post-formats' );
     5625                }
     5626
     5627                $this->assertCount( 2, $response->get_data(), 'Two posts are expected to be returned' );
     5628        }
     5629
     5630        /**
    55005631         * Internal function used to disable an insert query which
    55015632         * will trigger a wpdb error for testing purposes.
  • trunk/tests/qunit/fixtures/wp-api-generated.js

    r59034 r59115  
    626626                            "description": "Limit result set to items that are sticky.",
    627627                            "type": "boolean",
     628                            "required": false
     629                        },
     630                        "format": {
     631                            "description": "Limit result set to items assigned one or more given formats.",
     632                            "type": "array",
     633                            "uniqueItems": true,
     634                            "items": {
     635                                "enum": [
     636                                    "standard",
     637                                    "aside",
     638                                    "chat",
     639                                    "gallery",
     640                                    "link",
     641                                    "image",
     642                                    "quote",
     643                                    "status",
     644                                    "video",
     645                                    "audio"
     646                                ],
     647                                "type": "string"
     648                            },
    628649                            "required": false
    629650                        }
Note: See TracChangeset for help on using the changeset viewer.