diff --git a/templates/src/Rest/Rest_Controller.php b/templates/src/Rest/Rest_Controller.php index aa90f83..59a659b 100644 --- a/templates/src/Rest/Rest_Controller.php +++ b/templates/src/Rest/Rest_Controller.php @@ -44,23 +44,31 @@ public function init_hooks(): void { * @return void */ public function register_routes() { + // The endpoint definition is nested one level deeper than 'schema': + // register_rest_route() checks for a top-level 'callback' key and, if + // present, wraps the *whole* array as a single numerically-indexed + // endpoint -- 'schema' included, if it were a sibling of 'callback' + // here instead of one level up. Nesting keeps 'schema' a route-level + // option core can actually find. register_rest_route( $this->namespace, '/' . $this->rest_base, array( - 'methods' => \WP_REST_Server::READABLE, - 'callback' => $this->get_items( ... ), - 'permission_callback' => $this->get_items_permissions_check( ... ), - 'args' => array( - 'param' => array( - 'required' => false, - 'sanitize_callback' => 'sanitize_text_field', - 'validate_callback' => function ( $param ) { - return is_string( $param ); - }, + array( + 'methods' => \WP_REST_Server::READABLE, + 'callback' => $this->get_items( ... ), + 'permission_callback' => $this->get_items_permissions_check( ... ), + 'args' => array( + 'param' => array( + 'required' => false, + 'sanitize_callback' => 'sanitize_text_field', + 'validate_callback' => function ( $param ) { + return is_string( $param ); + }, + ), ), ), - 'schema' => $this->get_public_item_schema( ... ), + 'schema' => $this->get_public_item_schema( ... ), ) ); } diff --git a/templates/tests/Unit/Rest_Controller_Test.php b/templates/tests/Unit/Rest_Controller_Test.php index a133d81..b059e62 100644 --- a/templates/tests/Unit/Rest_Controller_Test.php +++ b/templates/tests/Unit/Rest_Controller_Test.php @@ -52,6 +52,29 @@ public function test_register_routes(): void { $this->assertTrue( true ); } + /** + * The schema must be a route-level option, not a key inside the endpoint + * definition -- register_rest_route() only upgrades a flat array to + * multiple endpoints when it sees a top-level 'callback' key, and + * otherwise swallows 'schema' into that same numerically-indexed entry, + * where core never looks for it (C). + */ + public function test_register_routes_exposes_schema_as_a_route_option(): void { + Functions\when( '__' )->returnArg(); + + Functions\expect( 'register_rest_route' ) + ->once() + ->andReturnUsing( + function ( $route_namespace, $route, $args ) { + $this->assertArrayHasKey( 'schema', $args, 'schema must be a route option, not inside the endpoint' ); + $this->assertArrayHasKey( 0, $args, 'the endpoint must be nested so core does not swallow schema' ); + return true; + } + ); + + ( new Rest_Controller() )->register_routes(); + } + /** * The permission callback fails closed — it gates on a capability, it does * not blanket-allow.