Skip to content

Comment REST API route: add support for filtering by multiple statuses. - #9870

Open
adamsilverstein wants to merge 27 commits into
WordPress:trunkfrom
adamsilverstein:add/comment-rest-api-types-support
Open

Comment REST API route: add support for filtering by multiple statuses.#9870
adamsilverstein wants to merge 27 commits into
WordPress:trunkfrom
adamsilverstein:add/comment-rest-api-types-support

Conversation

@adamsilverstein

@adamsilverstein adamsilverstein commented Sep 14, 2025

Copy link
Copy Markdown
Member

Add support for filtering the comments collection endpoint by multiple statuses, eg. /wp/v2/comments?status=approve,hold or status[]=approve&status[]=hold. WP_Comment_Query already accepts an array of statuses, so this brings the REST endpoint in line with what the underlying query supports. Single string values continue to work as before.

Originally extracted from the Notes work, see WordPress/gutenberg#71271 (WordPress/gutenberg#71271 (comment))

Changes:

  • Define the status collection param as an array of strings, each sanitized with sanitize_key, following the pattern of the posts controller.
  • Update the status check in get_items_permissions_check to handle array values. Behavior is unchanged: unauthenticated requests can only use the default approve status, anything else requires edit_posts.
  • Add tests covering single, multiple, comma separated and all status queries, sanitization of the param, and the authorization boundary.
  • Regenerate the QUnit REST schema fixture.

Note: I did not add an enum of allowed values here - WP_Comment_Query explicitly supports custom comment statuses and the endpoint currently accepts any string (eg. spam and trash work today for authorized users), so an enum limited to the registered statuses would be a back compat break.

Testing instructions

  1. On a test site, create some comments and mark a few as approved and a few as pending (the Comments screen works for this).
  2. As an authorized user (I used an application password), request multiple statuses and verify comments from both lists are returned:
    curl --user admin:APP_PASSWORD "https://example.test/wp-json/wp/v2/comments?status=approve,hold"
  3. Request a single status, eg. ?status=hold, and verify only pending comments are returned.
  4. Without authentication, verify ?status=approve still returns approved comments and ?status=approve,hold returns a rest_forbidden_param error.
  5. Run the unit tests: npm run test:php -- --filter WP_Test_REST_Comments_Controller

Trac ticket: https://core.trac.wordpress.org/ticket/63982


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@adamsilverstein adamsilverstein changed the title Add/comment rest api types support Comment REST API route: add support for status as array Sep 15, 2025
@adamsilverstein
adamsilverstein marked this pull request as ready for review September 15, 2025 05:18
@github-actions

github-actions Bot commented Sep 15, 2025

Copy link
Copy Markdown

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 adamsilverstein, westonruter, swissspidy, timothyblynjacobs, mukesh27, wildworks.

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

@adamsilverstein adamsilverstein changed the title Comment REST API route: add support for status as array Comment REST API route: add support multiple statuses Sep 15, 2025
Comment thread tests/phpunit/tests/rest-api/rest-comments-controller.php Outdated
@t-hamano

Copy link
Copy Markdown
Contributor

We might not need to extend the REST API itself. See WordPress/gutenberg#71271 (comment)

Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com>
@adamsilverstein

adamsilverstein commented Sep 15, 2025

Copy link
Copy Markdown
Member Author

We might not need to extend the REST API itself. See WordPress/gutenberg#71271 (comment)

