Skip to content

REST API: Support nested _fields in revisions controller#11230

Closed
ellatrix wants to merge 1 commit intoWordPress:trunkfrom
ellatrix:fix/revisions-rest-api-nested-fields
Closed

REST API: Support nested _fields in revisions controller#11230
ellatrix wants to merge 1 commit intoWordPress:trunkfrom
ellatrix:fix/revisions-rest-api-nested-fields

Conversation

@ellatrix
Copy link
Member

@ellatrix ellatrix commented Mar 11, 2026

https://core.trac.wordpress.org/ticket/64844

Use rest_is_field_included() instead of in_array() for content, title, excerpt, and guid fields in WP_REST_Revisions_Controller. This lets clients request specific sub-fields (e.g. _fields=content.raw) and skip expensive the_content rendering.

The REST API _fields documentation states that nested fields are supported using dot notation. However, the revisions controller uses in_array() which doesn't match nested keys like content.raw.

Several other controllers already use rest_is_field_included() for raw/rendered sub-fields:

Related Gutenberg PR: WordPress/gutenberg#76347

Use rest_is_field_included() instead of in_array() for content, title,
excerpt, and guid fields in WP_REST_Revisions_Controller. This enables
clients to request individual sub-fields (e.g. content.raw without
content.rendered) via the _fields parameter, avoiding expensive
server-side rendering when only raw data is needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link

github-actions bot commented Mar 11, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props ellatrix, andrewserong, mukesh27.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Copy link
Contributor

@andrewserong andrewserong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems sound to me! As mentioned in the linked Gutenberg issue, the performance benefits of skipping the rendered fields makes this very much worthwhile 👍

Totally optional as I don't think we're testing this specifically in the posts controller, but if you wanted you could possibly add a test for the nested properties case? E.g. something like:

  /**
   * Tests that nested _fields are supported for revision responses.
   *
   * @ticket 64844
   */
  public function test_prepare_item_with_nested_fields() {
      wp_set_current_user( self::$editor_id );

      $endpoint = new WP_REST_Revisions_Controller( 'post' );
      $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 );

      $request->set_param( 'context', 'edit' );
      $request->set_param( '_fields', 'title.raw,content.raw' );

      $revision = get_post( $this->revision_id1 );
      $response = $endpoint->prepare_item_for_response( $revision, $request );

      $data = $response->get_data();

      // Only the requested sub-fields should be present.
      $this->assertArrayHasKey( 'raw', $data['title'] );
      $this->assertArrayNotHasKey( 'rendered', $data['title'] );
      $this->assertArrayHasKey( 'raw', $data['content'] );
      $this->assertArrayNotHasKey( 'rendered', $data['content'] );
  }

Or, update the existing test_prepare_item_limit_fields test to include the sub-properties:

public function test_prepare_item_limit_fields() {
wp_set_current_user( self::$editor_id );
$request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 );
$endpoint = new WP_REST_Revisions_Controller( 'post' );
$request->set_param( 'context', 'edit' );
$request->set_param( '_fields', 'id,slug' );
$revision = get_post( $this->revision_id1 );
$response = $endpoint->prepare_item_for_response( $revision, $request );
$this->assertSame(
array(
'id',
'slug',
),
array_keys( $response->get_data() )
);
}

In any case, this LGTM!

$data['slug'] = $post->post_name;
}

if ( in_array( 'guid', $fields, true ) ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously, if guid was included, the response always contained both guid.rendered and guid.raw.
Now, if the we requests only guid (but not guid.raw or guid.rendered), they’ll get guid: [] (empty array).

Does this break BC?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'll actually wind up including both, due to how rest_is_field_included works internally. Take a look here:

foreach ( $fields as $accepted_field ) {
/*
* Check to see if $field is the parent of any item in $fields.
* A field "parent" should be accepted if "parent.child" is accepted.
*/
if ( str_starts_with( $accepted_field, "$field." ) ) {
return true;
}
/*
* Conversely, if "parent" is accepted, all "parent.child" fields
* should also be accepted.
*/
if ( str_starts_with( $field, "$accepted_field." ) ) {
return true;
}
}

It's pretty neat — if a user just provides the parent guid then the sub-properties will also be matched.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Thanks!

@ellatrix
Copy link
Member Author

Thanks for the review! I'm going to leave it as-is since we're close to Beta 5.

pento pushed a commit that referenced this pull request Mar 12, 2026
Use `rest_is_field_included()` instead of `in_array()` for `content`, `title`, `excerpt`, and `guid` fields in `WP_REST_Revisions_Controller`. This lets clients request specific sub-fields (e.g. `_fields=content.raw`) and skip expensive `the_content` rendering.

The [REST API `_fields` documentation](https://developer.wordpress.org/rest-api/using-the-rest-api/global-parameters/#_fields) states that nested fields are supported using dot notation. However, the revisions controller uses `in_array()` which doesn't match nested keys like `content.raw`.

Developed in: #11230.

Props ellatrix, andrewserong, mukeshpanchal27.
Fixes #64844.

git-svn-id: https://develop.svn.wordpress.org/trunk@61987 602fd350-edb4-49c9-b593-d223f7449a82
@github-actions
Copy link

A commit was made that fixes the Trac ticket referenced in the description of this pull request.

SVN changeset: 61987
GitHub commit: 4ad7d81

This PR will be closed, but please confirm the accuracy of this and reopen if there is more work to be done.

@ellatrix ellatrix closed this Mar 12, 2026
markjaquith pushed a commit to markjaquith/WordPress that referenced this pull request Mar 12, 2026
Use `rest_is_field_included()` instead of `in_array()` for `content`, `title`, `excerpt`, and `guid` fields in `WP_REST_Revisions_Controller`. This lets clients request specific sub-fields (e.g. `_fields=content.raw`) and skip expensive `the_content` rendering.

The [REST API `_fields` documentation](https://developer.wordpress.org/rest-api/using-the-rest-api/global-parameters/#_fields) states that nested fields are supported using dot notation. However, the revisions controller uses `in_array()` which doesn't match nested keys like `content.raw`.

Developed in: WordPress/wordpress-develop#11230.

Props ellatrix, andrewserong, mukeshpanchal27.
Fixes #64844.
Built from https://develop.svn.wordpress.org/trunk@61987


git-svn-id: http://core.svn.wordpress.org/trunk@61269 1a063a9b-81f0-0310-95a4-ce76da25c4cd
@ellatrix ellatrix deleted the fix/revisions-rest-api-nested-fields branch March 12, 2026 13:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants