The Zend_Controller
system was built with extensibility in mind, either
by subclassing the existing classes or writing new classes that implement
the interfaces Zend_Controller_Router_Interface
and Zend_Controller_Dispatcher_Interface
.
Possible reasons for implementing a new router or dispatcher might be:
The existing URI routing system is not suitable in some way, such as integrating into an existing website that uses its own conventions for routing that do not mesh with the routing mechanism provided with the Zend Framework.
You need to implement routing for something completely different. The Zend_Controller_Router
class only deals with URIs. It's possible and likely that you might want to use the MVC
pattern for developing another type of program, such as a console application. In the case of
a console application, a custom router could process command line arguments to determine
the route.
The mechanism provided by Zend_Controller_Dispatcher
is not suitable. The default configuration
assumes a convention that controllers are classes and actions are methods of those classes.
However, there are many other strategies for doing this. One example would be where controllers
are directories and actions are files within those directories.
You wish to provide additional capabilities that will be inherited by all of your controllers.
For example, Zend_Controller_Action
does not integrate with Zend_View
by default.
However, you could extend your own controller to do this, and using it would not require modifying the
supplied Zend_Controller_Router
or Zend_Controller_Dispatcher
.
Please be careful when overriding significant parts of the system, particularly the dispatcher. One of
the advantages of Zend_Controller
is that it establishes common conventions for building applications. If
too much of this default behavior is changed, some of these advantages are lost. However, there are many
different needs and one solution can't fit them all, so the freedom is provided if needed.
When subclassing any of the Zend_Controller classes, you are strongly encouraged to follow these conventions for naming or storing files. Doing so will ensure that another programmer who is familiar with the Zend Framework will be able to understand your project easily.
Classes included with the Zend Framework follow a convention where every class is prefixed by "Zend_". This is the prefix. We recommend that you name all of your classes in the same way, e.g. if your company name is Widget, Inc., the prefix might be "Widget_".
The Zend_Controller
classes are stored in the library directory as follows:
/library /Zend /Controller Action.php Dispatcher.php Router.php
When subclassing Zend_Controller
classes, it is recommended that the new classes
be stored in an identical structure under your prefix. This will make them easy to find for
someone during that learning process of reviewing the code for your project.
For example, a project from Widget, Inc. which implements only a custom router might look like this:
/library /Zend /Widget /Controller Router.php README.txt
Notice in this example that the Widget/Controller/
directory mirrors the Zend/Controller/
directory wherever possible. In this case, it provides the class Widget_Controller_Router
,
which would be either a subclass or replacement for Zend_Controller_Router
implementing
Zend_Controller_Router_Interface
.
Also, notice in the example above that a README.txt
file has been placed in Widget/Controller/
.
Zend strongly encourages you to document your projects through supplying separate tests and
documentation for customers. However, we encourage you to also place a simple README.txt
file
right in the directory to briefly explain your changes and how they work.
The interface Zend_Controller_Router_Interface
provides a definition for only one method:
<?php /** * @param Zend_Controller_Dispatcher_Interface * @throws Zend_Controller_Router_Exception * @return Zend_Controller_Dispatcher_Token|boolean */ public function route(Zend_Controller_Dispatcher_Interface $dispatcher); ?>
Routing only occurs once: when the request is first received into the system. The purpose of the
router is to generate a Zend_Controller_Dispatch_Token
that specifies a controller and action of
that controller. This is then passed to the dispatcher. If it is not possible to map a route to
a dispatch token (nonsenical route) then a boolean, FALSE
, should be returned.
Some routers may process dynamic elements and need a way to determine if the dispatch token generated
is actually dispatchable before returning it. For this reason, the router receives the object handle
of the dispatcher as the sole argument to its route()
method. The dispatcher provides a method,
isDispatchable()
, for this testing.
Zend_Controller_Front
will first call the router to receive the first dispatch token, which it will
pass to the dispatcher. The dispatcher will dispatch the action (instantiate the controller, call
its action) and then return with either a boolean, FALSE, or another dispatch token.
Zend_Controller_Front
repeatedly calls the dispatcher until a dispatch token is not returned from
it. This is known as the dispatch loop. It allows for actions to be processed sequentially
until all work has been done.
The interface Zend_Controller_Dispatcher_Interface
provides definitions for two methods:
<?php /** * @param Zend_Controller_Dispatcher_Token $route * @return boolean */ public function isDispatchable(Zend_Controller_Dispatcher_Token $route); ?>
isDispatchable()
checks if a dispatch token is dispatchable. If it is, it returns TRUE
.
Otherwise, it returns FALSE
. The definition of what is dispatchable is left to the class
implementing the interface. In the case of the default implementation, Zend_Controller_Dispatcher
, it means
that the controller file exists, the class exists within the file, and the action method exists within the class.
<?php /** * @param Zend_Controller_Dispatcher_Token $route * @return Zend_Controller_Dispatcher_Token|boolean */ public function dispatch(Zend_Controller_Dispatcher_Token $route); ?>
dispatch()
is where the work gets done. This method must execute the action of the controller.
It must return either a dispatch token or a boolean, FALSE, to indicate that there is no more work to do.