Chapter 7. Zend_Controller
Prev |  | Next

## Chapter 7. Zend_Controller

## 7.1. Overview

### 7.1.1. Introduction

`Zend_Controller` provides the foundation for building a website based on the Model-View-Controller (MVC) pattern.

The `Zend_Controller` system is designed to be lightweight, modular, and extensible. It is a minimalist design to permit flexibility and some freedom to users while providing enough structure so that systems built around `Zend_Controller` share some common conventions and similar code layout.

The `Zend_Controller` workflow is implemented by several components. While it is not necessary to completely understand the underpinnings of all of these components to use the system, having a working knowledge of the process is helpful.

- `Zend_Controller_Front` orchestrates the entire workflow of the `Zend_Controller` system. It is an interpretation of the FrontController pattern. `Zend_Controller_Front` processes all requests received by the server and is ultimately responsible for delegating requests to ActionControllers (`Zend_Controller_Action`).

- `Zend_Controller_Request_Abstract` represents the request environment and provides methods for setting and retrieving the controller and action names and any request parameters. Additionally it keeps track of whether or not the action it contains has been dispatched by `Zend_Controller_Dispatcher`. Extensions to the abstract request object can be used to encapsulate the entire request environment, allowing routers to pull information from the request environment in order to set the controller and action names.

  By default, `Zend_Controller_Request_Http` is used, which provides access to the entire HTTP request environment.

- `Zend_Controller_Router_Interface` is used to define routers. Routing is the process of examining the request environment to determine which controller, and action of that controller, should receive the request. This controller, action, and optional parameters are then set in the request object to be processed by `Zend_Controller_Dispatcher_Standard`. Routing occurs only once: when the request is initially received and before the first controller is dispatched.

  The default router is `Zend_Controller_Router_Rewrite`.

  The default router, `Zend_Controller_Router_Rewrite`, takes a URI endpoint as specified in `Zend_Controller_Request_Http` and decomposes it into a controller, action, and parameters based on the path information in the url. As an example, the URL `http://localhost/foo/bar/key/value` would be decoded to use the `foo` controller, `bar` action, and specify a parameter `key` with a value of `value`.

  `Zend_Controller_Router_Rewrite` can also be used to match arbitrary paths; see Section 7.4.3, “Zend_Controller_Router_Rewrite” for more information.

- `Zend_Controller_Dispatcher_Interface` is used to define dispatchers. Dispatching is the process of pulling the controller and action from the request object and mapping them to a controller file/class and action method in the controller class. If the controller or action do not exist, it handles determining default controllers and actions to dispatch.

  The actual dispatching process consists of instantiating the controller class and calling the action method in that class. Unlike routing, which occurs only once, dispatching occurs in a loop. If the request object's dispatched status is reset at any point, the loop will be repeated, calling whatever action is currently set in the request object. The first time the loop finishes with the request object's dispatched status set (boolean true), it will finish processing.

  The default dispatcher is `Zend_Controller_Dispatcher_Standard`. It defines controllers as CamelCasedClasses ending in the word Controller, and action methods as camelCasedMethods ending in the word Action: `SomeFooController::barAction`. In this case, the controller would be referred to as `somefoo` and the action as `bar`.

- `Zend_Controller_Action` is the base controller component. Each controller is a single class that extends the `Zend_Controller_Action class`, and this class has action methods.

- `Zend_Controller_Response_Abstract` defines a base response class used to collect and return responses from the action controllers. It collects both headers and body content, and, because it implements `__toString()`, can be directly echoed in order to send all headers and content at once.

  The default response class is `Zend_Controller_Response_Http`, which is suitable for use in an HTTP environment.

The workflow of `Zend_Controller` is relatively simple. A request is received by `Zend_Controller_Front`, which in turn calls `Zend_Controller_Router_Rewrite` to determine which controller (and action in that controller) to dispatch. `Zend_Controller_Router_Rewrite` decomposes the URI in order to set the controller and action names in the request. `Zend_Controller_Front` then enters a dispatch loop. It calls `Zend_Controller_Dispatcher_Standard`, passing it the request, to dispatch to the controller and action specified in the request (or use defaults). After the controller has finished, control returns to `Zend_Controller_Front`. If the controller has indicated that another controller should be dispatched by resetting the dispatched status of the request, the loop continues and another dispatch is performed. Otherwise, the process ends.

### 7.1.2. Request Object

The request object is a simple value object that is passed between `Zend_Controller_Front` and the router, dispatcher, and controller classes. It packages a definition of a controller, an action, and parameters to be passed to the action, as well as the rest of the request environment, be it HTTP, the CLI, or PHP-GTK.

- The controller name is accessed by `getControllerName()` and `setControllerName()`.

- The name of the action to call within that controller is accessed by `getActionName()` and `setActionName()`.

- Parameters to be passed to that action are an associative array of key/value pairs that are accessed by `getParams()` and `setParams()`, or individually by `getParam()` and `setParam()`.

Based on the type of request, there may be more methods available. The default request used, `Zend_Controller_Request_Http`, for instance, has methods for retrieving the request URI, path information, $_GET and $_POST parameters, etc.

The request object is passed to the front controller, or if none is provided, instantiated at the beginning of the dispatch process, before routing occurs. It is passed through to every object in the dispatch chain.

