Skip to content

Commit

Permalink
Add prefix support to resources.
Browse files Browse the repository at this point in the history
This allows resources and more importantly nested resources to have
routing prefixes applied to them. This allows nested resource
controllers to be mapped onto namespaces/routing prefixes. I thought
this was a more robust/opinionated solution that allowing arbitrary
class names.

I've had to relax the duplicate key checking between scopes and their
routes to allow differences in prefixes to be created.

Refs #8063
  • Loading branch information
markstory committed Mar 19, 2016
1 parent f41fd23 commit 5b7a788
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 3 deletions.
16 changes: 15 additions & 1 deletion src/Routing/RouteBuilder.php
Expand Up @@ -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.
Expand All @@ -307,6 +309,7 @@ public function resources($name, $options = [], $callback = null)
'only' => [],
'actions' => [],
'map' => [],
'prefix' => null,
];

foreach ($options['map'] as $k => $mapped) {
Expand All @@ -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;
Expand All @@ -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'],
Expand Down Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions src/Routing/Router.php
Expand Up @@ -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);
Expand Down
37 changes: 35 additions & 2 deletions tests/TestCase/Routing/RouteBuilderTest.php
Expand Up @@ -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']);
}

/**
Expand Down Expand Up @@ -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
*
Expand Down

0 comments on commit 5b7a788

Please sign in to comment.