Browse Source

Merge pull request #5934 from cakephp/3.0-custom-resources

Implement a map option for resources().
Mark Story 11 years ago
parent
commit
4739d8306b
3 changed files with 93 additions and 6 deletions
  1. 31 6
      src/Routing/RouteBuilder.php
  2. 5 0
      src/Routing/Router.php
  3. 57 0
      tests/TestCase/Routing/RouteBuilderTest.php

+ 31 - 6
src/Routing/RouteBuilder.php

@@ -216,13 +216,27 @@ class RouteBuilder
      * ```
      *
      * 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.
+     * - 'inflect' - Choose the inflection method used on the resource name. Defaults to 'underscore'.
      * - '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.
@@ -238,22 +252,33 @@ class RouteBuilder
         }
         $options += [
             'connectOptions' => [],
+            'inflect' => 'underscore',
             '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::{$options['inflect']}($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;
             }
 

+ 5 - 0
src/Routing/Router.php

@@ -269,9 +269,14 @@ class Router
      *    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 = [])

+ 57 - 0
tests/TestCase/Routing/RouteBuilderTest.php

@@ -345,6 +345,63 @@ class RouteBuilderTest extends TestCase
     }
 
     /**
+     * Test connecting resources with the inflection option
+     *
+     * @return void
+     */
+    public function testResourcesInflection()
+    {
+        $routes = new RouteBuilder($this->collection, '/api', ['prefix' => 'api']);
+        $routes->resources('BlogPosts', ['_ext' => 'json', 'inflect' => 'dasherize']);
+
+        $all = $this->collection->routes();
+        $this->assertCount(5, $all);
+
+        $this->assertEquals('/api/blog-posts', $all[0]->template);
+        $this->assertEquals(
+            ['controller', 'action', '_method', 'prefix', 'plugin'],
+            array_keys($all[0]->defaults)
+        );
+        $this->assertEquals('BlogPosts', $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.
      *
      * @return void