Additionally, the request object is particularly useful in testing. The developer may craft the request environment, including controller, action, parameters, URI, etc, and pass the request object to the front controller to test application flow. When paired with the response object, elaborate and precise unit testing of MVC applications becomes possible.

### 7.1.3. Route Process

Before your first controller can be built, you need to understand how the routing process works as it is implemented in `Zend_Controller_Router_Rewrite`. Remember that the workflow is divided into routing, which occurs only once, and dispatching, which occurs thereafter in a loop.

`Zend_Controller_Front` calls `Zend_Controller_Router_Rewrite` (or another registered router) to map a URI to a controller -- and an action within that controller. `Zend_Controller_Router_Rewrite` retrieves the URI from the request object and passes it to the Route objects in its chain; by default, it uses `Zend_Controller_Router_Route_Module` to match incoming URLs. The route object then decomposes the URL to determine the controller, action, and any other URL parameters passed in the path; the router itself then sets these in the request object.

`Zend_Controller_Router_Route_Module` uses a very simple mapping to determine the name of the controller and the name of the action within that controller:

    http://framework.zend.com/controller/action/

Notice above that the first segment is always the name of the controller and the second segment is always the name of the action.

Optionally, parameters may be defined in the URI that will be passed to the controller. These take the form of key/value pairs:

    http://framework.zend.com/controller/action/key1/value1/

If either the controller or action are missing from the URI path, `Zend_Controller_Dispatcher_Standard` will try and grab the value from the request object's parameters, and, if not found, use default values. In both cases, the default values are "`index`". These examples illustrate:

    http://framework.zend.com/roadmap/future/
    Controller: roadmap
    Action    : future

    http://framework.zend.com/roadmap/
    Controller: roadmap
    Action    : index

    http://framework.zend.com/
    Controller: index
    Action    : index

| Flexibility
If you want more flexible capabilities, you may want to check out Section 7.4.3, “Zend_Controller_Router_Rewrite”.

The controller name, the action name within that controller, and any optional parameters are set in the request object. When `Zend_Controller_Front` enters the dispatch loop, the request object will be passed to `Zend_Controller_Dispatcher_Standard`.

### 7.1.4. Dispatch Process

Dispatching is the process of taking the request object, `Zend_Controller_Request_Abstract`, extracting the controller name, action name, and optional parameters contained in it, and then instantiating a controller and calling an action of that controller. If no controller or action are found, it will use default values for them. `Zend_Controller_Dispatcher_Standard` specifies `index` for each of these defaults, but allows the developer to change them using the `setDefaultController()` and `setDefaultAction()` methods.

Dispatching happens in a loop in the front controller. Before dispatching occurs, the front controller routes the request to find user specified values for the controller, action, and optional parameters. It then enters a dispatch loop, dispatching the request.

At the beginning of each iteration, it sets a flag in the request object indicating that the action has been dispatched. If an action or pre/postDispatch plugin resets that flag, the dispatch loop will continue and attempt to dispatch the request again. By changing the controller and/or action in the request and resetting the dispatched flag, the developer may define a chain of requests to perform.

The action controller method that controlls such dispatching is `_forward()`; call this method from any of the pre/postDispatch() or action methods, providing a controller, action, and optionally any additional parameters you may wish to send to the new action:

    public function myAction()
    {
        // do some processing...
        // forward to another action, FooController::barAction(), in the current
        // module:
        $this->_forward('bar', 'foo', null, array('baz' => 'bogus'));
    }

### 7.1.5. Response Object

The response object is the logical pair to the request object. Its purpose is to collate content and/or headers so that they may be returned en masse. Additionally, the front controller will pass any caught exceptions to the response object, allowing the developer to gracefully handle exceptions. This functionality may be overridden by setting `Zend_Controller_Front::throwExceptions(true)`:

    $front->throwExceptions(true);

To send the response output, including headers, use `sendOutput()`.

    $response->sendOutput();

Developers should make use of the response object in their action controllers. Instead of directly rendering output and sending headers, push them to the response object:

    // Within an action controller action:
    // Set a header
    $this->getResponse()
        ->setHeader('Content-Type', 'text/html')
        ->appendBody($content);

By doing this, all headers get sent at once, just prior to displaying the content.

Should an exception occur in an application, check the response object's `isException()` flag, and retrieve the exception using `getException()`. Additionally, one may create custom response objects that redirect to error pages, log exception messages, do pretty formatting of exception messages (for development environments), etc.

You may retrieve the response object following the front controller dispatch(), or request the front controller to return the response object instead of rendering output.

    // retrieve post-dispatch:
    $front->dispatch();
    $response = $front->getResponse();
    if ($response->isException()) {
        // log, mail, etc...
    }

    // Or, have the front controller dispatch() process return it
    $front->returnResponse(true);
    $response = $front->dispatch();

    // do some processing...

    // finally, echo the response
    $response->sendResponse();

By default, exception messages are not displayed. This behaviour may be overridden by calling `renderExceptions()`, or enabling the front controller to throwExceptions(), as shown above:

    $response->renderExceptions(true);
    $front->dispatch($request, $response);

    // or:
    $front->returnResponse(true);
    $response = $front->dispatch();
    $response->renderExceptions();
    $response->sendOutput();

    // or:
    $front->throwExceptions(true);
    $front->dispatch();

Prev |  | Next
6.4. Configuring Zend_Console_Getopt | Home | 7.2. Getting Started