I promised myself I won’t do anymore tutorials regarding CodeIgniter until a new version is about to appear, but I can’t help myself. And this subject seems more than appropriate for a new tutorial.
Let us start with the beginning. In routes.php (application/config/routes.php), we have a configuration parameter called “translate_uri_dashes“. If we set this parameter to TRUE, the CodeIgniter framework will translate the dashes (“-“) in our urls into underscores (“_”) when calling a controller or a method. This, of course is what we want in order to have “SEO friendly” URLs (although I think it’s strange that up until this moment, search engines can’t interpret an url).
That means that when the CodeIgniter sees an address like http://yourdomain.com/test-controller/a-url-method/ will know to call the Test_controller method and the a_url_method() method.
But, hey!… This won’t remove the possibility of accessing the same controller and method by using underscores in the url. So, that means that you get same content at http://yourdomain.com/test-controller/a-url-method/ and at http://yourdomain.com/test_controller/a_url_method/ and that is a BIIIG “NO-NO” from our beloved search engines. Why? The famous “DUPLICATE CONTENT“.
Then how do save ourselves from this SEO Armageddon? Well… Let us take a look at the Router.php inside the system/core. Looking at that we will see that the method that deals with “translating” the URL into a controller and its method is _set_request():
// --------------------------------------------------------------------
/**
* Set request route
*
* Takes an array of URI segments as input and sets the class/method
* to be called.
*
* @used-by CI_Router::_parse_routes()
* @param array $segments URI segments
* @return void
*/
protected function _set_request($segments = array())
{
$segments = $this->_validate_request($segments);
// If we don't have any segments left - try the default controller;
// WARNING: Directories get shifted out of the segments array!
if (empty($segments))
{
$this->_set_default_controller();
return;
}
if ($this->translate_uri_dashes === TRUE)
{
$segments[0] = str_replace('-', '_', $segments[0]);
if (isset($segments[1]))
{
$segments[1] = str_replace('-', '_', $segments[1]);
}
}
$this->set_class($segments[0]);
if (isset($segments[1]))
{
$this->set_method($segments[1]);
}
else
{
$segments[1] = 'index';
}
array_unshift($segments, NULL);
unset($segments[0]);
$this->uri->rsegments = $segments;
}
So, this is the culprit… But what now? Should we simply modify it? No… NEVER MODIFY THE SYSTEM FILES IN CODEIGNITER!
Let’s go into our application/core and create a file named MY_Router.php in it. As you may, or may not know, CodeIgniter automatically extends anything that starts with MY_… This class will simply extend the core Router.php and we will only modify the method in question:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class MY_Router extends CI_Router {
function __construct()
{
parent::__construct();
}
// --------------------------------------------------------------------
/**
* Set request route
*
* Takes an array of URI segments as input and sets the class/method
* to be called.
*
* @used-by CI_Router::_parse_routes()
* @param array $segments URI segments
* @return void
*/
protected function _set_request($segments = array())
{
$segments = $this->_validate_request($segments);
// If we don't have any segments left - try the default controller;
// WARNING: Directories get shifted out of the segments array!
if (empty($segments))
{
$this->_set_default_controller();
return;
}
if ($this->translate_uri_dashes === TRUE)
{
if(strpos($segments[0],'_')!==FALSE)
{
$wrong_controller = TRUE;
}
$segments[0] = str_replace('-', '_', $segments[0]);
if(isset($segments[1]) && strpos($segments[1],'_')!==FALSE)
{
$wrong_method = TRUE;
}
if (isset($segments[1]))
{
$segments[1] = str_replace('-', '_', $segments[1]);
}
}
if(isset($wrong_controller) || isset($wrong_method))
{
$url_segments = $segments;
$url = $this->config->config['base_url'];
$controller = array_shift($url_segments);
$url .= str_replace('_','-',$controller);
if(isset($url_segments) && sizeof($url_segments)>0) {
$method = array_shift($url_segments);
$url .= '/' . str_replace('_', '-', $method);
}
if(!empty($url_segments))
{
$url .= '/'.implode('/',$url_segments);
}
header('Location: '.$url, TRUE, 301);
}
$this->set_class($segments[0]);
if (isset($segments[1]))
{
$this->set_method($segments[1]);
}
else
{
$segments[1] = 'index';
}
array_unshift($segments, NULL);
unset($segments[0]);
$this->uri->rsegments = $segments;
}
}
As you can see in lines 35-38 and 41-44 we look to see if what we get from URL contains any underscores (“_”). If it does, then that means that the URL is no good and we need to do a redirect to the url that only contains dashes “-“. Now in lines 51-57 we simply do a redirect with a 301 server code (moved permanently), if either the controller’ or the method’s URL contain underscore “_”.
And that’s it. Of course, this is not an universal solution and is prone to errors if you have a more “exotic” setup of urls. Do you have another solution? I would be more than happy to put it here. Can someone, please, buy me a coffee (hint-hint)?
Subject: why develop a web app with Codeigniter in 2016?
That is a really good subject
How to remove index.php from URL in codeignitor?
http://avenir.ro/codeigniter-tutorials/removing-the-index-php-from-the-url-and-allow-the-use-of-search-engine-friendly-urls/ 🙂
You are a Codeignitor expert and using WordPress as your website. Why?
I am not a “Codeignitor” expert. Anyway… why use my time creating a CodeIgniter application when I can use it to write tutorials (not only CodeIgniter tutorials…)?
Thanks – was searching Google for this exact question