Permalink
Browse files

Added support for redirection routes. Fixes #1151

  • Loading branch information...
1 parent 692aafb commit 7630580511f1ece4bac3e1ea3d6f2c613de1f243 @jeremyharris jeremyharris committed Nov 8, 2010
@@ -0,0 +1,74 @@
+<?php
+App::import('Core', 'CakeResponse');
+App::import('Core', 'route/CakeRoute');
+/**
+ * Redirect route will perform an immediate redirect
+ *
+ * PHP5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link http://cakephp.org CakePHP(tm) Project
+ * @package cake
+ * @subpackage cake.cake.libs.route
+ * @since CakePHP(tm) v 2.0
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class RedirectRoute extends CakeRoute {
+
+/**
+ * A CakeResponse object
+ *
+ * @var CakeResponse
+ */
+ public $response = null;
+
+/**
+ * Parses a string url into an array. Parsed urls will result in an automatic
+ * redirection
+ *
+ * @param string $url The url to parse
+ * @return boolean False on failure
+ */
+ public function parse($url) {
+ $params = parent::parse($url);
+ if (!$params) {
+ return false;
+ }
+ if (!$this->response) {
+ $this->response = new CakeResponse();
+ }
+ $redirect = $this->defaults;
+ if (count($this->defaults) == 1 && !isset($this->defaults['controller'])) {
+ $redirect = $this->defaults[0];
+ }
+ if (isset($this->options['persist']) && is_array($redirect)) {
+ $argOptions['context'] = array('action' => $redirect['action'], 'controller' => $redirect['controller']);
+ $args = Router::getArgs($params['_args_'], $argOptions);
+ $redirect += $args['pass'];
+ $redirect += $args['named'];
+ }
+ $status = 301;
+ if (isset($this->options['status']) && ($this->options['status'] >= 300 && $this->options['status'] < 400)) {
+ $status = $this->options['status'];
+ }
+ $this->response->header(array('Location' => Router::url($redirect, true)));
+ $this->response->statusCode($status);
+ $this->response->send();
+ }
+
+/**
+ * There is no reverse routing redirection routes
+ *
+ * @param array $url Array of parameters to convert to a string.
+ * @return mixed either false or a string url.
+ */
+ public function match($url) {
+ return false;
+ }
+}
View
@@ -254,6 +254,41 @@ public static function connect($route, $defaults = array(), $options = array())
}
/**
+ * Connects a new redirection Route in the router.
+ *
+ * Redirection routes are different from normal routes as they perform an actual
+ * header redirection if a match is found. The redirection can occur within your
+ * application or redirect to an outside location.
+ *
+ * Examples:
+ *
+ * `Router::redirect('/home/*', array('controller' => 'posts', 'action' => 'view', array('persist' => true));`
+ *
+ * Redirects /home/* to /posts/view and passes the parameters to /posts/view
+ *
+ * `Router::redirect('/posts/*', 'http://google.com', array('status' => 302));`
+ *
+ * Redirects /posts/* to http://google.com with a HTTP status of 302
+ *
+ * ### Options:
+ * - `status` Sets the HTTP status (default 301)
+ * - `persist` Passes the params to the redirected route, if it can
+ *
+ * @param string $route A string describing the template of the route
+ * @param array $url A url to redirect to. Can be a string or a Cake array-based url
+ * @param array $options An array matching the named elements in the route to regular expressions which that
+ * element should match. Also contains additional parameters such as which routed parameters should be
+ * shifted into the passed arguments. As well as supplying patterns for routing parameters.
+ * @see routes
+ * @return array Array of routes
+ */
+ public static function redirect($route, $url, $options) {
+ App::import('Core', 'route/RedirectRoute');
+ $options['routeClass'] = 'RedirectRoute';
+ return self::connect($route, $url, $options);
+ }
+
+/**
* Specifies what named parameters CakePHP should be parsing. The most common setups are:
*
* Do not parse any named parameters:
@@ -0,0 +1,71 @@
+<?php
+App::import('Core', 'route/RedirectRoute');
+App::import('Core', 'CakeResponse');
+App::import('Core', 'Router');
+/**
+ * test case for RedirectRoute
+ *
+ * @package cake.tests.libs.route
+ */
+class RedirectRouteTestCase extends CakeTestCase {
+/**
+ * setUp method
+ *
+ * @return void
+ */
+ function setUp() {
+ parent::setUp();
+ Configure::write('Routing', array('admin' => null, 'prefixes' => array()));
+ Router::reload();
+ }
+
+/**
+ * test the parsing of routes.
+ *
+ * @return void
+ */
+ function testParsing() {
+ $route = new RedirectRoute('/home', array('controller' => 'posts'));
+ $route->response = $this->getMock('CakeResponse', array('_sendHeader'));
+ $result = $route->parse('/home');
+ $this->assertEqual($route->response->header(), array('Location' => Router::url('/posts', true)));
+
+ $route = new RedirectRoute('/home', array('controller' => 'posts', 'action' => 'index'));
+ $route->response = $this->getMock('CakeResponse', array('_sendHeader'));
+ $result = $route->parse('/home');
+ $this->assertEqual($route->response->header(), array('Location' => Router::url('/posts', true)));
+ $this->assertEqual($route->response->statusCode(), 301);
+
+ $route = new RedirectRoute('/google', 'http://google.com');
+ $route->response = $this->getMock('CakeResponse', array('_sendHeader'));
+ $result = $route->parse('/google');
+ $this->assertEqual($route->response->header(), array('Location' => 'http://google.com'));
+
+ $route = new RedirectRoute('/posts/*', array('controller' => 'posts', 'action' => 'view'), array('status' => 302));
+ $route->response = $this->getMock('CakeResponse', array('_sendHeader'));
+ $result = $route->parse('/posts/2');
+ $this->assertEqual($route->response->header(), array('Location' => Router::url('/posts/view', true)));
+ $this->assertEqual($route->response->statusCode(), 302);
+
+ $route = new RedirectRoute('/posts/*', array('controller' => 'posts', 'action' => 'view'), array('persist' => true));
+ $route->response = $this->getMock('CakeResponse', array('_sendHeader'));
+ $result = $route->parse('/posts/2');
+ $this->assertEqual($route->response->header(), array('Location' => Router::url('/posts/view/2', true)));
+
+ $route = new RedirectRoute('/posts/*', '/test', array('persist' => true));
+ $route->response = $this->getMock('CakeResponse', array('_sendHeader'));
+ $result = $route->parse('/posts/2');
+ $this->assertEqual($route->response->header(), array('Location' => Router::url('/test', true)));
+
+ $route = new RedirectRoute('/my_controllers/:action/*', array('controller' => 'tags', 'action' => 'add'), array('persist' => true));
+ $route->response = $this->getMock('CakeResponse', array('_sendHeader'));
+ $result = $route->parse('/my_controllers/do_something/passme/named:param');
+ $this->assertEqual($route->response->header(), array('Location' => Router::url('/tags/add/passme/named:param', true)));
+
+ $route = new RedirectRoute('/my_controllers/:action/*', array('controller' => 'tags', 'action' => 'add'));
+ $route->response = $this->getMock('CakeResponse', array('_sendHeader'));
+ $result = $route->parse('/my_controllers/do_something/passme/named:param');
+ $this->assertEqual($route->response->header(), array('Location' => Router::url('/tags/add', true)));
+ }
+
+}
@@ -18,6 +18,7 @@
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Core', array('Router'));
+App::import('Core', 'CakeResponse');
if (!defined('FULL_BASE_URL')) {
define('FULL_BASE_URL', 'http://cakephp.org');
@@ -2191,4 +2192,24 @@ function testSetRequestInfoLegacy() {
$this->assertEqual($result->here, '/protected/images/index');
$this->assertEqual($result->webroot, '/');
}
+
+/**
+ * test setting redirect routes
+ *
+ * @return void
+ */
+ function testRouteRedirection() {
+ Router::redirect('/blog', array('controller' => 'posts'), array('status' => 302));
+ $this->assertEqual(count(Router::$routes), 1);
+ Router::$routes[0]->response = $this->getMock('CakeResponse', array('_sendHeader'));
+ $this->assertEqual(Router::$routes[0]->options['status'], 302);
+
+ Router::parse('/blog');
+ $this->assertEqual(Router::$routes[0]->response->header(), array('Location' => Router::url('/posts', true)));
+ $this->assertEqual(Router::$routes[0]->response->statusCode(), 302);
+
+ Router::$routes[0]->response = $this->getMock('CakeResponse', array('_sendHeader'));
+ Router::parse('/not-a-match');
+ $this->assertEqual(Router::$routes[0]->response->header(), array());
+ }
}

0 comments on commit 7630580

Please sign in to comment.