Skip to content

DrF Reverse Routing

Derek Jones edited this page Jul 5, 2012 · 29 revisions

DrF Reverse Routing is an extension of the URL Helper that overrides the site_url function to provide a reverse lookup. If a custom route can be found that matches the given "standard CodeIgniter URI", a custom route URI will be created and returned. Otherwise, a standard site_url will be returned.

Discuss this in the Forum

Overview

  • Translates a "standard CodeIgniter URI" into a custom route if possible. Standard, in this case, means controller/function/param1/param2/.../paramN
  • Fails gracefully, providing a standard site_url if no route can be found/translated
  • Works automatically with all of URL Helper's functions
  • Concept inspired by CakePHP's reverse routing

Usage

  • place MY_url_helper.php in your system/application/helpers folder
  • include the URL helper in your controller ```php $this->load->helper( 'url' );
- use URL Helper's functions as you normally would ## Example - setup a Test Controller ```php <?php class Test extends Controller{ function Test() { parent::Controller(); } function testFunc( $param1 ){ echo 'You passed in: ' . $param1; } function redirect() { $this->load->helper( 'url' ); redirect( 'test/testFunc/bar' ); } } ?> 
  • create a route for the test function
... $route['foo/(:any)'] = 'test/testFunc/$1'; ...

Download

File:DrF_Reverse_Routing.20090331.zip

Changelog

2009.03.31 by AJ Heller aj@drfloob.com Fixed a bug that did not handle unordered backreferences correctly (when $2 comes before $1, for example. Bug reported by Johan André View original report

Code

system/application/helpers/MY_url_helper.php

&lt;?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); /**  * DrF Reverse Routing  *  * @author AJ Heller <aj@drfloob.com>  * @link http://drfloob.com/  */ // ------------------------------------------------------------------------ /**  * (Reverse) Site_URL  *  * Returns a custom routed URL if one exists. Returns a normal site_url otherwise.  * It works by translating the passed-in URI into a custom route URI, if possible.  * This function does not handle ANY regex used without capture-groups and back-references.  * Visit http://drfloob.com/codeIgniter/reverse_redirect to learn why  *  * @access public  * @param $uri The standard CI URL, e.g. controller/function/param1  * @param $method  * @param $http_response_code  */ function site_url($uri = '') { $Router =& load_class('Router'); // $uri is expected to be a string, in the form of controller/function/param1 // trim leading and trailing slashes, just in case $uri = trim($uri,'/'); $routes = $Router->routes; $reverseRoutes = array_flip( $routes ); unset( $routes['default_controller'], $routes['scaffolding_trigger'] ); // Loop through all routes to check for back-references, then see if the user-supplied URI matches one  foreach ($routes as $key => $val) { // bailing if route contains ungrouped regex, otherwise this fails badly if( preg_match( '/[^\(][.+?{\:]/', $key ) ) continue; // Do we have a back-reference?  if (strpos($val, '$') !== FALSE AND strpos($key, '(') !== FALSE) { // Find all back-references in custom route and CI route  preg_match_all( '/\(.+?\)/', $key, $keyRefs ); preg_match_all( '/\$.+?/', $val, $valRefs ); $keyRefs = $keyRefs[0]; // Create URI Regex, to test passed-in uri against a custom route's CI ( standard ) route $uriRegex = $val; // Extract positional parameters (backreferences), and order them such that // the keys of $goodValRefs dirrectly mirror the correct value in $keyRefs $goodValRefs = array(); foreach ($valRefs[0] as $ref) { $tempKey = substr($ref, 1); if (is_numeric($tempKey)) { --$tempKey; $goodValRefs[$tempKey] = $ref; } } // Replaces back-references in CI route with custom route's regex [ $1 replaced with (:num), for example ] foreach ($goodValRefs as $tempKey => $ref) { if (isset($keyRefs[$tempKey])) { $uriRegex = str_replace($ref, $keyRefs[$tempKey], $uriRegex); } } // replace :any and :num with .+ and [0-9]+, respectively $uriRegex = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $uriRegex)); // regex creation is finished. Test it against uri if (preg_match('#^'.$uriRegex.'$#', $uri)){ // A match was found. We can now build the custom URI // We need to create a custom route back-referenced regex, to plug user's uri params into the new routed uri. // First, find all custom route strings between capture groups $key = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $key)); $routeString = preg_split( '/\(.+?\)/', $key ); // build regex using original CI route's back-references $replacement = ''; $rsEnd = count( $routeString ) - 1; // merge route strings with original back-references, 1-for-1, like a zipper for( $i = 0; $i < $rsEnd; $i++ ){ $replacement .= $routeString[$i] . $valRefs[0][$i]; } $replacement .= $routeString[$rsEnd]; /*  At this point,our variables are defined as:  $uriRegex: regex to match against user-supplied URI  $replacement: custom route regex, replacing capture-groups with back-references    All that's left to do is create the custom URI, and return the site_url  */ $customURI = preg_replace( '#^'.$uriRegex.'$#', $replacement, $uri ); return normal_site_url( $customURI ); } } // If there is a literal match AND no back-references are setup, and we are done else if($val == $uri) return normal_site_url( $key ); } return normal_site_url( $uri ); } function normal_site_url($uri = '') { $CI =& get_instance(); return $CI->config->site_url($uri); } 
Clone this wiki locally