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