Permalink
Browse files

Implementing formatters for route parameters.

  • Loading branch information...
1 parent 44015db commit 2dc40c9b6e5bc55a790cd37cd05efb510a8a6517 @nateabele nateabele committed Apr 11, 2012
Showing with 107 additions and 22 deletions.
  1. +17 −6 net/http/Route.php
  2. +65 −15 net/http/Router.php
  3. +6 −1 tests/cases/net/http/RouteTest.php
  4. +19 −0 tests/cases/net/http/RouterTest.php
View
@@ -150,14 +150,22 @@ class Route extends \lithium\core\Object {
protected $_handler = null;
/**
+ * Array of closures used to format route parameters when compiling URLs.
+ *
+ * @see lithium\net\http\Router::formatters()
+ * @var array
+ */
+ protected $_formatters = array();
+
+ /**
* Auto configuration properties. Also used as the list of properties to return when exporting
* this `Route` object to an array.
*
* @see lithium\net\http\Route::export()
* @var array
*/
protected $_autoConfig = array(
- 'template', 'pattern', 'params', 'match', 'meta',
+ 'template', 'pattern', 'params', 'match', 'meta', 'formatters',
'keys', 'defaults', 'subPatterns', 'persist', 'handler'
);
@@ -172,7 +180,8 @@ public function __construct(array $config = array()) {
'keys' => array(),
'persist' => array(),
'handler' => null,
- 'continue' => false
+ 'continue' => false,
+ 'formatters' => array()
);
parent::__construct($config + $defaults);
}
@@ -336,10 +345,6 @@ protected function _matchKeys($options) {
protected function _write($options, $defaults) {
$template = $this->_template;
$trimmed = true;
-
- if (isset($options['args']) && is_array($options['args'])) {
- $options['args'] = join('/', $options['args']);
- }
$options += array('args' => '');
foreach (array_reverse($this->_keys, true) as $key) {
@@ -354,6 +359,9 @@ protected function _write($options, $defaults) {
continue;
}
}
+ if (isset($this->_formatters[$key])) {
+ $value = $this->_formatters[$key]($value);
+ }
if ($value === null) {
$template = str_replace("/{$rpl}", '', $template);
continue;
@@ -377,6 +385,9 @@ public function export() {
$result = array();
foreach ($this->_autoConfig as $key) {
+ if ($key === 'formatters') {
+ continue;
+ }
$result[$key] = $this->{'_' . $key};
}
return $result;
View
@@ -45,13 +45,23 @@
class Router extends \lithium\core\StaticObject {
/**
- * An array of loaded lithium\net\http\Route objects used to match Request objects against.
+ * An array of loaded `Route` objects used to match Request objects against.
*
+ * @see lithium\net\http\Route
* @var array
*/
protected static $_configurations = array();
/**
+ * An array of named closures matching up to corresponding route parameter values. Used to
+ * format those values.
+ *
+ * @see lithium\net\http\Router::formatters()
+ * @var array
+ */
+ protected static $_formatters = array();
+
+ /**
* Classes used by `Router`.
*
* @var array
@@ -84,21 +94,23 @@ public static function config($config = array()) {
* @return array Array of routes
*/
public static function connect($template, $params = array(), $options = array()) {
- if (!is_object($template)) {
- if (is_string($params)) {
- $params = static::_parseString($params, false);
- }
- if (isset($params[0]) && is_array($tmp = static::_parseString($params[0], false))) {
- unset($params[0]);
- $params = $tmp + $params;
- }
- if (is_callable($options)) {
- $options = array('handler' => $options);
- }
- $class = static::$_classes['route'];
- $template = new $class(compact('template', 'params') + $options);
+ if (is_object($template)) {
+ return (static::$_configurations[] = $template);
+ }
+ if (is_string($params)) {
+ $params = static::_parseString($params, false);
}
- return (static::$_configurations[] = $template);
+ if (isset($params[0]) && is_array($tmp = static::_parseString($params[0], false))) {
+ unset($params[0]);
+ $params = $tmp + $params;
+ }
+ if (is_callable($options)) {
+ $options = array('handler' => $options);
+ }
+ $config = compact('template', 'params') + $options + array(
+ 'formatters' => static::formatters()
+ );
+ return (static::$_configurations[] = static::_instance('route', $config));
}
/**
@@ -116,6 +128,44 @@ public static function process($request) {
}
/**
+ * Used to get or set an array of named formatter closures, which are used to format route
+ * parameters when generating URLs. For example, for controller/action parameters to be dashed
+ * instead of underscored or camelBacked, you could do the following:
+ *
+ * {{{
+ * use lithium\util\Inflector;
+ *
+ * Router::formatters(array(
+ * 'controller' => function($value) { return Inflector::slug($value); },
+ * 'action' => function($value) { return Inflector::slug($value); }
+ * ));
+ * }}}
+ *
+ * _Note_: Because formatters are copied to `Route` objects on an individual basis, make sure
+ * you append custom formatters _before_ connecting new routes.
+ *
+ * @param array $formatters An array of named formatter closures to append to (or overwrite) the
+ * existing list.
+ * @return array Returns the formatters array.
+ */
+ public static function formatters(array $formatters = array()) {
+ if (!static::$_formatters) {
+ static::$_formatters = array(
+ 'args' => function($value) {
+ return is_array($value) ? join('/', $value) : $value;
+ },
+ 'controller' => function($value) {
+ return Inflector::underscore($value);
+ }
+ );
+ }
+ if ($formatters) {
+ static::$_formatters = array_filter($formatters + static::$_formatters);
+ }
+ return static::$_formatters;
+ }
+
+ /**
* Accepts an instance of `lithium\action\Request` (or a subclass) and matches it against each
* route, in the order that the routes are connected.
*
@@ -209,7 +209,12 @@ public function testRouteParsingWithOptionalParamsAndType() {
}
public function testRouteMatchingWithEmptyTrailingParams() {
- $route = new Route(array('template' => '/{:controller}/{:action}/{:args}'));
+ $route = new Route(array(
+ 'template' => '/{:controller}/{:action}/{:args}',
+ 'formatters' => array('args' => function($value) {
+ return is_array($value) ? join('/', $value) : $value;
+ })
+ ));
$result = $route->match(array('controller' => 'posts'));
$this->assertEqual('/posts', $result);
@@ -777,6 +777,25 @@ public function testUnicodeParameters() {
$result = Router::parse($request, array('url' => $request->url));
$this->assertEqual(array('controller' => 'users', 'action' => 'view', 'slug' => $slug), $result->params);
}
+
+ /**
+ * Tests default route formatters, and setting/getting new formatters.
+ */
+ public function testRouteFormatters() {
+ $formatters = Router::formatters();
+ $this->assertEqual(array('args', 'controller'), array_keys($formatters));
+
+ $this->assertEqual('foo/bar', $formatters['args'](array('foo', 'bar')));
+ $this->assertEqual('list_items', $formatters['controller']('ListItems'));
+
+ Router::formatters(array('action' => function($value) { return strtolower($value); }));
+ $formatters = Router::formatters();
+ $this->assertEqual(array('action', 'args', 'controller'), array_keys($formatters));
+
+ Router::formatters(array('action' => null));
+ $formatters = Router::formatters();
+ $this->assertEqual(array('args', 'controller'), array_keys($formatters));
+ }
}
?>

0 comments on commit 2dc40c9

Please sign in to comment.