Software and Other Mysteries

On code and productivity with a dash of unicorn dust.

Fallback and Relax

I recently finished a project for a client in CodeIgniter where a large part of  the site was made up of static pages. The obvious approach for this is  to create controllers and methods to load these views, meaning that the method team() in controller Football would load the view views/football/team.php and be done with it.

This, no offense, seemed stupid, so I decided to create a fallback controller that would have the sole task of dealing with unmatched requests. In other words, if the current URL can’t be matched to a controller method,  the request will be dispatched to the specified controller. In the example of the client site, this would then load the view who’s folder structure and filename matched the URL (see Football example above), but it could pretty much be tasked to deal with anything from error logging to paying your taxes.

MY_Router.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');

class MY_Router extends CI_Router {

    function MY_Router() {
        parent::CI_Router();

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

  /**
  * Validates the supplied segments.  Attempts to determine the path to
  * the controller.
  *
  * @access  private
  * @param   array
  * @return  array
  */
  function _validate_request($segments)
  {
      // Does the requested controller exist in the root folder?
      if (file_exists(APPPATH.'controllers/'.$segments[0].EXT))
      {
          return $segments;
      }

      // Is the controller in a sub-folder?
      if (is_dir(APPPATH.'controllers/'.$segments[0]))
      {
          // Set the directory and remove it from the segment array
          $this->set_directory($segments[0]);
          $segments = array_slice($segments, 1);

          if (count($segments) > 0)
          {
              // Does the requested controller exist in the sub-folder?
              if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].EXT))
              {
                  show_404($this->fetch_directory().$segments[0]);
              }
          }
          else
          {
              $this->set_class($this->default_controller);
              $this->set_method('index');

              // Does the default controller exist in the sub-folder?
              if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.EXT))
              {
                  $this->directory = '';
                  return array();
              }

          }

          return $segments;
      }

      // Can't find the requested controller...
      if( isset($this->routes['fallback_controller']) AND $this->routes['fallback_controller']) {
          $uri = explode('/', strtolower($this->routes['fallback_controller']));
          $uri = array_merge($uri, $segments);
          $this->_set_request($uri);
          return $uri;
      } else {
          show_404($segments[0]);
      }
  }

}

Most of the code above is copied from the core Router class, the only thing changed is below the last comment. This method does exactly what the PHPdoc says - it validates a request and decides which controller should be responsible for it. Normally though, it will just display the 404 page if no matching controller is found, but this is where our magic happens. We just check if a fallback controller has been set and if so, load it with any arguments from the URI.

Easy enough right? So all that remains is setting the fallback, which is done in application/config/routes.php where we add the following line (with the choice of controller up to you of course):

1
$route['fallback_controller'] = "loader/load";

I’ll leave writing the actual controller to you guys though.

Comments