From 2b94c843a43f4b8adcf6955c3ffe1281b8729e02 Mon Sep 17 00:00:00 2001 From: wj-yuta-imai Date: Thu, 25 Jun 2026 15:12:30 +0900 Subject: [PATCH] fix: use the post type or taxonomy slug for settings section URLs The Settings page built its deep links to each post type and taxonomy section from the rewrite slug, which is not guaranteed to be unique and can change. A custom type reusing a built-in type's rewrite slug made a section unreachable. Route_Helper::get_route() now uses the unique, stable name instead; the now-unused $rewrite parameter is removed and its three callers are updated. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/helpers/route-helper.php | 13 +-- src/integrations/settings-integration.php | 4 +- .../tasks/set-search-appearance-templates.php | 2 +- tests/Unit/Helpers/Route_Helper_Test.php | 82 +++++++++++++++++++ .../Settings_Integration_Test.php | 4 +- 5 files changed, 94 insertions(+), 11 deletions(-) create mode 100644 tests/Unit/Helpers/Route_Helper_Test.php diff --git a/src/helpers/route-helper.php b/src/helpers/route-helper.php index 68fed259f78..8e683be1cd6 100644 --- a/src/helpers/route-helper.php +++ b/src/helpers/route-helper.php @@ -8,19 +8,20 @@ class Route_Helper { /** - * Gets the route from a name, rewrite and rest_base. + * Gets the route from a name and rest_base. + * + * The post type or taxonomy name is used as the route by default, because it is unique and + * stable. The rewrite slug is deliberately not used: it is not guaranteed to be unique (a + * custom type can reuse a built-in type's rewrite slug) and it can change, which would make + * the settings section unreachable or its URL unstable. See https://github.com/Yoast/wordpress-seo/issues/20864. * * @param string $name The name. - * @param array $rewrite The rewrite data. * @param string $rest_base The rest base. * * @return string The route. */ - public function get_route( $name, $rewrite, $rest_base ) { + public function get_route( $name, $rest_base ) { $route = $name; - if ( isset( $rewrite['slug'] ) ) { - $route = $rewrite['slug']; - } if ( ! empty( $rest_base ) ) { $route = $rest_base; } diff --git a/src/integrations/settings-integration.php b/src/integrations/settings-integration.php index 20f1d5b785b..3c95f0ddc60 100644 --- a/src/integrations/settings-integration.php +++ b/src/integrations/settings-integration.php @@ -994,7 +994,7 @@ protected function transform_post_types( $post_types ) { foreach ( $post_types as $post_type ) { $transformed[ $post_type->name ] = [ 'name' => $post_type->name, - 'route' => $this->route_helper->get_route( $post_type->name, $post_type->rewrite, $post_type->rest_base ), + 'route' => $this->route_helper->get_route( $post_type->name, $post_type->rest_base ), 'label' => $post_type->label, 'singularLabel' => $post_type->labels->singular_name, 'hasArchive' => $this->post_type_helper->has_archive( $post_type ), @@ -1047,7 +1047,7 @@ protected function transform_taxonomies( $taxonomies, $post_type_names ) { foreach ( $taxonomies as $taxonomy ) { $transformed[ $taxonomy->name ] = [ 'name' => $taxonomy->name, - 'route' => $this->route_helper->get_route( $taxonomy->name, $taxonomy->rewrite, $taxonomy->rest_base ), + 'route' => $this->route_helper->get_route( $taxonomy->name, $taxonomy->rest_base ), 'label' => $taxonomy->label, 'showUi' => $taxonomy->show_ui, 'singularLabel' => $taxonomy->labels->singular_name, diff --git a/src/task-list/application/tasks/set-search-appearance-templates.php b/src/task-list/application/tasks/set-search-appearance-templates.php index 74543d9b42b..08a584f1e6a 100644 --- a/src/task-list/application/tasks/set-search-appearance-templates.php +++ b/src/task-list/application/tasks/set-search-appearance-templates.php @@ -100,7 +100,7 @@ public function get_link(): ?string { $post_type = \get_post_type_object( $this->get_post_type() ); $link = \sprintf( 'admin.php?page=wpseo_page_settings#/post-type/%s', - $this->route_helper->get_route( $post_type->name, $post_type->rewrite, $post_type->rest_base ), + $this->route_helper->get_route( $post_type->name, $post_type->rest_base ), ); return \self_admin_url( $link ); diff --git a/tests/Unit/Helpers/Route_Helper_Test.php b/tests/Unit/Helpers/Route_Helper_Test.php new file mode 100644 index 00000000000..e4c7ef7ff88 --- /dev/null +++ b/tests/Unit/Helpers/Route_Helper_Test.php @@ -0,0 +1,82 @@ +instance = new Route_Helper(); + } + + /** + * Tests that the route is built from the name, falling back to the rest base, with leading + * slashes stripped. + * + * @covers ::get_route + * + * @dataProvider provider_get_route + * + * @param string $name The name. + * @param string $rest_base The rest base. + * @param string $expected The expected route. + * + * @return void + */ + public function test_get_route( $name, $rest_base, $expected ) { + $this->assertSame( $expected, $this->instance->get_route( $name, $rest_base ) ); + } + + /** + * Data provider for test_get_route. + * + * @return array> + */ + public static function provider_get_route() { + return [ + 'Uses the name when no rest base is set' => [ + 'name' => 'my-post-type', + 'rest_base' => '', + 'expected' => 'my-post-type', + ], + 'Uses the rest base when it is set' => [ + 'name' => 'my-post-type', + 'rest_base' => 'my-rest-base', + 'expected' => 'my-rest-base', + ], + 'Strips a leading slash from the name' => [ + 'name' => '/my-post-type', + 'rest_base' => '', + 'expected' => 'my-post-type', + ], + 'Strips multiple leading slashes from the rest base' => [ + 'name' => 'my-post-type', + 'rest_base' => '///my-rest-base', + 'expected' => 'my-rest-base', + ], + ]; + } +} diff --git a/tests/Unit/Integrations/Settings_Integration_Test.php b/tests/Unit/Integrations/Settings_Integration_Test.php index 31229a302ac..a76323b024c 100644 --- a/tests/Unit/Integrations/Settings_Integration_Test.php +++ b/tests/Unit/Integrations/Settings_Integration_Test.php @@ -393,7 +393,7 @@ public function test_transform_post_types( $post_types, $new_post_types, $expect $post_type = $post_types['book']; $this->route_helper ->expects( 'get_route' ) - ->with( $post_type->name, $post_type->rewrite, $post_type->rest_base ) + ->with( $post_type->name, $post_type->rest_base ) ->andReturn( $post_type->rewrite['slug'] ); $result = $this->instance_double->transform_post_types( $post_types ); @@ -486,7 +486,7 @@ public function test_transform_taxonomies( $taxonomies, $post_type_names, $new_t $taxonomy = $taxonomies['book_category']; $this->route_helper ->expects( 'get_route' ) - ->with( $taxonomy->name, $taxonomy->rewrite, $taxonomy->rest_base ) + ->with( $taxonomy->name, $taxonomy->rest_base ) ->andReturn( $taxonomy->rewrite['slug'] ); $result = $this->instance_double->transform_taxonomies( $taxonomies, $post_type_names );