Table of Contents
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 the
Rewrite Router documentation 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.
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.
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 the Rewrite Router documentation. |
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
.
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')); }
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();