Browse Source

Merge pull request #4453 from cakephp/3.0-route-class

3.0 default route class
Mark Story 11 years ago
parent
commit
081f271b19

+ 52 - 14
src/Routing/RouteBuilder.php

@@ -16,8 +16,8 @@ namespace Cake\Routing;
 
 use BadMethodCallException;
 use Cake\Core\App;
-use Cake\Routing\Route\Route;
 use Cake\Routing\Router;
+use Cake\Routing\Route\Route;
 use Cake\Utility\Inflector;
 use InvalidArgumentException;
 
@@ -57,11 +57,18 @@ class RouteBuilder {
 	];
 
 /**
+ * Default route class to use if none is provided in connect() options.
+ *
+ * @var string
+ */
+	protected $_routeClass = 'Cake\Routing\Route\Route';
+
+/**
  * The extensions that should be set into the routes connected.
  *
  * @var array
  */
-	protected $_extensions;
+	protected $_extensions = [];
 
 /**
  * The path prefix scope that this collection uses.
@@ -90,13 +97,34 @@ class RouteBuilder {
  * @param \Cake\Routing\RouteCollection $collection The route collection to append routes into.
  * @param string $path The path prefix the scope is for.
  * @param array $params The scope's routing parameters.
- * @param array $extensions The extensions to connect when adding routes.
+ * @param array $options Options list. Valid keys are:
+ *
+ *   - `routeClass` - The default route class to use when adding routes.
+ *   - `extensions` - The extensions to connect when adding routes.
  */
