[ Team LiB ] Previous Section Next Section

The Controller Object

In this hour we focus on a particular implementation of the MVC pattern. The so-called front controller pattern places a single controller class at the heart of the system, where it fields requests, invokes commands, and dispatches views.

Although a controller object lies at the heart of our MVC system, it is also simple. Let's take a look at one. Listing 24.1 shows a stripped-down controller object, called Controller.php. We are going to maintain a crude package system based on the directories that enclose our scripts. Controller.php belongs in the controller package.

Listing 24.1 The Controller Class
 1: <?
 2: // controller/Controller.php
 3: // qframe license: http://resources.corrosive.co.uk/pkg/qframe/license.txt
 4: require_once 'controller/ApplicationResources.php';
 5: require_once 'controller/RequestHelper.php';
 6: require_once 'command/CommandFactory.php';
 7:
 8: class Controller {
 9:   private $applicationResources;
10:
11:   function __construct( ApplicationResources $res ) {
12:     $this->applicationResources = $res;
13:     $res->init();
14:   }
15:
16:   function handleRequest() {
17:     $command;
18:     $requestHelper = new RequestHelper( );
19:     $com_factory = $this->applicationResources->getCommandFactory();
20:     try {
21:       $command = $com_factory->getCommand( $requestHelper );
22:       $command->execute( $requestHelper );
23:       $this->applicationResources->getDispatcher()
24:           ->dispatch( $requestHelper );
25:     } catch ( Exception $e ) {
26:       throw $e;
27:     }
28:   }
29: }
30: ?>

Similar to the main() function in a C program, the Controller class sits above the action, commanding in broad sweeps and letting lesser objects busy themselves in pursuit of detail. It is invoked by a simple index.php file that instantiates a Controller object and calls handleRequest():


try {
   $controller = new Controller( new ApplicationResourcesImpl () );
   $controller->handleRequest();
} catch ( Exception $e ) {
  print "\n\nException reported: <br />\n<pre>\n";
  print_r( $e );
  print "\n</pre>\n\n";
}

graphics/bytheway_icon.gif

Because the code in this hour is derived from a project that programmers at Corrosive (my employer) have been working on, the code is itself open source.

Every page shown contains a link to a license notice at http://resources.corrosive.co.uk/pkg/qframe/license.txt.

You can also download the full code listing at http://resources.corrosive.co.uk/pkg/qframe/qframe.tar.gz even though the code is not designed for full deployment.


I said that Listing 24.1 had been stripped down. So, what's missing? The comments have been removed. Library code usually has comments for every class function and property. In many libraries, comments take as much as 30% of the total source code.

Notice also that the catch clause on line 25 does nothing but rethrow the exception it catches. Letting all exceptions that are generated by our system bubble right to the surface during development is useful. We don't want bugs hidden behind a graceful failure, which is why we use print_r() in our index.php script to display any exception generated. We could have omitted the try/catch clauses completely to achieve the same effect because uncaught exceptions are automatically rethrown if they are not caught. However, we leave the catch clause in place as a reminder that we want to implement more error handling.

So, what is going on in our Controller class? On line 11 our constructor requires an ApplicationResources object. We use a subclass of ApplicationResources to initialize the system. It handles details such as setting up the username and password for database access, as well as configuring a class that handles view dispatch. The ApplicationResources class itself is declared abstract. The only thing the Controller class knows about is that it is guaranteed to have an init() method, which kicks off configuration, and two methods for acquiring useful objects. These are the getDispatcher() method, which returns a Dispatcher object, and the getCommandFactory() method, which returns a CommandFactory object. Devoid of comments, the ApplicationResources class looks like this:


abstract class ApplicationResources {
  public abstract function init();
  public abstract function getDispatcher();
  public abstract function getCommandFactory();
}

We will look at our application-specific implementation later in the hour.

The Controller::handleRequest() method deals with the real business of the script. First, we instantiate a RequestHelper object on line 18. RequestHelper acts as a context for the work that is done in the controller tier and caches the contents of the $_REQUEST array, so request parameters can be overridden in tests or in a non-Web context. Commands use RequestHelper to register their statuses and save information for use by views. The dispatcher logic uses the record held by the RequestHelper object to determine which view to serve. We will look at the RequestHelper in more detail shortly.

We get an instance of a CommandFactory object from the ApplicationResources object on line 21. A CommandFactory is responsible for examining a client's request and finding a Command to execute in response. If the CommandFactory fails to generate a Command object, it throws an exception and flow moves to our catch block before we attempt to work with the return value of the getCommand() method on line 22. Assuming that we are furnished with a Command object, we call execute(). Command::execute() requires a RequestHelper object. Command objects leave a record of their names and return values with the RequestHelper; this information is used by a Dispatcher object on line 24 to decide which page to serve.

If that summary leaves you confused, don't worry! We are going to discuss all these classes in this chapter. If you get lost at any point, remember that the key to it all lies with the Controller object. You should be able to remind yourself where a class fits at any time by returning to this example.

Let's get started with the RequestHelper class.

    [ Team LiB ] Previous Section Next Section