Skip to content

Commit

Permalink
Implement a map option for resources().
Browse files Browse the repository at this point in the history
Having a map option allows users to map additional resource methods into
their routes. This is handy when apps have custom actions they want
mapped onto resources.

Refs #5929
  • Loading branch information
markstory committed Feb 20, 2015
1 parent 6b2b9fa commit fbf1031
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 6 deletions.
35 changes: 29 additions & 6 deletions src/Routing/RouteBuilder.php
Expand Up @@ -216,13 +216,26 @@ public function params()
* ```
*
* The above would generate both resource routes for `/articles`, and `/articles/:article_id/comments`.
* You can use the `map` option to connect additional resource methods:
*
* ```
* $routes->resources('Articles', [
* 'map' => ['deleteAll' => ['action' => 'deleteAll', 'method' => 'DELETE']]
* ]);
* ```
*
* In addition to the default routes, this would also connect a route for `/articles/delete_all`.
* By default the path segment will match the key name. You can use the 'path' key inside the resource
* definition to customize the path name.
*
* ### Options:
*
* - 'id' - The regular expression fragment to use when matching IDs. By default, matches
* integer values and UUIDs.
* - 'only' - Only connect the specific list of actions.
* - '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.
*
* @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 @@ -239,21 +252,31 @@ public function resources($name, $options = [], $callback = null)
$options += [
'connectOptions' => [],
'id' => static::ID . '|' . static::UUID,
'only' => ['index', 'update', 'create', 'view', 'delete'],
'only' => [],
'actions' => [],
'map' => [],
];
$options['only'] = (array)$options['only'];
$connectOptions = $options['connectOptions'];

$urlName = Inflector::underscore($name);
foreach ($options['map'] as $k => $mapped) {
$options['map'][$k] += ['method' => 'GET', 'path' => $k, 'action' => ''];
}

$ext = null;
if (!empty($options['_ext'])) {
$ext = $options['_ext'];
}

foreach (static::$_resourceMap as $method => $params) {
if (!in_array($method, $options['only'], true)) {
$connectOptions = $options['connectOptions'];
$urlName = Inflector::underscore($name);
$resourceMap = array_merge(static::$_resourceMap, $options['map']);

$only = (array)$options['only'];
if (empty($only)) {
$only = array_keys($resourceMap);
}

foreach ($resourceMap as $method => $params) {
if (!in_array($method, $only, true)) {
continue;
}

Expand Down
5 changes: 5 additions & 0 deletions src/Routing/Router.php
Expand Up @@ -269,9 +269,14 @@ public static function redirect($route, $url, $options = [])
* integer values and UUIDs.
* - 'prefix' - Routing prefix to use for the generated routes. Defaults to ''.
* Using this option will create prefixed routes, similar to using Routing.prefixes.
* - 'only' - Only connect the specific list of actions.
* - '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.
*
* @param string|array $controller A controller name or array of controller names (i.e. "Posts" or "ListItems")
* @param array $options Options to use when generating REST routes
* @see \Cake\Routing\RouteBuilder::resources()
* @return void
*/
public static function mapResources($controller, $options = [])
Expand Down
36 changes: 36 additions & 0 deletions tests/TestCase/Routing/RouteBuilderTest.php
Expand Up @@ -344,6 +344,42 @@ public function testResources()
$this->assertEquals('Articles', $all[0]->defaults['controller']);
}

/**
* Test connecting resources with additional mappings
*
* @return void
*/
public function testResourcesMappings()
{
$routes = new RouteBuilder($this->collection, '/api', ['prefix' => 'api']);
$routes->resources('Articles', [
'_ext' => 'json',
'map' => [
'delete_all' => ['action' => 'deleteAll', 'method' => 'DELETE'],
'update_many' => ['action' => 'updateAll', 'method' => 'DELETE', 'path' => '/updateAll'],
]
]);

$all = $this->collection->routes();
$this->assertCount(7, $all);

$this->assertEquals('/api/articles/delete_all', $all[5]->template, 'Path defaults to key name.');
$this->assertEquals(
['controller', 'action', '_method', 'prefix', 'plugin'],
array_keys($all[5]->defaults)
);
$this->assertEquals('Articles', $all[5]->defaults['controller']);
$this->assertEquals('deleteAll', $all[5]->defaults['action']);

$this->assertEquals('/api/articles/updateAll', $all[6]->template, 'Explicit path option');
$this->assertEquals(
['controller', 'action', '_method', 'prefix', 'plugin'],
array_keys($all[6]->defaults)
);
$this->assertEquals('Articles', $all[6]->defaults['controller']);
$this->assertEquals('updateAll', $all[6]->defaults['action']);
}

/**
* Test connecting resources.
*
Expand Down

0 comments on commit fbf1031

Please sign in to comment.