[CodeIgniter] Dynamic Routes from Database
Posted: Tue Oct 26, 2010 1:47 am
This is from a CMS I am working on. The idea is to allow the following types of URLS:
/ (index)
/page-name
/products/product-name
/news/article-name
/page-name/var_33/setting_off (anything with a _ gets turned into $_GET['var']='33' ; $_GET['setting']='off';
Also in the table for paths, I have:
path_id (unsigned auto increment int)
path (varchar, what is actually in url)
section (enum, pages/products/news/paths - paths basically lets you do two pagenames to same content)
section_id (unsigned it, this is the id for the specific section (table))
status (enum, active/disabled/linkable)
While the routes will easily allow handling of /products/product-name and /news/article-name I have all sections available as dynamic, as I have some uses for this where it is not desirable to prefix each section with its type.
So anyhow, here is how I accomplished it, and please note, this is my first major working with CI, and while I'm building it, yes it is just set for mysql until I can see if I can clean it up some and use built in DB calls...
First: Set up in the main index.php the variable that will hold what gets set via hooks, and what get used via routes:
Second, in applications/hooks.php we set a pre_system hook:
Now we create application/hooks/router_hook.php:
Note, I forget if DB_PREFIX which I have defined in the database.php file is part of CI or something I added...
Now we will either have a good matching route, or not, so set this route up with normal route programming in application/config/routes.php (put it after all other routing code)
there really is no need for the foreach to loop, there will only be one, I just used it to easily get the key/value pair
At this point, you will have a route such as the following, assuming you are viewing a page called 'test', its page_id is 72 and it wasn't redirected:
doing a /test2/action_edit would give you the following, assuming test2 points back to test:
So now, finally in the controller for pages, for the view (note, this has a little SQL specific to my needs, but you get the idea):
Anyhow, that was the way I found to do it, if anyone has suggestions on how to clean it up a bit, I'd welcome them.
-Greg
/ (index)
/page-name
/products/product-name
/news/article-name
/page-name/var_33/setting_off (anything with a _ gets turned into $_GET['var']='33' ; $_GET['setting']='off';
Also in the table for paths, I have:
path_id (unsigned auto increment int)
path (varchar, what is actually in url)
section (enum, pages/products/news/paths - paths basically lets you do two pagenames to same content)
section_id (unsigned it, this is the id for the specific section (table))
status (enum, active/disabled/linkable)
While the routes will easily allow handling of /products/product-name and /news/article-name I have all sections available as dynamic, as I have some uses for this where it is not desirable to prefix each section with its type.
So anyhow, here is how I accomplished it, and please note, this is my first major working with CI, and while I'm building it, yes it is just set for mysql until I can see if I can clean it up some and use built in DB calls...
First: Set up in the main index.php the variable that will hold what gets set via hooks, and what get used via routes:
Code: Select all
// THIS WILL HOLD THE DYNAMIC ROUTE POSSIBLY FOUND IN router_hook.php
$cms_dyn_route = NULL;Code: Select all
require_once (APPPATH."config/database.php");
$hook['pre_system'] = array('class' => 'Router_Hook',
'function' => 'dynamic_route',
'filename' => 'router_hook.php',
'filepath' => 'hooks',
'params' => array('server'=>$db['default']['hostname'],
'user'=>$db['default']['username'],
'password'=>$db['default']['password'],
'database'=>$db['default']['database'],
'driver'=>$db['default']['dbdriver']
)
);Note, I forget if DB_PREFIX which I have defined in the database.php file is part of CI or something I added...
Code: Select all
class Router_Hook
{
/**
* Loads routes from database.
*
* @access public
* @params array : hostname, username, password, database, db_prefix
* @return void
*/
function dynamic_route($params) {
global $cms_dyn_route; // This is initialized in the main index.php. It will be used in
if ($_SERVER['REQUEST_URI']=='/') {
$strRequestURI = '/index/';
}
else {
$strRequestURI = $_SERVER['REQUEST_URI'].'/';
}
if (preg_match('%^/(([a-z][-a-z0-9]*)/)?(([a-z][-a-z0-9]*)/)?([-a-z0-9]+_.*)?$%i', $strRequestURI, $aryPathParts)) {
$aryLookup = array();
if (!isset($aryPathParts[4]) || $aryPathParts[4]=='') { // Only singe page name
$aryLookup['section'] = 'pages';
$aryLookup['path'] = ($aryPathParts[2]!='') ? $aryPathParts[2] : 'index';
}
else {
$aryLookup['section'] = $aryPathParts[2];
$aryLookup['path'] = $aryPathParts[4];
}
if (isset($aryPathParts[5]) && trim($aryPathParts[5],'/')!='') {
$aryGets = explode('/',trim($aryPathParts[5],'/'));
foreach($aryGets as $strGet) {
if (preg_match('/^([a-z][-0-9a-z]*)_(.*)$/i', $strGet, $regs)) {
$_GET[$regs[1]] = (isset($regs[2])) ? $regs[2] : '';
}
}
}
switch($params['driver']) {
case 'mysql':
mysql_connect($params['server'],$params['user'],$params['password']);
mysql_select_db($params['database']);
$intLevels = 0;
$SQL = sprintf("SELECT * FROM `".DB_PREFIX."paths` WHERE (`section`='%s' OR `section`='paths') AND `path`='%s' AND `status`<>'disabled' LIMIT 1",
mysql_real_escape_string($aryLookup['section']),
mysql_real_escape_string($aryLookup['path'])
);
$rsLookup = mysql_query($SQL);
if ($rsLookup && mysql_num_rows($rsLookup)>0) {
$aryLookup = mysql_fetch_assoc($rsLookup);
mysql_free_result($rsLookup);
}
else {
$aryLookup = FALSE;
}
while ($aryLookup && ++$intLevels<6 && $aryLookup['section']=='paths') {
$rsLookup = mysql_query("SELECT * FROM `".DB_PREFIX."paths` WHERE `path_id`=".(int)$aryLookup['section_id']." AND `status`<>'disabled' LIMIT 1");
if ($rsLookup && mysql_num_rows($rsLookup)>0) {
$aryLookup = mysql_fetch_assoc($rsLookup);
mysql_free_result($rsLookup);
}
else {
$aryLookup = FALSE;
}
}
unset($rsLookup);
break;
default:
}
if ($aryLookup) {
if (!isset($_GET['action']) || !preg_match('/^[a-z]+$/',$_GET['action'])) {
$_GET['action'] = 'view'; // set default action
}
$cms_dyn_route = array('.*'=>$aryLookup['section'].'/'.$_GET['action'].'/'.$aryLookup['path'].'/'.$aryLookup['section_id'].'/'.$intLevels);
}
unset($aryLookup);
}
}
}there really is no need for the foreach to loop, there will only be one, I just used it to easily get the key/value pair
Code: Select all
global $cms_dyn_route;
if (!is_null($cms_dyn_route) && is_array($cms_dyn_route)) {
foreach($cms_dyn_route as $route_key=>$dynamic_route) {
$route[$route_key] = $dynamic_route;
}
}Code: Select all
$route['.*'] = 'pages/view/test/72/1';Code: Select all
$route['.*'] = 'pages/edit/test/72/2';Code: Select all
function view($strPath='index',$intPageID=1,$intLevels=1) {
// Load Up Page Data
$SQL = 'SELECT p.*, t.* FROM '.DB_PREFIX.'pages AS p LEFT JOIN '.DB_PREFIX.'templates AS t on p.template_id=t.template_id WHERE page_id = '.(int)$intPageID.' LIMIT 1';
$qPages = $this->db->query($SQL);
if ($qPages->num_rows() < 1) {
show_404($strPath);
return;
}
$aryPage = $qPages->row_array();
$qPages->free_result();
unset($qPages);
$aryData = array();
$aryData['strCanonical'] = ($intLevels>1) ? '<link rel="canonical" href="http://'.$_SERVER['HTTP_HOST'].'/'.$strPath.'" />' : NULL;
... Your other assignments from the database ...
-Greg