Please consider donating: a local project in memory of my daughter

CodeIgniter

If you've been following me on Twitter, then you'll know I've been umm-ing and err-ing about whether or not to use CodeIgniter, the PHP framework.

It really boils down to this: I've built my own framework in PHP, which works the way I like - but I'm attracted to the code infrastructure support CodeIgniter (CI) can offer.

I thought I'd share the key lessons I learnt and show how I implemented the features I liked about my framework in to CI.

1. Bespoke URL Handling

The real biggie for me was how URLs were handled. I knew that the basic structure is:

[sitename]/[controller | blank]/[method]/[param1,etc]

e.g.

http://del.icio.us/user/remy.sharp

This would bring up my home page. However, I prefer URLs that don't include superfluous info, like the user part:

http://del.icio.us/remy.sharp

The way this method works is failing to match any other controller, then checking the database for a valid X type of object (in this case user).

To achieve this in CI you, ideally, need to handle the show_404 yourself. Since that function is sitting in the Common library, we can subclass the Router class instead.

class MY_Router extends CI_Router {
  /**
   * Constructor
   */    
  function MY_Router() {   
    parent::CI_Router(); 

    log_message('debug', "Custom Router Class Initialized");
  }

  /**
   * Replaces the default show_404 logic flow in _validate_request
   * and returns our custom controller
   */  
  function missing_controller($segments) {
    $this->set_class('missing'); // <-- this is our custom 404 handler
    $this->set_method('index');
    return array('missing'); // <-- and again
  }

  /**
   * Taken directly from Router.php - to overload the 404 method
   */ 
  function _validate_request($segments) {
    // ... original code, except show_404() is replaced with:
    return $this->missing_controller($segments);
    // ... continues with original code, replacing all instances of show_404
  }
}

You can download my copy of MY_Router.php and it should live in your application/libraries directory.

2. Common Headers and Footers

My PHP framework would load everything sequentially - the OO approach that CI has attracted me, but I liked having the same header and footer always loading, handing their own navigation selection and login logic with me having to specify it in each controller.

CI's basic approach, if you did indeed split out the header and footer would be this for a controller index method:

function index() {
  $this->load->view('header');
  $this->load->view('welcome');
  $this->load->view('footer');
}

On top if which, I also like my controllers to be reused for the Ajax logic (as per my Ajax pattern), so typically I would sniff for the Ajax header and automatically skip over the header and footer.

This too can be achieve in CI with some subclassing.

I've subclassed the Controller class and I use MY_Controller as the base class the app controllers extend, like this:

class Home extends MY_Controller {
  function Home() {
    parent::MY_Controller();
    $this->page_title = 'Home!';
  }

  function index() {
    $data = array();

    if ($this->ajax) {
      if ($this->input->post('date')) {
        $this->load->model('Events');
        $data['json'] = (object)$this->Events->check($this->input->post('date'));
      }

      // the MY_Controller knows not to print the header + footer
      $this->view('json', $data);
    } else {
      $this->view('home');
    }
  }
}

MY_Controller handles the Ajax aspect of the header and footer. It handles whether to show a different header if the user is logged in. It handles custom page titles, and it also gives me debug information on the page if I request it (which I'll explain next).

You can download my copy of MY_Controller.php, and again, it should live in your application/libraries directory.

The only subtle thing I've done differently to CI (and I'm sure if more experienced CI coder see this they may have an opinion that it's wrong), is that I've defined the privately scoped methods using protected. I'm not a fan of the _private approach, and protected means the function can't be called from the URL

† If this is completely wrong - please let me know!

3. Debugging Queries

Something that I've always built in to my web projects, is the ability to quickly pull up all the queries that ran on the page. This is useful for debugging problems, checking data is being returned, optimisation, etc.

I couldn't easily see in the (rather excellent) documentation to CI how I could hook a print function after each query had ran...but a few minutes after giving up my search, I'm dipping through the DB code and found that it's take log of all the queries run.

So now I've got the information, and using MY_Controller.php, I can set my browser user-agent string to contain a trigger variable (since I can continue to browser the site normally and it will remain in debug mode), and it will output all the SQL run on the page via my debug_sql.php view, inline with the content.

4. Next Up: Environment Aware Configs

My next job is to handle different environments for the config and database variables. I'm sure this can be easily done, but I like to use a trigger file (so I'll do touch htdocs/live) that can be tested for that drives which config is loaded. If this already exists and someone knows of it - please let me know!

7 Responses to “CodeIgniter”

  1. Hey Remy.

    Nice post, thoughtful, well written.

    Have you seen the profiler yet? It'll do what you're looking for in #3. Also

    $this->db->last_query();

    Is helpful for the... um... last query ;)

    Different environmental configs is a bit trickier, but the database file supports multiple environments easily. If you hit the database config page you'll see examples us setting up a dev and live db.

  2. [...] Just another nice tutorial [...]

  3. [...] A good idea to load header and footer not in every controller (http://remysharp.com/2008/03/25/codeigniter/) [...]

  4. Any chance of posting a link to the missing.php-controller file as well?
    Having a ball with your MY_Router already, but I am quite curious as to how you'll handle things in there...

  5. Good tutorial Remy, but I have a doubt/problem.
    I called my default_controller as 'homepage' and I want remove it from the url. But the site only work with '/index.php/homepage/'. If I remove this it shows me an 500 Internal Server Error.
    My .htacess:
    Options +FollowSymlinks
    RewriteEngine On
    RewriteCond $1 !^(index\.php|images|css|js|robots\.txt|favicon\.ico)
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ /novo-site/index.php/$1 [L]

    How can I remove the '/index.php/homepage/' from the url? My site is http://www.monicafittipaldi.com.br/novo-site/

    Thanks.

  6. It seems you got it done Hugo Fittipaldi. I guess you have looked to your config.php

    By the way remy sharp did you manage to load different database config depending on the environment?

    Myself I made a little script that look to different config file depending on the var $_SERVER['SERVER_NAME']

  7. Jeroen van der Tuin April 27th, 2010 at 9:28 pm

    Thanks for MY_Router. I have just set it up in CI 1.7.2. and it works.

    I've pasted code of a plugin to create slugs here: http://gist.github.com/381274

Leave a Reply
Not required

CODE: Please escape code and wrap in <pre><code>, doing so will automatically syntax highlight