-	public function __construct($collection, $path, array $params = [], array $extensions = []) {
+	public function __construct($collection, $path, array $params = [], array $options = []) {
 		$this->_collection = $collection;
 		$this->_path = $path;
 		$this->_params = $params;
-		$this->_extensions = $extensions;
+		if (isset($options['routeClass'])) {
+			$this->_routeClass = $options['routeClass'];
+		}
+		if (isset($options['extensions'])) {
+			$this->_extensions = $options['extensions'];
+		}
+	}
+
+/**
+ * Get or set default route class.
+ *
+ * @param string|null $routeClass Class name.
+ * @return string|void
+ */
+	public function routeClass($routeClass = null) {
+		if ($routeClass == null) {
+			return $this->_routeClass;
+		}
+		$this->_routeClass = $routeClass;
 	}
 
 /**
@@ -323,6 +351,10 @@ class RouteBuilder {
 			$options['_ext'] = $this->_extensions;
 		}
 
+		if (empty($options['routeClass'])) {
+			$options['routeClass'] = $this->_routeClass;
+		}
+
 		$route = $this->_makeRoute($route, $defaults, $options);
 		$this->_collection->add($route, $options);
 	}
@@ -339,14 +371,13 @@ class RouteBuilder {
  */
 	protected function _makeRoute($route, $defaults, $options) {
 		if (is_string($route)) {
-			$routeClass = 'Cake\Routing\Route\Route';
-			if (isset($options['routeClass'])) {
-				$routeClass = App::className($options['routeClass'], 'Routing/Route');
-			}
+			$routeClass = App::className($options['routeClass'], 'Routing/Route');
 			if ($routeClass === false) {
-				throw new InvalidArgumentException(sprintf('Cannot find route class %s', $options['routeClass']));
+				throw new InvalidArgumentException(sprintf(
+					'Cannot find route class %s',
+					$options['routeClass']
+				));
 			}
-			unset($options['routeClass']);
 
 			$route = str_replace('//', '/', $this->_path . $route);
 			$route = $route === '/' ? $route : rtrim($route, '/');
@@ -503,7 +534,10 @@ class RouteBuilder {
 			$path = $this->_path . $path;
 		}
 		$params = $params + $this->_params;
-		$builder = new static($this->_collection, $path, $params, $this->_extensions);
+		$builder = new static($this->_collection, $path, $params, [
+			'routeClass' => $this->_routeClass,
+			'extensions' => $this->_extensions
+		]);
 		$callback($builder);
 	}
 
@@ -515,8 +549,12 @@ class RouteBuilder {
  * @return void
  */
 	public function fallbacks() {
-		$this->connect('/:controller', ['action' => 'index'], ['routeClass' => 'InflectedRoute']);
-		$this->connect('/:controller/:action/*', [], ['routeClass' => 'InflectedRoute']);
+		$routeClass = $this->_routeClass;
+		if ($routeClass === 'Cake\Routing\Route\Route') {
+			$routeClass = 'InflectedRoute';
+		}
+		$this->connect('/:controller', ['action' => 'index'], compact('routeClass'));
+		$this->connect('/:controller/:action/*', [], compact('routeClass'));
 	}
 
 }

+ 24 - 1
src/Routing/Router.php

@@ -45,6 +45,13 @@ class Router {
 	public static $initialized = false;
 
 /**
+ * Default route class.
+ *
+ * @var bool
+ */
+	protected static $_defaultRouteClass = 'Cake\Routing\Route\Route';
+
+/**
  * Contains the base string that will be applied to all generated URLs
  * For example `https://example.com`
  *
@@ -143,6 +150,19 @@ class Router {
 	protected static $_urlFilters = [];
 
 /**
+ * Get or set default route class.
+ *
+ * @param string|null $routeClass Class name.
+ * @return string|void
+ */
+	public static function defaultRouteClass($routeClass = null) {
+		if ($routeClass == null) {
+			return static::$_defaultRouteClass;
+		}
+		static::$_defaultRouteClass = $routeClass;
+	}
+
+/**
  * Gets the named route patterns for use in config/routes.php
  *
  * @return array Named route elements
@@ -822,7 +842,10 @@ class Router {
  *   was created/used.
  */
 	public static function scope($path, $params = [], $callback = null) {
-		$builder = new RouteBuilder(static::$_collection, '/', [], static::$_collection->extensions());
+		$builder = new RouteBuilder(static::$_collection, '/', [], [
+			'routeClass' => static::defaultRouteClass(),
+			'extensions' => static::$_collection->extensions()
+		]);
 		$builder->scope($path, $params, $callback);
 	}
 

+ 47 - 3
tests/TestCase/Routing/RouteBuilderTest.php

@@ -81,6 +81,31 @@ class RouteBuilderTest extends TestCase {
 	}
 
 /**
+ * Test setting default route class
+ *
+ * @return void
+ */
+	public function testRouteClass() {
+		$routes = new RouteBuilder($this->collection, '/l', [],
+			['routeClass' => 'InflectedRoute']
+		);
+		$routes->connect('/:controller', ['action' => 'index']);
+		$routes->connect('/:controller/:action/*');
+
+		$all = $this->collection->routes();
+		$this->assertInstanceOf('Cake\Routing\Route\InflectedRoute', $all[0]);
+		$this->assertInstanceOf('Cake\Routing\Route\InflectedRoute', $all[1]);
+
+		$this->collection = new RouteCollection();
+		$routes = new RouteBuilder($this->collection, '/l');
+		$routes->routeClass('TestApp\Routing\Route\DashedRoute');
+
+		$routes->connect('/:controller', ['action' => 'index']);
+		$all = $this->collection->routes();
+		$this->assertInstanceOf('TestApp\Routing\Route\DashedRoute', $all[0]);
+	}
+
+/**
  * Test connecting an instance routes.
  *
  * @return void
@@ -132,7 +157,9 @@ class RouteBuilderTest extends TestCase {
  * @return void
  */
 	public function testConnectExtensions() {
-		$routes = new RouteBuilder($this->collection, '/l', [], ['json']);
+		$routes = new RouteBuilder($this->collection, '/l', [],
+			['extensions' => ['json']]
+		);
 		$this->assertEquals(['json'], $routes->extensions());
 
 		$routes->connect('/:controller');
@@ -167,7 +194,9 @@ class RouteBuilderTest extends TestCase {
  * @return void
  */
 	public function testConnectErrorInvalidRouteClass() {
-		$routes = new RouteBuilder($this->collection, '/l', [], ['json']);
+		$routes = new RouteBuilder($this->collection, '/l', [],
+			['extensions' => ['json']]
+		);
 		$routes->connect('/:controller', [], ['routeClass' => '\StdClass']);
 	}
 
@@ -179,7 +208,7 @@ class RouteBuilderTest extends TestCase {
  * @return void
  */
 	public function testConnectConflictingParameters() {
-		$routes = new RouteBuilder($this->collection, '/admin', ['prefix' => 'admin'], []);
+		$routes = new RouteBuilder($this->collection, '/admin', ['prefix' => 'admin']);
 		$routes->connect('/', ['prefix' => 'manager', 'controller' => 'Dashboard', 'action' => 'view']);
 	}
 
@@ -406,6 +435,21 @@ class RouteBuilderTest extends TestCase {
 		$all = $this->collection->routes();
 		$this->assertEquals('/api/:controller', $all[0]->template);
 		$this->assertEquals('/api/:controller/:action/*', $all[1]->template);
+		$this->assertInstanceOf('Cake\Routing\Route\InflectedRoute', $all[0]);
+	}
+
+/**
+ * Test connecting fallback routes after setting default route class.
+ *
+ * @return void
+ */
+	public function testDefaultRouteClassFallbacks() {
+		$routes = new RouteBuilder($this->collection, '/api', ['prefix' => 'api']);
+		$routes->routeClass('TestApp\Routing\Route\DashedRoute');
+		$routes->fallbacks();
+
+		$all = $this->collection->routes();
+		$this->assertInstanceOf('TestApp\Routing\Route\DashedRoute', $all[0]);
 	}
 
 /**

+ 32 - 2
tests/TestCase/Routing/RouterTest.php

@@ -50,9 +50,8 @@ class RouterTest extends TestCase {
 	public function tearDown() {
 		parent::tearDown();
 		Plugin::unload();
-		Router::fullBaseUrl('');
-		Configure::write('App.fullBaseUrl', 'http://localhost');
 		Router::reload();
+		Router::defaultRouteClass('Cake\Routing\Route\Route');
 	}
 
 /**
@@ -2665,6 +2664,37 @@ class RouterTest extends TestCase {
 	}
 
 /**
+ * Test setting default route class.
+ *
+ * @return void
+ */
+	public function testDefaultRouteClass() {
+		Router::connect('/:controller', ['action' => 'index']);
+		$result = Router::url(['controller' => 'FooBar', 'action' => 'index']);
+		$this->assertEquals('/FooBar', $result);
+
+		// This is needed because tests/boostrap.php sets App.namespace to 'App'
+		Configure::write('App.namespace', 'TestApp');
+
+		Router::defaultRouteClass('DashedRoute');
+		Router::connect('/cake/:controller', ['action' => 'cake']);
+		$result = Router::url(['controller' => 'FooBar', 'action' => 'cake']);
+		$this->assertEquals('/cake/foo-bar', $result);
+
+		$result = Router::url(['controller' => 'FooBar', 'action' => 'index']);
+		$this->assertEquals('/FooBar', $result);
+
+		Router::reload();
+		Router::defaultRouteClass('DashedRoute');
+		Router::scope('/', function($routes) {
+			$routes->fallbacks();
+		});
+
+		$result = Router::url(['controller' => 'FooBar', 'action' => 'index']);
+		$this->assertEquals('/foo-bar', $result);
+	}
+
+/**
  * Connect some fallback routes for testing router behavior.
  *
  * @return void

+ 20 - 0
tests/test_app/TestApp/Routing/Route/DashedRoute.php

@@ -0,0 +1,20 @@
+<?php
+namespace TestApp\Routing\Route;
+
+use Cake\Routing\Route\InflectedRoute;
+
+class DashedRoute extends InflectedRoute {
+
+	protected function _underscore($url) {
+		$url = parent::_underscore($url);
+
+		if (!empty($url['controller'])) {
+			$url['controller'] = str_replace('_', '-', $url['controller']);
+		}
+		if (!empty($url['plugin'])) {
+			$url['plugin'] = str_replace('_', '-', $url['plugin']);
+		}
+		return $url;
+	}
+
+}