diff --git a/src/Routing/RouteBuilder.php b/src/Routing/RouteBuilder.php index 628a4ed61c7..e53d6024a50 100644 --- a/src/Routing/RouteBuilder.php +++ b/src/Routing/RouteBuilder.php @@ -287,6 +287,8 @@ public function namePrefix($value = null) * - 'actions' - Override the method names used for connecting actions. * - 'map' - Additional resource routes that should be connected. If you define 'only' and 'map', * make sure that your mapped methods are also in the 'only' list. + * - 'prefix' - Define a routing prefix for the resource controller. If the current scope + * defines a prefix, this prefix will be appended to it. * * @param string $name A controller name to connect resource routes for. * @param array|callable $options Options to use when generating REST routes, or a callback. @@ -307,6 +309,7 @@ public function resources($name, $options = [], $callback = null) 'only' => [], 'actions' => [], 'map' => [], + 'prefix' => null, ]; foreach ($options['map'] as $k => $mapped) { @@ -327,6 +330,14 @@ public function resources($name, $options = [], $callback = null) $only = array_keys($resourceMap); } + $prefix = ''; + if ($options['prefix']) { + $prefix = $options['prefix']; + } + if (isset($this->_params['prefix']) && $prefix) { + $prefix = $this->_params['prefix'] . '/' . $prefix; + } + foreach ($resourceMap as $method => $params) { if (!in_array($method, $only, true)) { continue; @@ -343,6 +354,9 @@ public function resources($name, $options = [], $callback = null) 'action' => $action, '_method' => $params['method'], ]; + if ($prefix) { + $params['prefix'] = $prefix; + } $routeOptions = $connectOptions + [ 'id' => $options['id'], 'pass' => ['id'], @@ -482,7 +496,7 @@ protected function _makeRoute($route, $defaults, $options) $route = $route === '/' ? $route : rtrim($route, '/'); foreach ($this->_params as $param => $val) { - if (isset($defaults[$param]) && $defaults[$param] !== $val) { + if (isset($defaults[$param]) && $param !== 'prefix' && $defaults[$param] !== $val) { $msg = 'You cannot define routes that conflict with the scope. ' . 'Scope had %s = %s, while route had %s = %s'; throw new BadMethodCallException(sprintf( diff --git a/src/Routing/Router.php b/src/Routing/Router.php index 76ff241b378..ed1588ce13e 100644 --- a/src/Routing/Router.php +++ b/src/Routing/Router.php @@ -289,6 +289,7 @@ public static function mapResources($controller, $options = []) $prefix = $pluginUrl = false; if (!empty($options['prefix'])) { $prefix = $options['prefix']; + unset($options['prefix']); } if ($plugin) { $pluginUrl = Inflector::underscore($plugin); diff --git a/tests/TestCase/Routing/RouteBuilderTest.php b/tests/TestCase/Routing/RouteBuilderTest.php index da714cd21d4..ca5af6f1c12 100644 --- a/tests/TestCase/Routing/RouteBuilderTest.php +++ b/tests/TestCase/Routing/RouteBuilderTest.php @@ -252,8 +252,8 @@ public function testConnectErrorInvalidRouteClass() */ public function testConnectConflictingParameters() { - $routes = new RouteBuilder($this->collection, '/admin', ['prefix' => 'admin']); - $routes->connect('/', ['prefix' => 'manager', 'controller' => 'Dashboard', 'action' => 'view']); + $routes = new RouteBuilder($this->collection, '/admin', ['plugin' => 'TestPlugin']); + $routes->connect('/', ['plugin' => 'TestPlugin2', 'controller' => 'Dashboard', 'action' => 'view']); } /** @@ -366,6 +366,39 @@ public function testResources() $this->assertEquals('Articles', $all[0]->defaults['controller']); } + /** + * Test connecting resources with a prefix + * + * @return void + */ + public function testResourcesPrefix() + { + $routes = new RouteBuilder($this->collection, '/api'); + $routes->resources('Articles', ['prefix' => 'rest']); + $all = $this->collection->routes(); + $this->assertEquals('rest', $all[0]->defaults['prefix']); + } + + /** + * Test that resource prefixes work within a prefixed scope. + * + * @return void + */ + public function testResourcesNestedPrefix() + { + $routes = new RouteBuilder($this->collection, '/api', ['prefix' => 'api']); + $routes->resources('Articles', ['prefix' => 'rest']); + + $all = $this->collection->routes(); + $this->assertCount(5, $all); + + $this->assertEquals('/api/articles', $all[0]->template); + foreach ($all as $route) { + $this->assertEquals('api/rest', $route->defaults['prefix']); + $this->assertEquals('Articles', $route->defaults['controller']); + } + } + /** * Test connecting resources with the inflection option *