Good point; that said its probably worth adding as its a simple change and reflects what the underlying core comments class already supports (so it feels like a bug or oversight that it isn't supported already). While we don't need it yet, I could easily see us or a plugin wanting this.

Comment thread src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php Outdated
@@ -1681,7 +1681,7 @@ public function get_collection_params() {
'default' => 'approve',
'description' => __( 'Limit result set to comments assigned a specific status. Requires authorization.' ),
'sanitize_callback' => 'sanitize_key',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is an issue since it only does anything for strings.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good point. I'll expand this to handle arrays based on what we do in posts. Should be straightforward to recuse existing code for each item.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added handling in 5fba5f6 calling sanitize_key for each status.

@adamsilverstein adamsilverstein Sep 15, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Actually @TimothyBJacobs I'm not sure we need this -

  • sanitization only called when creating or updating a comment - in this case we should still only accept a single status value, right? sanitize_key would throw an error, maybe thats what we want?

Also, while writing tests, I noticed as far as I can see we already strictly enforce status values from a list in WP_Test_REST_Comments_Controller::handle_status_param. Shouldn't the endpoint accept custom status values like WP_Comment_Query?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

handle_status_param is for the writes though, right? This is the arg list for the query parameters so they'd be a bit different.

Are custom comment status really supported by Core? wp_get_comment_status makes it seem like the list is fixed.

We should probably have an enum keyword with all of the supported statuses I think?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks for the feedback @TimothyBJacobs

We don't need this immediately so I am deprioritizing it for the moment.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Picking this back up. I updated the param to a proper array of strings with each value run through sanitize_key, following the pattern of the posts controller, and added tests for the sanitize callback directly.

On the enum question: I looked at it and decided against for now. WP_Comment_Query explicitly accepts custom comment statuses, and the endpoint currently accepts any string here (eg. spam and trash work today for users with edit_posts), so an enum limited to the registered statuses would be a back compat break. What do you think?

Worth noting that while testing I found the status comparison in get_items_permissions_check needed updating too - with the param now an array it never string-matched approve, which broke unauthenticated requests. Fixed by comparing against the parsed list, with tests covering the authorization boundary.

Comment thread src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php Outdated
…roller.php

Co-authored-by: Mukesh Panchal <mukeshpanchal27@users.noreply.github.com>
Comment thread tests/phpunit/tests/rest-api/rest-comments-controller.php Outdated
Co-authored-by: Mukesh Panchal <mukeshpanchal27@users.noreply.github.com>
@adamsilverstein adamsilverstein changed the title Comment REST API route: add support multiple statuses [WIP] Comment REST API route: add support multiple statuses Sep 18, 2025
With the status collection param now an array, the sanitized value never
string-matches 'approve', so unauthenticated requests using the default
status were rejected. Compare against the parsed list instead, define the
schema as an array of strings per review feedback, and regenerate the
QUnit fixture to match.
Copilot AI review requested due to automatic review settings July 23, 2026 20:49
Co-authored-by: Weston Ruter <westonruter@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Copilot AI review requested due to automatic review settings July 23, 2026 20:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment on lines +2056 to +2059
public function sanitize_comment_statuses( $statuses ): array {
$statuses = wp_parse_list( $statuses );
return array_values( array_filter( array_unique( array_map( 'sanitize_key', $statuses ) ) ) );
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

True, but '0' is not a valid status. So this doesn't seem necessary.

@adamsilverstein adamsilverstein Jul 23, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I thought so too, but it turns out '0' is queryable: WP_Comment_Query passes unrecognized statuses straight through to a comment_approved match (its default case), and held comments are stored with comment_approved of '0'. So ?status=0 works today as an alias for hold for authorized users, and sanitize_key preserves it - only the bare array_filter() dropped it. I made the filter explicit in 6dd63d2 and added the '0' cases to the sanitization tests.

@westonruter westonruter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good to add this test: #9870 (comment)

Otherwise, pre-approving.

foreach ( $protected_params as $param ) {
if ( 'status' === $param ) {
if ( 'approve' !== $request[ $param ] ) {
if ( array( 'approve' ) !== $request[ $param ] ) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oh, is there a back-compat concern with the status arg now becoming an array of strings? Could there be any plugins that are looking at this parsed request param which would fail now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good question. The raw query string is unchanged, but once sanitization runs (which happens before the permission callback), $request['status'] is always an array - so a plugin doing a string comparison against the parsed param, eg. 'approve' === $request['status'] inside a rest_comment_query filter callback, would now see array( 'approve' ) and fail. $prepared_args['status'] handed to WP_Comment_Query also becomes an array, though that part is transparent since the query class accepts both forms.

So yes, there is a narrow back compat surface for code inspecting the parsed request param directly. It matches the shape the posts controller status param has had since 4.7 ([39104], https://core.trac.wordpress.org/ticket/38420), so the pattern is well established. I'd rather call this out in the dev note than try to preserve the string shape for single values, which would make the parsed type unpredictable. What do you think?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I can try searching https://veloria.dev/ but may be hard to be sure without a ton of digging. I can also merge this in early 7.2 so there is plenty of time to get feedback.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah, I think it's ok.

The unauthenticated 401 boundary was only covered for the array form of
the status parameter. Since the permission check now relies on the
sanitize callback having parsed the raw value, the comma-separated form
needs its own case so it cannot regress independently. Convert the test
to a data provider covering both forms.
Copilot AI review requested due to automatic review settings July 23, 2026 22:52
array_filter() without a callback drops all falsy strings, including
'0'. WP_Comment_Query treats a literal '0' status as a direct
comment_approved match (how held comments are stored), so ?status=0 is
a working query today and must survive sanitization. Filter only empty
strings instead.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings July 23, 2026 22:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@@ -1777,9 +1777,12 @@ public function get_collection_params() {

$query_params['status'] = array(
'default' => 'approve',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@adamsilverstein Why this one is still string instead of array?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I guess the endpoint will accept a string or array? Still for consistency you are right this should probably be an array.

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.

7 participants