1 <?xml version="1.0" encoding="UTF-8"?>
3 <sect1 id="zend.controller.action">
4 <title>Action Controllers</title>
6 <sect2 id="zend.controller.action.introduction">
7 <title>Introduction</title>
10 <classname>Zend_Controller_Action</classname> is an abstract class you may use
11 for implementing Action Controllers for use with the Front
12 Controller when building a website based on the
13 Model-View-Controller (<acronym>MVC</acronym>) pattern.
17 To use <classname>Zend_Controller_Action</classname>, you will need to
18 subclass it in your actual action controller classes (or subclass it
19 to create your own base class for action controllers). The most
20 basic operation is to subclass it, and create action methods that
21 correspond to the various actions you wish the controller to handle
22 for your site. <classname>Zend_Controller</classname>'s routing and dispatch handling
23 will autodiscover any methods ending in 'Action' in your class as
24 potential controller actions.
28 For example, let's say your class is defined as follows:
31 <programlisting language="php"><![CDATA[
32 class FooController extends Zend_Controller_Action
34 public function barAction()
39 public function bazAction()
47 The above <emphasis>FooController</emphasis> class (controller
48 <emphasis>foo</emphasis>) defines two actions, <emphasis>bar</emphasis> and
49 <emphasis>baz</emphasis>.
53 There's much more that can be accomplished than this, such as custom
54 initialization actions, default actions to call should no action (or
55 an invalid action) be specified, pre- and post-dispatch hooks, and a
56 variety of helper methods. This chapter serves as an overview of the
57 action controller functionality
61 <title>Default Behaviour</title>
64 By default, the <link linkend="zend.controller.front">front
65 controller</link> enables the <link
66 linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer</link>
67 action helper. This helper takes care of injecting the view
68 object into the controller, as well as automatically rendering
69 views. You may disable it within your action controller via one
70 of the following methods:
73 <programlisting language="php"><![CDATA[
74 class FooController extends Zend_Controller_Action
76 public function init()
78 // Local to this controller only; affects all actions,
80 $this->_helper->viewRenderer->setNoRender(true);
83 $this->_helper->removeHelper('viewRenderer');
85 // Also globally, but would need to be in conjunction with the
86 // local version in order to propagate for this controller:
87 Zend_Controller_Front::getInstance()
88 ->setParam('noViewRenderer', true);
94 <methodname>initView()</methodname>, <methodname>getViewScript()</methodname>,
95 <methodname>render()</methodname>, and <methodname>renderScript()</methodname> each
96 proxy to the <emphasis>ViewRenderer</emphasis> unless the helper is not
97 in the helper broker or the <emphasis>noViewRenderer</emphasis> flag has
102 You can also simply disable rendering for an individual view by
103 setting the <emphasis>ViewRenderer</emphasis>'s <emphasis>noRender</emphasis>
107 <programlisting language="php"><![CDATA[
108 class FooController extends Zend_Controller_Action
110 public function barAction()
112 // disable autorendering for this action only:
113 $this->_helper->viewRenderer->setNoRender();
119 The primary reasons to disable the <emphasis>ViewRenderer</emphasis> are
120 if you simply do not need a view object or if you are not
121 rendering via view scripts (for instance, when using an action
122 controller to serve web service protocols such as <acronym>SOAP</acronym>,
123 <acronym>XML-RPC</acronym>, or <acronym>REST</acronym>). In most cases, you will
124 never need to globally disable the <emphasis>ViewRenderer</emphasis>, only
125 selectively within individual controllers or actions.
130 <sect2 id="zend.controller.action.initialization">
131 <title>Object Initialization</title>
134 While you can always override the action controller's constructor, we
135 do not recommend this. <methodname>Zend_Controller_Action::__construct()</methodname>
136 performs some important tasks, such as registering the request and
137 response objects, as well as any custom invocation arguments passed
138 in from the front controller. If you must override the constructor,
139 be sure to call <methodname>parent::__construct($request, $response,
140 $invokeArgs)</methodname>.
144 The more appropriate way to customize instantiation is to use the
145 <methodname>init()</methodname> method, which is called as the last task of
146 <methodname>__construct()</methodname>. For example, if you want to connect to
147 a database at instantiation:
150 <programlisting language="php"><![CDATA[
151 class FooController extends Zend_Controller_Action
153 public function init()
155 $this->db = Zend_Db::factory('Pdo_Mysql', array(
157 'username' => 'user',
158 'password' => 'XXXXXXX',
159 'dbname' => 'website'
166 <sect2 id="zend.controller.action.prepostdispatch">
167 <title>Pre- and Post-Dispatch Hooks</title>
170 <classname>Zend_Controller_Action</classname> specifies two methods that may
171 be called to bookend a requested action, <methodname>preDispatch()</methodname>
172 and <methodname>postDispatch()</methodname>. These can be useful in a variety of
173 ways: verifying authentication and <acronym>ACL</acronym>'s prior to running an action
174 (by calling <methodname>_forward()</methodname> in
175 <methodname>preDispatch()</methodname>, the action will be skipped), for instance, or
176 placing generated content in a sitewide template
177 (<methodname>postDispatch()</methodname>).
181 <title>Usage of init() vs. preDispatch()</title>
184 In the <link linkend="zend.controller.action.initialization">previous
185 section</link>, we introduced the <methodname>init()</methodname> method, and
186 in this section, the <methodname>preDispatch()</methodname> method. What is the
187 difference between them, and what actions would you take in each?
191 The <methodname>init()</methodname> method is primarily intended for extending the
192 constructor. Typically, your constructor should simply set object state, and not
193 perform much logic. This might include initializing resources used in the controller
194 (such as models, configuration objects, etc.), or assigning values retrieved from
195 the front controller, bootstrap, or a registry.
199 The <methodname>preDispatch()</methodname> method can also be used to set object
200 or environmental (e.g., view, action helper, etc.) state, but its primary purpose
201 is to make decisions about whether or not the requested action should be dispatched.
202 If not, you should then <methodname>_forward()</methodname> to another action, or
207 Note: <methodname>_forward()</methodname> actually will not work correctly when
208 executed from <methodname>init()</methodname>, which is a formalization of the
209 intentions of the two methods.
214 <sect2 id="zend.controller.action.accessors">
215 <title>Accessors</title>
218 A number of objects and variables are registered with the object,
219 and each has accessor methods.
225 <emphasis>Request Object</emphasis>: <methodname>getRequest()</methodname>
226 may be used to retrieve the request object used to call the action.
232 <emphasis>Response Object</emphasis>:
233 <methodname>getResponse()</methodname> may be used to retrieve the
234 response object aggregating the final response. Some typical
235 calls might look like:
238 <programlisting language="php"><![CDATA[
239 $this->getResponse()->setHeader('Content-Type', 'text/xml');
240 $this->getResponse()->appendBody($content);
246 <emphasis>Invocation Arguments</emphasis>: the front
247 controller may push parameters into the router, dispatcher,
248 and action controller. To retrieve these, use
249 <methodname>getInvokeArg($key)</methodname>; alternatively, fetch the
250 entire list using <methodname>getInvokeArgs()</methodname>.
256 <emphasis>Request parameters</emphasis>: The request object
257 aggregates request parameters, such as any <constant>_GET</constant> or
258 <constant>_POST</constant> parameters, or user parameters specified in the
259 <acronym>URL</acronym>'s path information. To retrieve these, use
260 <methodname>_getParam($key)</methodname> or
261 <methodname>_getAllParams()</methodname>. You may also set request
262 parameters using <methodname>_setParam()</methodname>; this is useful
263 when forwarding to additional actions.
267 To test whether or not a parameter exists (useful for
268 logical branching), use <methodname>_hasParam($key)</methodname>.
273 <methodname>_getParam()</methodname> may take an optional second
274 argument containing a default value to use if the
275 parameter is not set or is empty. Using it eliminates
276 the need to call <methodname>_hasParam()</methodname> prior to
280 <programlisting language="php"><![CDATA[
281 // Use default value of 1 if id is not set
282 $id = $this->_getParam('id', 1);
285 if ($this->_hasParam('id') {
286 $id = $this->_getParam('id');
296 <sect2 id="zend.controller.action.viewintegration">
297 <title>View Integration</title>
299 <note id="zend.controller.action.viewintegration.viewrenderer">
300 <title>Default View Integration is Via the ViewRenderer</title>
303 The content in this section is only valid when you have explicitly disabled the
304 <link linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer</link>.
305 Otherwise, you can safely skip over this section.
310 <classname>Zend_Controller_Action</classname> provides a rudimentary and
311 flexible mechanism for view integration. Two methods accomplish
312 this, <methodname>initView()</methodname> and <methodname>render()</methodname>; the
313 former method lazy-loads the <varname>$view</varname> public property, and the
314 latter renders a view based on the current requested action, using
315 the directory hierarchy to determine the script path.
318 <sect3 id="zend.controller.action.viewintegration.initview">
319 <title>View Initialization</title>
322 <methodname>initView()</methodname> initializes the view object.
323 <methodname>render()</methodname> calls <methodname>initView()</methodname> in
324 order to retrieve the view object, but it may be initialized at any time;
325 by default it populates the <varname>$view</varname> property with a
326 <classname>Zend_View</classname> object, but any class implementing
327 <classname>Zend_View_Interface</classname> may be used. If
328 <varname>$view</varname> is already initialized, it simply returns
333 The default implementation makes the following assumption of
334 the directory structure:
337 <programlisting language="php"><![CDATA[
350 In other words, view scripts are assumed to be in the
351 <filename>/views/scripts/</filename> subdirectory, and the
352 <filename>/views/</filename> subdirectory is assumed to contain sibling
353 functionality (helpers, filters). When determining the view
354 script name and path, the <filename>/views/scripts/</filename> directory
355 will be used as the base path, with directories named after the
356 individual controllers providing a hierarchy of view scripts.
360 <sect3 id="zend.controller.action.viewintegration.render">
361 <title>Rendering Views</title>
364 <methodname>render()</methodname> has the following signature:
367 <programlisting language="php"><![CDATA[
368 string render(string $action = null,
370 bool $noController = false);
374 <methodname>render()</methodname> renders a view script. If no arguments are
375 passed, it assumes that the script requested is
376 <filename>[controller]/[action].phtml</filename> (where
377 <filename>.phtml</filename> is the value of the <varname>$viewSuffix</varname>
378 property). Passing a value for <varname>$action</varname> will render
379 that template in the <filename>/[controller]/</filename> subdirectory. To
380 override using the <filename>/[controller]/</filename> subdirectory, pass
381 a <constant>TRUE</constant> value for <varname>$noController</varname>. Finally,
382 templates are rendered into the response object; if you wish to render to
384 linkend="zend.controller.response.namedsegments">named
385 segment</link> in the response object, pass a value to
386 <varname>$name</varname>.
391 Since controller and action names may contain word delimiter
392 characters such as '_', '.', and '-', <methodname>render()</methodname>
393 normalizes these to '-' when determining the script name. Internally,
394 it uses the dispatcher's word and path delimiters to do this
395 normalization. Thus, a request to
396 <filename>/foo.bar/baz-bat</filename> will render the script
397 <filename>foo-bar/baz-bat.phtml</filename>. If your action method
398 contains camelCasing, please remember that this will result
399 in '-' separated words when determining the view script
408 <programlisting language="php"><![CDATA[
409 class MyController extends Zend_Controller_Action
411 public function fooAction()
413 // Renders my/foo.phtml
416 // Renders my/bar.phtml
417 $this->render('bar');
420 $this->render('baz', null, true);
422 // Renders my/login.phtml to the 'form' segment of the
424 $this->render('login', 'form');
426 // Renders site.phtml to the 'page' segment of the response
427 // object; does not use the 'my/' subirectory
428 $this->render('site', 'page', true);
431 public function bazBatAction()
433 // Renders my/baz-bat.phtml
441 <sect2 id="zend.controller.action.utilmethods">
442 <title>Utility Methods</title>
445 Besides the accessors and view integration methods,
446 <classname>Zend_Controller_Action</classname> has several utility methods for
447 performing common tasks from within your action methods (or from
448 pre- and post-dispatch).
454 <methodname>_forward($action, $controller = null, $module = null,
455 array $params = null)</methodname>: perform another action. If
456 called in <methodname>preDispatch()</methodname>, the currently
457 requested action will be skipped in favor of the new one.
458 Otherwise, after the current action is processed, the action
459 requested in <methodname>_forward()</methodname> will be executed.
465 <methodname>_redirect($url, array $options =
466 array())</methodname>: redirect to another location. This
467 method takes a <acronym>URL</acronym> and an optional set of options. By
468 default, it performs an <acronym>HTTP</acronym> 302 redirect.
472 The options may include one or more of the following:
478 <emphasis>exit:</emphasis> whether or not to exit
479 immediately. If requested, it will cleanly close any
480 open sessions and perform the redirect.
484 You may set this option globally within the
485 controller using the <methodname>setRedirectExit()</methodname>
492 <emphasis>prependBase:</emphasis> whether or not to
493 prepend the base <acronym>URL</acronym> registered with the request
494 object to the <acronym>URL</acronym> provided.
498 You may set this option globally within the
500 <methodname>setRedirectPrependBase()</methodname> accessor.
506 <emphasis>code:</emphasis> what <acronym>HTTP</acronym> code to utilize
507 in the redirect. By default, an <acronym>HTTP</acronym> 302 is
508 utilized; any code between 301 and 306 may be used.
512 You may set this option globally within the
514 <methodname>setRedirectCode()</methodname> accessor.
522 <sect2 id="zend.controller.action.subclassing">
523 <title>Subclassing the Action Controller</title>
526 By design, <classname>Zend_Controller_Action</classname> must be subclassed
527 in order to create an action controller. At the minimum, you will
528 need to define action methods that the controller may call.
532 Besides creating useful functionality for your web applications, you
533 may also find that you're repeating much of the same setup or
534 utility methods in your various controllers; if so, creating a
535 common base controller class that extends
536 <classname>Zend_Controller_Action</classname> could solve such redundancy.
539 <example id="zend.controller.action.subclassing.example-call">
540 <title>Handling Non-Existent Actions</title>
543 If a request to a controller is made that includes an undefined
544 action method, <methodname>Zend_Controller_Action::__call()</methodname>
545 will be invoked. <methodname>__call()</methodname> is, of course,
546 <acronym>PHP</acronym>'s magic method for method overloading.
550 By default, this method throws a
551 <classname>Zend_Controller_Action_Exception</classname> indicating the
552 requested method was not found in the controller. If the method
553 requested ends in 'Action', the assumption is that an action was
554 requested and does not exist; such errors result in an exception
555 with a code of 404. All other methods result in an exception
556 with a code of 500. This allows you to easily differentiate
557 between page not found and application errors in your error
562 You should override this functionality if you wish to perform
563 other operations. For instance, if you wish to display an error
564 message, you might write something like this:
567 <programlisting language="php"><![CDATA[
568 class MyController extends Zend_Controller_Action
570 public function __call($method, $args)
572 if ('Action' == substr($method, -6)) {
573 // If the action method was not found, render the error
575 return $this->render('error');
578 // all other methods throw an exception
579 throw new Exception('Invalid method "'
588 Another possibility is that you may want to forward on to a
589 default controller page:
592 <programlisting language="php"><![CDATA[
593 class MyController extends Zend_Controller_Action
595 public function indexAction()
600 public function __call($method, $args)
602 if ('Action' == substr($method, -6)) {
603 // If the action method was not found, forward to the
605 return $this->_forward('index');
608 // all other methods throw an exception
609 throw new Exception('Invalid method "'
619 Besides overriding <methodname>__call()</methodname>, each of the
620 initialization, utility, accessor, view, and dispatch hook methods
621 mentioned previously in this chapter may be overridden in order to
622 customize your controllers. As an example, if you are storing your
623 view object in a registry, you may want to modify your
624 <methodname>initView()</methodname> method with code resembling the following:
627 <programlisting language="php"><![CDATA[
628 abstract class My_Base_Controller extends Zend_Controller_Action
630 public function initView()
632 if (null === $this->view) {
633 if (Zend_Registry::isRegistered('view')) {
634 $this->view = Zend_Registry::get('view');
636 $this->view = new Zend_View();
637 $this->view->setBasePath(dirname(__FILE__) . '/../views');
647 Hopefully, from the information in this chapter, you can see the
648 flexibility of this particular component and how you can shape it to
649 your application's or site's needs.