Adding header detection to Router, and implementing prototype REST routes

git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@5454 3807eeeb-6ff5-0310-8944-8be069107fe0
This commit is contained in:
nate 2007-07-24 13:50:50 +00:00
parent bdaca1698b
commit 7b4264b68c
3 changed files with 182 additions and 21 deletions

View file

@ -90,6 +90,30 @@ class Router extends Object {
* @access private * @access private
*/ */
var $__currentRoute = array(); var $__currentRoute = array();
/**
* HTTP header shortcut map. Used for evaluating header-based route expressions.
*
* @var array
* @access private
*/
var $__headerMap = array(
'type' => 'content_type',
'method' => 'request_method',
'server' => 'server_name'
);
/**
* Default HTTP request method => controller action map.
*
* @var array
* @access private
*/
var $__resourceMap = array(
'index' => array('method' => 'GET', 'id' => false),
'view' => array('method' => 'GET', 'id' => true),
'add' => array('method' => 'POST', 'id' => false),
'edit' => array('method' => 'PUT', 'id' => true),
'delete' => array('method' => 'DELETE', 'id' => true)
);
/** /**
* Maintains the parameter stack for the current request * Maintains the parameter stack for the current request
* *
@ -167,21 +191,13 @@ class Router extends Object {
*/ */
function connect($route, $default = array(), $params = array()) { function connect($route, $default = array(), $params = array()) {
$_this =& Router::getInstance(); $_this =& Router::getInstance();
$parsed = array();
if (defined('CAKE_ADMIN') && $default == null) { if (defined('CAKE_ADMIN') && $default == null && $route == CAKE_ADMIN) {
if ($route == CAKE_ADMIN) {
$_this->routes[] = $_this->__admin; $_this->routes[] = $_this->__admin;
$_this->__admin = null; $_this->__admin = null;
} }
} $default = am(array('plugin' => null, 'controller' => null), $default);
if (empty($default['plugin'])) {
$default['plugin'] = null;
}
if (empty($default['controller'])) {
$default['controller'] = null;
}
if (!empty($default) && empty($default['action'])) { if (!empty($default) && empty($default['action'])) {
$default['action'] = 'index'; $default['action'] = 'index';
} }
@ -190,6 +206,36 @@ class Router extends Object {
} }
return $_this->routes; return $_this->routes;
} }
/**
* Creates REST resource routes for the given controller(s)
*
* @param mixed $controller A controller name or array of controller names (i.e. "Posts" or "ListItems")
* @param array $options
* @access public
* @static
*/
function mapResources($controller, $options = array()) {
$_this =& Router::getInstance();
$options = am(
array('prefix' => '/'),
$options
);
$prefix = $options['prefix'];
foreach((array)$controller as $ctlName) {
$urlName = Inflector::underscore($ctlName);
foreach ($_this->__resourceMap as $action => $params) {
$id = null;
if ($params['id']) {
$id = '/:id';
}
Router::connect(
"{$prefix}{$urlName}{$id}",
array('controller' => $urlName, 'action' => $action, '[method]' => $params['method'])
);
}
}
}
/** /**
* Builds a route regular expression * Builds a route regular expression
* *
@ -246,7 +292,7 @@ class Router extends Object {
function parse($url) { function parse($url) {
$_this =& Router::getInstance(); $_this =& Router::getInstance();
$_this->__connectDefaultRoutes(); $_this->__connectDefaultRoutes();
$out = array('pass'=>array()); $out = array('pass' => array());
$r = $ext = null; $r = $ext = null;
if ($url && strpos($url, '/') !== 0) { if ($url && strpos($url, '/') !== 0) {
@ -258,13 +304,12 @@ class Router extends Object {
extract($_this->__parseExtension($url)); extract($_this->__parseExtension($url));
foreach ($_this->routes as $route) { foreach ($_this->routes as $route) {
if (($r = $_this->matchRoute($route, $url)) !== false) {
$_this->__currentRoute[] = $route;
list($route, $regexp, $names, $defaults) = $route; list($route, $regexp, $names, $defaults) = $route;
if (preg_match($regexp, $url, $r)) {
$_this->__currentRoute[] = $route;
// remove the first element, which is the url // remove the first element, which is the url
array_shift ($r); array_shift($r);
// hack, pre-fill the default route names // hack, pre-fill the default route names
foreach ($names as $name) { foreach ($names as $name) {
$out[$name] = null; $out[$name] = null;
@ -302,6 +347,36 @@ class Router extends Object {
} }
return $out; return $out;
} }
/**
* Checks to see if the given URL matches the given route
*
* @param array $route
* @param string $url
* @return mixed Boolean false on failure, otherwise array
* @access public
*/
function matchRoute($route, $url) {
$_this =& Router::getInstance();
list($route, $regexp, $names, $defaults) = $route;
if (!preg_match($regexp, $url, $r)) {
return false;
} else {
foreach ($defaults as $key => $val) {
if (preg_match('/^\[(\w+)\]$/', $key, $header)) {
if (isset($_this->__headerMap[$header[1]])) {
$header = $_this->__headerMap[$header[1]];
} else {
$header = 'http_' . $header[1];
}
if (env(strtoupper($header)) != $val) {
return false;
}
}
}
}
return $r;
}
/** /**
* Parses a file extension out of a URL, if Router::parseExtensions() is enabled. * Parses a file extension out of a URL, if Router::parseExtensions() is enabled.
* *
@ -558,7 +633,7 @@ class Router extends Object {
if (defined('CAKE_ADMIN') && isset($url[CAKE_ADMIN]) && $url[CAKE_ADMIN]) { if (defined('CAKE_ADMIN') && isset($url[CAKE_ADMIN]) && $url[CAKE_ADMIN]) {
array_unshift($urlOut, CAKE_ADMIN); array_unshift($urlOut, CAKE_ADMIN);
} }
$output = join('/', $urlOut); $output = join('/', $urlOut) . '/';
} }
foreach (array('args', 'named') as $var) { foreach (array('args', 'named') as $var) {
@ -828,6 +903,56 @@ class Router extends Object {
$_this->__validExtensions = func_get_args(); $_this->__validExtensions = func_get_args();
} }
} }
/**
* Takes an array of params and converts it to named args
*
* @access public
* @param array $params
* @param mixed $named
* @param string $separator
* @static
*/
function getArgs($params, $named = true, $separator = ':') {
$passedArgs = $namedArgs = array();
if (is_array($named)) {
if (array_key_exists($params['action'], $named)) {
$named = $named[$params['action']];
}
$namedArgs = true;
}
if (!empty($params['pass'])) {
$passedArgs = $params['pass'];
if ($namedArgs === true || $named == true) {
$namedArgs = array();
$c = count($passedArgs);
for ($i = 0; $i <= $c; $i++) {
if (isset($passedArgs[$i]) && strpos($passedArgs[$i], $separator) !== false) {
list($argKey, $argVal) = explode($separator, $passedArgs[$i]);
if ($named === true || (!empty($named) && in_array($argKey, array_keys($named)))) {
$passedArgs[$argKey] = $argVal;
$namedArgs[$argKey] = $argVal;
unset($passedArgs[$i]);
unset($params['pass'][$i]);
}
} elseif ($separator === '/') {
$ii = $i + 1;
if (isset($passedArgs[$i]) && isset($passedArgs[$ii])) {
$argKey = $passedArgs[$i];
$argVal = $passedArgs[$ii];
if (empty($namedArgs) || (!empty($namedArgs) && in_array($argKey, array_keys($namedArgs)))) {
$passedArgs[$argKey] = $argVal;
$namedArgs[$argKey] = $argVal;
unset($passedArgs[$i], $passedArgs[$ii]);
unset($params['pass'][$i], $params['pass'][$ii]);
}
}
}
}
}
}
return array($passedArgs, $namedArgs);
}
} }
if (!function_exists('http_build_query')) { if (!function_exists('http_build_query')) {

View file

@ -48,6 +48,8 @@ class RouterTest extends UnitTestCase {
$this->router->testVar = 'test'; $this->router->testVar = 'test';
$this->assertIdentical($this->router, Router::getInstance()); $this->assertIdentical($this->router, Router::getInstance());
unset($this->router->testVar); unset($this->router->testVar);
//pr($_SERVER);
echo "<form method='post' action='http://localhost/cake/testbed/webroot/test.php?case=libs%2Frouter.test.php'><input type='text' /></form>";
} }
function testRouteWriting() { function testRouteWriting() {
@ -100,6 +102,31 @@ class RouterTest extends UnitTestCase {
$this->assertEqual(get_object_vars($this->router), get_object_vars($router2)); $this->assertEqual(get_object_vars($this->router), get_object_vars($router2));
} }
function testResourceRoutes() {
$this->router->reload();
$this->router->mapResources('Posts');
$_SERVER['REQUEST_METHOD'] = 'GET';
$result = $this->router->parse('/posts');
$this->assertEqual($result, array ('pass' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'index', '[method]' => 'GET'));
$_SERVER['REQUEST_METHOD'] = 'GET';
$result = $this->router->parse('/posts/13');
$this->assertEqual($result, array ('pass' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'view', 'id' => '13', '[method]' => 'GET'));
$_SERVER['REQUEST_METHOD'] = 'POST';
$result = $this->router->parse('/posts');
$this->assertEqual($result, array ('pass' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'add', '[method]' => 'POST'));
$_SERVER['REQUEST_METHOD'] = 'PUT';
$result = $this->router->parse('/posts/13');
$this->assertEqual($result, array ('pass' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'edit', 'id' => '13', '[method]' => 'PUT'));
$_SERVER['REQUEST_METHOD'] = 'DELETE';
$result = $this->router->parse('/posts/13');
$this->assertEqual($result, array ('pass' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'delete', 'id' => '13', '[method]' => 'DELETE'));
}
function testUrlGeneration() { function testUrlGeneration() {
extract($this->router->getNamedExpressions()); extract($this->router->getNamedExpressions());
@ -302,6 +329,15 @@ class RouterTest extends UnitTestCase {
$expected = '/posts/index/published:0/deleted:0'; $expected = '/posts/index/published:0/deleted:0';
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
} }
function testParamsUrlParsing() {
$this->router->routes = array();
Router::connect('/', array('controller' => 'posts', 'action' => 'index'));
Router::connect('/view/:user/*', array('controller' => 'posts', 'action' => 'view'), array('user'));
$result = $this->router->parse('/view/gwoo/');
$expected = array('user' => 'gwoo', 'controller' => 'posts', 'action' => 'view', 'plugin' =>'', 'pass' => array());
$this->assertEqual($result, $expected);
}
} }
?> ?>

View file

@ -59,11 +59,11 @@ class ViewTest extends UnitTestCase {
function testUUIDGeneration() { function testUUIDGeneration() {
$result = $this->view->uuid('form', array('controller' => 'posts', 'action' => 'index')); $result = $this->view->uuid('form', array('controller' => 'posts', 'action' => 'index'));
$this->assertEqual($result, 'form5988016017'); $this->assertEqual($result, 'form0425fe3bad');
$result = $this->view->uuid('form', array('controller' => 'posts', 'action' => 'index')); $result = $this->view->uuid('form', array('controller' => 'posts', 'action' => 'index'));
$this->assertEqual($result, 'formc3dc6be854'); $this->assertEqual($result, 'forma9918342a7');
$result = $this->view->uuid('form', array('controller' => 'posts', 'action' => 'index')); $result = $this->view->uuid('form', array('controller' => 'posts', 'action' => 'index'));
$this->assertEqual($result, 'form28f92cc87f'); $this->assertEqual($result, 'form3ecf2e3e96');
} }
function testAddInlineScripts() { function testAddInlineScripts() {