Skip to content

Instantly share code, notes, and snippets.

@ball6847
Last active December 19, 2015 17:19
Show Gist options
  • Save ball6847/5990464 to your computer and use it in GitHub Desktop.
Save ball6847/5990464 to your computer and use it in GitHub Desktop.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/* load the MX_Router class */
require APPPATH."third_party/MX/Router.php";
/**
* Extends HMVC Modular Extension to modify route parsing
* to use Route class instead of default route handler
*
*
**/
class MY_Router extends MX_Router {
public function _parse_routes()
{
// Turn the segment array into a URI string
$uri = '/'.implode('/', $this->uri->segments).'/';
$routes = Route::fetch();
foreach ($routes as $prefix => $group)
{
// test prefix match, of not, just skip to next group
if (strpos($uri, $prefix) !== 0)
{
continue;
}
// prefix match, try to check full route
foreach ($group as $route)
{
// Does the RegEx match?
if (preg_match('#^'.$route['regex'].'$#', $uri, $params))
{
// matched parameter will be replaced to the result
// we don't need [0] so we remove it here
unset($params[0]);
// prepare default value for route
$params = array_merge($route['default'], $params);
// replace matched value to the result route
foreach ($params as $name => $value)
{
$route['handler'] = str_replace(':'.$name, $value, $route['handler']);
}
// here we go
return $this->_set_request(explode('/', $route['handler']));
}
}
}
// If we got this far it means we didn't encounter a
// matching route so we'll set the site default route
$this->_set_request($this->uri->segments);
}
}
// -------------------------------------------------------------------------
/**
* Main Route class
*
* @todo more document on this class
*
**/
class Route {
/**
* Rules collection
**/
public static $rules = array();
/**
* Named rules collection
**/
public static $named = array();
// -------------------------------------------------------------------------
/**
* Create a Route Group Object
*
* @param String $location route prefix for this group
* @return Route_Group
**/
public static function group($location)
{
return new Route_Group($location);
}
// -------------------------------------------------------------------------
/**
* Create a Route Group object
* with admin_base configuration options prepended
* this is just to make it quick to add a route to admin area
* You can change url to admin anytime by changing $config['admin_base'] in config.php
* with named route feature, all your link will be automatically updated
*
* @param String $location route to admin
* @return Route_Group
*/
public static function admin($location)
{
return self::group('/'.config_item('admin_base').$location);
}
// -------------------------------------------------------------------------
/**
* Fetch Rule collection
*
* @param String name of group, default to all
* @return Array
*/
public static function fetch($name = NULL)
{
if (is_null($name))
{
return self::$rules;
}
if (isset(self::$rules[$name]))
{
return self::$rules[$name];
}
return array();
}
// -------------------------------------------------------------------------
/**
* Url resolve for a specific route using its name and values
* You can then use site_url or redirect to make it a correct link
*
* @param String $name name of route to resolve
* @param Array $params parameter to resolve to route
* @return String ready to use route, raise an exception if not found or unable resolve route parameter
**/
public static function to($name, $params = array())
{
// try to find pattern in named route
if ( ! isset(self::$named[$name]))
{
throw new Exception('RouteLinker cannot found any route matched with given name ('.$name.')');
}
$pattern = self::$named[$name]['pattern'];
// any parameter binding in pattern
if (preg_match_all('~:\w+~', $pattern, $matches))
{
// override default value with given parameter
$params = array_merge(self::$named[$name]['default'], $params);
// replace each token with parameter value
// raise an error if unable to resolve value for params
foreach ($matches[0] as $args)
{
$key = substr($args, 1);
// if no value binding for parameter and no default value defined in route
// raise an error, cause we cannot let any token left in url
if ( ! isset($params[$key]) AND ! isset($default[$key]))
{
throw new Exception('RouteLinker cannot found any value for parameter name ('.$key.') in '.self::$named[$name]['pattern']);
}
$pattern = str_replace($args, $params[$key], $pattern);
}
}
// remove heading and trailing slash from pattern
// so this pattern can process with site_url() or redirect() later
$pattern = trim($pattern, '/');
return $pattern;
}
}
// -------------------------------------------------------------------------
/**
* Route Group class
*
* Most of time, you wouldn't initiate this class directly
* but from returning object of Route::group() or Route::admin()
* This class contains 2 important method for your routing
* which are Route::group('/')->addRules() and Route::group('/')->addRule()
*
*/
class Route_Group {
protected $base;
/**
* Constructor, set base route passing by Route::group or Route::admin
*
* @param String $base base route or route group prefix
**/
public function __construct($base = '')
{
$this->base = $base;
}
// -------------------------------------------------------------------------
/**
* Add multiple Rules at once
*
* Generally you should use this method for main route registering
* to make your route clean
*
* @todo write more detail on $rules array structure ...
*
* @param Array $rules collection of rule definition
*
**/
public function addRules($rules)
{
foreach ($rules as $name => $rule)
{
$this->addRule($name, $rule);
}
}
// -------------------------------------------------------------------------
/**
* Add single rule, one by one
*
* @param String $name unique identifier of this route (for internal linking purpose)
* @param Array $rule Rule definition
**/
public function addRule($name, $rule)
{
// create container if not already existed
if ( ! isset(Route::$rules[$this->base]))
{
Route::$rules[$this->base] = array();
}
// prepend prefix to route
$rule[0] = $this->base . $rule[0] . '/';
// turn a pattern into regular expression, for using on parsing route in MY_Route::_parse_routes()
$regex = preg_replace('~:(\w+)~', '(?P<$1>[^/]+)', $rule[0]);
// restructure the rules to make it more readable
// and append to Collection
Route::$rules[$this->base][$name] = array(
'pattern' => $rule[0],
'regex' => $regex,
'handler' => $rule[1],
'default' => isset($rule[2]) ? $rule[2] : array()
);
// create additional named rule index
// needed in url resolving process
Route::$named[$name] = array(
// delete all regex optional flag (whatever)?
'pattern' => preg_replace('~\((.*?)\)\?~', '$1', $rule[0]),
'default' => isset($rule[2]) ? $rule[2] : array()
);
}
}
/* end ./application/core/MY_Router.php */
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class News extends MX_Controller {
public function category($category, $page)
{
echo 'Looking for category name "'.$category.'", page "'.$page.'"';
echo 'Linking back using ' . site_url(Route::to('news.category', array('category' => 'sports', 'page' => 15)));
}
}
/* End ./modules/news/controllers/news.php */
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
$route['default_controller'] = "home";
$route['404_override'] = 'error/notfound';
$route['error/404'] = $route['404_override'];
// -------------------------------------------------------------------------
// Example - NEWS Module Route
Route::group('/news')->addRules(array(
'news.category' => array('/:category(/page/:page)?', 'news/category/:category/:page', array('page' => 1)),
'news.detail' => array('/:url', 'news/detail/:url')
));
Route::admin('/news')->addRules(array(
'admin.news.list' => array('/page/:page', 'news/admin/news/page', array('page' => 1)),
'admin.news.add' => array('/add', 'news/admin/news/add'),
'admin.news.edit' => array('/edit/:id', 'news/admin/news/edit/:id'),
'admin.news.trash' => array('/trash/:id', 'news/admin/news/trash/:id'),
'admin.news.delete' => array('/delete/:id', 'news/admin/news/delete/:id'),
'admin.news.view' => array('/view/:id', 'news/admin/news/view/:id'),
'admin.news.edit' => array('/edit/:id', 'news/admin/news/edit/:id')
));
Route::admin('/news/category')->addRules(array(
'admin.news.category.list' => array('/page/:page', 'news/admin/category/page/:page', array('page' => 1)),
'admin.news.category.add' => array('/add', 'news/admin/category/add'),
'admin.news.category.edit' => array('/edit/:id', 'news/admin/category/edit/:id'),
'admin.news.category.trash' => array('/trash/:id', 'news/admin/category/trash/:id'),
'admin.news.category.delete' => array('/delete/:id', 'news/admin/category/delete/:id')
));
/* end ./application/config/routes.php */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment