Donations help me to keep this site running and to pay for further development and hosting. If you have found something useful please consider offering a token donation of your choice. I am very grateful. Thanks!

My Songs


SEO friendly urls in codeigniter - the challenge

by James Stoddern - Posted on 14-04-2013

Codeigniter is generally quite good at providing friendly urls, mainly due to its url segment approach. Instead of the traditional querystring approach where paramaters are passed as follows:


codeigniter uses segments instead.


So the segmented approach is much friendlier but still not perfect. What I want to achieve is a much more descriptive url such as "view-my-account.html". In order to this I decided to generate a slug for each post as I create or edit them in the cms. The slug is a friendly version of the blog post title as can be seen below. This is stored in a column called "slug" in the database for use later.

My intention is to have my controller perform a database or file-cache lookup for the slug, and then route it to the correct controller.


One of the first challenges is to tame the Codeigniter routing system, which allows urls to be directed to the correct controllers and methods. Routes can be managed inside of "/application/config/routes.php". In order to dynamically route the first step is to add some extra code in the routes.php to handle dynamic routes:

// includethe dynamic routes from the cache file

We also need some way of actually generating these routes, so what I decided to do was generated them when new posts are created, or existing posts are edited. After the main post has been written to the database, a new record is created (edited if it already exists of course) that contains the friendly url, the Slug, and the route that it should be directed to. In my blog this model is quite straight forward and comprises of the segments "blog/category/postid". So in my add/edit posts I have some code like this

//update the record for the dynamic route
$dynamic_route = "blog/index/" . $category . "/false/" . (int)$id;
$route = array(
    'post_id' => $id,
    'route' => $dynamic_route,
    'slug' => $slug

$result = mysql_query("SELECT * FROM routes WHERE post_id='{$id}'");
$num_rows = mysql_num_rows($result);

if($num_rows > 0) {
    //update the record
    $this->db->where('post_id', $id);
    $this->db->update('routes', $route); 
} else {
    //insert a new record
    $this->db->insert('routes', $route);

Now that the route has been generated we have to make it accessable to Codeigniter somehow. To save on database queries I have decided to write all of the routes for the site into a file cache - this lives inside "application/cache/routes.php" and effectively contains an array of all the routes for the site. I did this with a new model called "Router_m" which has a method called "cache_routes()" which looks as follows:

class Router_m extends CI_Model {

    function __construct() {

    //grab all of the routes from the database, and cache to a file
    public function cache_routes()
        $query = $this->db->get("routes");
        foreach ($query->result() as $row)
            $data[] = '$route["' . $row->slug . '"] = "' . $row->route . '";';
            $output = "load->helper('file');
            write_file(APPPATH . "cache/routes.php", $output);

This cache file is loaded by "config/routes.php" after the static routes have been defined. The file looks like this:

$route["new-single-whore-left-me.html"] = "blog/index/musician/false/27";
$route["content-management-system.html"] = "blog/index/blog/false/3";
$route["inline-ajax-image-uploader-for-tinymce.html"] = "blog/index/developer/false/45";
$route["jumping-on-the-bandwagon-youtube-videos.html"] = "blog/index/blog/false/41";
$route["test-post.html"] = "blog/index/blog/false/46";
$route["seo-friendly-urls-in-codeigniter-the-challenge.html"] = "blog/index/developer/false/44";
$route["big-brother-is-watching-google-analytics.html"] = "blog/index/blog/false/42";
$route["new-image-uploader-plupload.html"] = "blog/index/developer/false/43";
$route["sublime-text-2-video-intro.html"] = "blog/index/developer/false/40";
$route["multiband-compression.html"] = "blog/index/musician/false/39";
$route["store-your-snippets-with-github-gist.html"] = "blog/index/developer/false/38";
$route["on-fire-with-sparks.html"] = "blog/index/developer/false/37";
$route["sublime-coding.html"] = "blog/index/developer/false/35";
$route["my-new-guitar.html"] = "blog/index/musician/false/36";
$route["a-moment-of-clarity-literally.html"] = "blog/index/musician/false/33";
$route["song-writing-basics.html"] = "blog/index/musician/false/15";
$route["codeigniter-language-packs.html"] = "blog/index/developer/false/29";
$route["are-you-listening-to-your-speakers-or-your-room.html"] = "blog/index/musician/false/34";
$route["the-importance-of-gain-staging.html"] = "blog/index/musician/false/32";
$route["the-studio.html"] = "blog/index/musician/false/30";
$route["the-subjective-part-of-mixing-your-own-vocals.html"] = "blog/index/musician/false/28";
$route["implementing-tinymce-as-your-rich-text-editor.html"] = "blog/index/developer/false/2";
$route["welcome-to-jamesstoddernnet.html"] = "blog/index/home/false/26";
$route["new-single-out-now-smile.html"] = "blog/index/musician/false/14";

Of course, to make use of all this routing, I have had to change the links on the site, so that in the "recent posts" panel for example, instead of building a traditional codeigniter controller/method style link,  the slus can be used directly. As soon as you navigate to the slug, its correct location is loaded from the routes cache. Of course the user doesn't know this and just sees the friendly title. I have also got the page title to be set now from the post's title as well to make everything even more friendly.

blog comments powered by Disqus