Zend_Controller-ActionController.xml 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- Reviewed: no -->
  3. <sect1 id="zend.controller.action">
  4. <title>Action Controllers</title>
  5. <sect2 id="zend.controller.action.introduction">
  6. <title>Introduction</title>
  7. <para>
  8. <classname>Zend_Controller_Action</classname> is an abstract class you may use
  9. for implementing Action Controllers for use with the Front
  10. Controller when building a website based on the
  11. Model-View-Controller (<acronym>MVC</acronym>) pattern.
  12. </para>
  13. <para>
  14. To use <classname>Zend_Controller_Action</classname>, you will need to
  15. subclass it in your actual action controller classes (or subclass it
  16. to create your own base class for action controllers). The most
  17. basic operation is to subclass it, and create action methods that
  18. correspond to the various actions you wish the controller to handle
  19. for your site. <classname>Zend_Controller</classname>'s routing and dispatch handling
  20. will autodiscover any methods ending in 'Action' in your class as
  21. potential controller actions.
  22. </para>
  23. <para>
  24. For example, let's say your class is defined as follows:
  25. </para>
  26. <programlisting language="php"><![CDATA[
  27. class FooController extends Zend_Controller_Action
  28. {
  29. public function barAction()
  30. {
  31. // do something
  32. }
  33. public function bazAction()
  34. {
  35. // do something
  36. }
  37. }
  38. ]]></programlisting>
  39. <para>
  40. The above <emphasis>FooController</emphasis> class (controller
  41. <emphasis>foo</emphasis>) defines two actions, <emphasis>bar</emphasis> and
  42. <emphasis>baz</emphasis>.
  43. </para>
  44. <para>
  45. There's much more that can be accomplished than this, such as custom
  46. initialization actions, default actions to call should no action (or
  47. an invalid action) be specified, pre- and post-dispatch hooks, and a
  48. variety of helper methods. This chapter serves as an overview of the
  49. action controller functionality
  50. </para>
  51. <note>
  52. <title>Default Behaviour</title>
  53. <para>
  54. By default, the <link linkend="zend.controller.front">front
  55. controller</link> enables the <link
  56. linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer</link>
  57. action helper. This helper takes care of injecting the view
  58. object into the controller, as well as automatically rendering
  59. views. You may disable it within your action controller via one
  60. of the following methods:
  61. </para>
  62. <programlisting language="php"><![CDATA[
  63. class FooController extends Zend_Controller_Action
  64. {
  65. public function init()
  66. {
  67. // Local to this controller only; affects all actions,
  68. // as loaded in init:
  69. $this->_helper->viewRenderer->setNoRender(true);
  70. // Globally:
  71. $this->_helper->removeHelper('viewRenderer');
  72. // Also globally, but would need to be in conjunction with the
  73. // local version in order to propagate for this controller:
  74. Zend_Controller_Front::getInstance()
  75. ->setParam('noViewRenderer', true);
  76. }
  77. }
  78. ]]></programlisting>
  79. <para>
  80. <methodname>initView()</methodname>, <methodname>getViewScript()</methodname>,
  81. <methodname>render()</methodname>, and <methodname>renderScript()</methodname> each
  82. proxy to the <emphasis>ViewRenderer</emphasis> unless the helper is not
  83. in the helper broker or the <emphasis>noViewRenderer</emphasis> flag has
  84. been set.
  85. </para>
  86. <para>
  87. You can also simply disable rendering for an individual view by
  88. setting the <emphasis>ViewRenderer</emphasis>'s <emphasis>noRender</emphasis>
  89. flag:
  90. </para>
  91. <programlisting language="php"><![CDATA[
  92. class FooController extends Zend_Controller_Action
  93. {
  94. public function barAction()
  95. {
  96. // disable autorendering for this action only:
  97. $this->_helper->viewRenderer->setNoRender();
  98. }
  99. }
  100. ]]></programlisting>
  101. <para>
  102. The primary reasons to disable the <emphasis>ViewRenderer</emphasis> are
  103. if you simply do not need a view object or if you are not
  104. rendering via view scripts (for instance, when using an action
  105. controller to serve web service protocols such as <acronym>SOAP</acronym>,
  106. <acronym>XML-RPC</acronym>, or <acronym>REST</acronym>). In most cases, you will
  107. never need to globally disable the <emphasis>ViewRenderer</emphasis>, only
  108. selectively within individual controllers or actions.
  109. </para>
  110. </note>
  111. </sect2>
  112. <sect2 id="zend.controller.action.initialization">
  113. <title>Object Initialization</title>
  114. <para>
  115. While you can always override the action controller's constructor, we
  116. do not recommend this. <methodname>Zend_Controller_Action::__construct()</methodname>
  117. performs some important tasks, such as registering the request and
  118. response objects, as well as any custom invocation arguments passed
  119. in from the front controller. If you must override the constructor,
  120. be sure to call <methodname>parent::__construct($request, $response,
  121. $invokeArgs)</methodname>.
  122. </para>
  123. <para>
  124. The more appropriate way to customize instantiation is to use the
  125. <methodname>init()</methodname> method, which is called as the last task of
  126. <methodname>__construct()</methodname>. For example, if you want to connect to
  127. a database at instantiation:
  128. </para>
  129. <programlisting language="php"><![CDATA[
  130. class FooController extends Zend_Controller_Action
  131. {
  132. public function init()
  133. {
  134. $this->db = Zend_Db::factory('Pdo_Mysql', array(
  135. 'host' => 'myhost',
  136. 'username' => 'user',
  137. 'password' => 'XXXXXXX',
  138. 'dbname' => 'website'
  139. ));
  140. }
  141. }
  142. ]]></programlisting>
  143. </sect2>
  144. <sect2 id="zend.controller.action.prepostdispatch">
  145. <title>Pre- and Post-Dispatch Hooks</title>
  146. <para>
  147. <classname>Zend_Controller_Action</classname> specifies two methods that may
  148. be called to bookend a requested action, <methodname>preDispatch()</methodname>
  149. and <methodname>postDispatch()</methodname>. These can be useful in a variety of
  150. ways: verifying authentication and <acronym>ACL</acronym>'s prior to running an action
  151. (by calling <methodname>_forward()</methodname> in
  152. <methodname>preDispatch()</methodname>, the action will be skipped), for instance, or
  153. placing generated content in a sitewide template
  154. (<methodname>postDispatch()</methodname>).
  155. </para>
  156. <note>
  157. <title>Usage of init() vs. preDispatch()</title>
  158. <para>
  159. In the <link linkend="zend.controller.action.initialization">previous
  160. section</link>, we introduced the <methodname>init()</methodname> method, and
  161. in this section, the <methodname>preDispatch()</methodname> method. What is the
  162. difference between them, and what actions would you take in each?
  163. </para>
  164. <para>
  165. The <methodname>init()</methodname> method is primarily intended for extending the
  166. constructor. Typically, your constructor should simply set object state, and not
  167. perform much logic. This might include initializing resources used in the controller
  168. (such as models, configuration objects, etc.), or assigning values retrieved from
  169. the front controller, bootstrap, or a registry.
  170. </para>
  171. <para>
  172. The <methodname>preDispatch()</methodname> method can also be used to set object
  173. or environmental (e.g., view, action helper, etc.) state, but its primary purpose
  174. is to make decisions about whether or not the requested action should be dispatched.
  175. If not, you should then <methodname>_forward()</methodname> to another action, or
  176. throw an exception.
  177. </para>
  178. <para>
  179. Note: <methodname>_forward()</methodname> actually will not work correctly when
  180. executed from <methodname>init()</methodname>, which is a formalization of the
  181. intentions of the two methods.
  182. </para>
  183. </note>
  184. </sect2>
  185. <sect2 id="zend.controller.action.accessors">
  186. <title>Accessors</title>
  187. <para>
  188. A number of objects and variables are registered with the object,
  189. and each has accessor methods.
  190. </para>
  191. <itemizedlist>
  192. <listitem>
  193. <para>
  194. <emphasis>Request Object</emphasis>: <methodname>getRequest()</methodname>
  195. may be used to retrieve the request object used to call the action.
  196. </para>
  197. </listitem>
  198. <listitem>
  199. <para>
  200. <emphasis>Response Object</emphasis>:
  201. <methodname>getResponse()</methodname> may be used to retrieve the
  202. response object aggregating the final response. Some typical
  203. calls might look like:
  204. </para>
  205. <programlisting language="php"><![CDATA[
  206. $this->getResponse()->setHeader('Content-Type', 'text/xml');
  207. $this->getResponse()->appendBody($content);
  208. ]]></programlisting>
  209. </listitem>
  210. <listitem>
  211. <para>
  212. <emphasis>Invocation Arguments</emphasis>: the front
  213. controller may push parameters into the router, dispatcher,
  214. and action controller. To retrieve these, use
  215. <methodname>getInvokeArg($key)</methodname>; alternatively, fetch the
  216. entire list using <methodname>getInvokeArgs()</methodname>.
  217. </para>
  218. </listitem>
  219. <listitem>
  220. <para>
  221. <emphasis>Request parameters</emphasis>: The request object
  222. aggregates request parameters, such as any <constant>_GET</constant> or
  223. <constant>_POST</constant> parameters, or user parameters specified in the
  224. <acronym>URL</acronym>'s path information. To retrieve these, use
  225. <methodname>_getParam($key)</methodname> or
  226. <methodname>_getAllParams()</methodname>. You may also set request
  227. parameters using <methodname>_setParam()</methodname>; this is useful
  228. when forwarding to additional actions.
  229. </para>
  230. <para>
  231. To test whether or not a parameter exists (useful for
  232. logical branching), use <methodname>_hasParam($key)</methodname>.
  233. </para>
  234. <note>
  235. <para>
  236. <methodname>_getParam()</methodname> may take an optional second
  237. argument containing a default value to use if the
  238. parameter is not set or is empty. Using it eliminates
  239. the need to call <methodname>_hasParam()</methodname> prior to
  240. retrieving a value:
  241. </para>
  242. <programlisting language="php"><![CDATA[
  243. // Use default value of 1 if id is not set
  244. $id = $this->_getParam('id', 1);
  245. // Instead of:
  246. if ($this->_hasParam('id') {
  247. $id = $this->_getParam('id');
  248. } else {
  249. $id = 1;
  250. }
  251. ]]></programlisting>
  252. </note>
  253. </listitem>
  254. </itemizedlist>
  255. </sect2>
  256. <sect2 id="zend.controller.action.viewintegration">
  257. <title>View Integration</title>
  258. <note id="zend.controller.action.viewintegration.viewrenderer">
  259. <title>Default View Integration is Via the ViewRenderer</title>
  260. <para>
  261. The content in this section is only valid when you have explicitly disabled the
  262. <link linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer</link>.
  263. Otherwise, you can safely skip over this section.
  264. </para>
  265. </note>
  266. <para>
  267. <classname>Zend_Controller_Action</classname> provides a rudimentary and
  268. flexible mechanism for view integration. Two methods accomplish
  269. this, <methodname>initView()</methodname> and <methodname>render()</methodname>; the
  270. former method lazy-loads the <varname>$view</varname> public property, and the
  271. latter renders a view based on the current requested action, using
  272. the directory hierarchy to determine the script path.
  273. </para>
  274. <sect3 id="zend.controller.action.viewintegration.initview">
  275. <title>View Initialization</title>
  276. <para>
  277. <methodname>initView()</methodname> initializes the view object.
  278. <methodname>render()</methodname> calls <methodname>initView()</methodname> in
  279. order to retrieve the view object, but it may be initialized at any time;
  280. by default it populates the <varname>$view</varname> property with a
  281. <classname>Zend_View</classname> object, but any class implementing
  282. <classname>Zend_View_Interface</classname> may be used. If
  283. <varname>$view</varname> is already initialized, it simply returns
  284. that property.
  285. </para>
  286. <para>
  287. The default implementation makes the following assumption of
  288. the directory structure:
  289. </para>
  290. <programlisting language="php"><![CDATA[
  291. applicationOrModule/
  292. controllers/
  293. IndexController.php
  294. views/
  295. scripts/
  296. index/
  297. index.phtml
  298. helpers/
  299. filters/
  300. ]]></programlisting>
  301. <para>
  302. In other words, view scripts are assumed to be in the
  303. <filename>/views/scripts/</filename> subdirectory, and the
  304. <filename>/views/</filename> subdirectory is assumed to contain sibling
  305. functionality (helpers, filters). When determining the view
  306. script name and path, the <filename>/views/scripts/</filename> directory
  307. will be used as the base path, with directories named after the
  308. individual controllers providing a hierarchy of view scripts.
  309. </para>
  310. </sect3>
  311. <sect3 id="zend.controller.action.viewintegration.render">
  312. <title>Rendering Views</title>
  313. <para>
  314. <methodname>render()</methodname> has the following signature:
  315. </para>
  316. <programlisting language="php"><![CDATA[
  317. string render(string $action = null,
  318. string $name = null,
  319. bool $noController = false);
  320. ]]></programlisting>
  321. <para>
  322. <methodname>render()</methodname> renders a view script. If no arguments are
  323. passed, it assumes that the script requested is
  324. <filename>[controller]/[action].phtml</filename> (where
  325. <filename>.phtml</filename> is the value of the <varname>$viewSuffix</varname>
  326. property). Passing a value for <varname>$action</varname> will render
  327. that template in the <filename>/[controller]/</filename> subdirectory. To
  328. override using the <filename>/[controller]/</filename> subdirectory, pass
  329. a <constant>TRUE</constant> value for <varname>$noController</varname>. Finally,
  330. templates are rendered into the response object; if you wish to render to
  331. a specific <link
  332. linkend="zend.controller.response.namedsegments">named
  333. segment</link> in the response object, pass a value to
  334. <varname>$name</varname>.
  335. </para>
  336. <note>
  337. <para>
  338. Since controller and action names may contain word delimiter
  339. characters such as '_', '.', and '-', <methodname>render()</methodname>
  340. normalizes these to '-' when determining the script name. Internally,
  341. it uses the dispatcher's word and path delimiters to do this
  342. normalization. Thus, a request to
  343. <filename>/foo.bar/baz-bat</filename> will render the script
  344. <filename>foo-bar/baz-bat.phtml</filename>. If your action method
  345. contains camelCasing, please remember that this will result
  346. in '-' separated words when determining the view script
  347. file name.
  348. </para>
  349. </note>
  350. <para>
  351. Some examples:
  352. </para>
  353. <programlisting language="php"><![CDATA[
  354. class MyController extends Zend_Controller_Action
  355. {
  356. public function fooAction()
  357. {
  358. // Renders my/foo.phtml
  359. $this->render();
  360. // Renders my/bar.phtml
  361. $this->render('bar');
  362. // Renders baz.phtml
  363. $this->render('baz', null, true);
  364. // Renders my/login.phtml to the 'form' segment of the
  365. // response object
  366. $this->render('login', 'form');
  367. // Renders site.phtml to the 'page' segment of the response
  368. // object; does not use the 'my/' subirectory
  369. $this->render('site', 'page', true);
  370. }
  371. public function bazBatAction()
  372. {
  373. // Renders my/baz-bat.phtml
  374. $this->render();
  375. }
  376. }
  377. ]]></programlisting>
  378. </sect3>
  379. </sect2>
  380. <sect2 id="zend.controller.action.utilmethods">
  381. <title>Utility Methods</title>
  382. <para>
  383. Besides the accessors and view integration methods,
  384. <classname>Zend_Controller_Action</classname> has several utility methods for
  385. performing common tasks from within your action methods (or from
  386. pre- and post-dispatch).
  387. </para>
  388. <itemizedlist>
  389. <listitem>
  390. <para>
  391. <methodname>_forward($action, $controller = null, $module = null,
  392. array $params = null)</methodname>: perform another action. If
  393. called in <methodname>preDispatch()</methodname>, the currently
  394. requested action will be skipped in favor of the new one.
  395. Otherwise, after the current action is processed, the action
  396. requested in <methodname>_forward()</methodname> will be executed.
  397. </para>
  398. </listitem>
  399. <listitem>
  400. <para>
  401. <methodname>_redirect($url, array $options =
  402. array())</methodname>: redirect to another location. This
  403. method takes a <acronym>URL</acronym> and an optional set of options. By
  404. default, it performs an <acronym>HTTP</acronym> 302 redirect.
  405. </para>
  406. <para>
  407. The options may include one or more of the following:
  408. </para>
  409. <itemizedlist>
  410. <listitem>
  411. <para>
  412. <emphasis>exit:</emphasis> whether or not to exit
  413. immediately. If requested, it will cleanly close any
  414. open sessions and perform the redirect.
  415. </para>
  416. <para>
  417. You may set this option globally within the
  418. controller using the <methodname>setRedirectExit()</methodname>
  419. accessor.
  420. </para>
  421. </listitem>
  422. <listitem>
  423. <para>
  424. <emphasis>prependBase:</emphasis> whether or not to
  425. prepend the base <acronym>URL</acronym> registered with the request
  426. object to the <acronym>URL</acronym> provided.
  427. </para>
  428. <para>
  429. You may set this option globally within the
  430. controller using the
  431. <methodname>setRedirectPrependBase()</methodname> accessor.
  432. </para>
  433. </listitem>
  434. <listitem>
  435. <para>
  436. <emphasis>code:</emphasis> what <acronym>HTTP</acronym> code to utilize
  437. in the redirect. By default, an <acronym>HTTP</acronym> 302 is
  438. utilized; any code between 301 and 306 may be used.
  439. </para>
  440. <para>
  441. You may set this option globally within the
  442. controller using the
  443. <methodname>setRedirectCode()</methodname> accessor.
  444. </para>
  445. </listitem>
  446. </itemizedlist>
  447. </listitem>
  448. </itemizedlist>
  449. </sect2>
  450. <sect2 id="zend.controller.action.subclassing">
  451. <title>Subclassing the Action Controller</title>
  452. <para>
  453. By design, <classname>Zend_Controller_Action</classname> must be subclassed
  454. in order to create an action controller. At the minimum, you will
  455. need to define action methods that the controller may call.
  456. </para>
  457. <para>
  458. Besides creating useful functionality for your web applications, you
  459. may also find that you're repeating much of the same setup or
  460. utility methods in your various controllers; if so, creating a
  461. common base controller class that extends
  462. <classname>Zend_Controller_Action</classname> could solve such redundancy.
  463. </para>
  464. <example id="zend.controller.action.subclassing.example-call">
  465. <title>Handling Non-Existent Actions</title>
  466. <para>
  467. If a request to a controller is made that includes an undefined
  468. action method, <methodname>Zend_Controller_Action::__call()</methodname>
  469. will be invoked. <methodname>__call()</methodname> is, of course,
  470. <acronym>PHP</acronym>'s magic method for method overloading.
  471. </para>
  472. <para>
  473. By default, this method throws a
  474. <classname>Zend_Controller_Action_Exception</classname> indicating the
  475. requested method was not found in the controller. If the method
  476. requested ends in 'Action', the assumption is that an action was
  477. requested and does not exist; such errors result in an exception
  478. with a code of 404. All other methods result in an exception
  479. with a code of 500. This allows you to easily differentiate
  480. between page not found and application errors in your error
  481. handler.
  482. </para>
  483. <para>
  484. You should override this functionality if you wish to perform
  485. other operations. For instance, if you wish to display an error
  486. message, you might write something like this:
  487. </para>
  488. <programlisting language="php"><![CDATA[
  489. class MyController extends Zend_Controller_Action
  490. {
  491. public function __call($method, $args)
  492. {
  493. if ('Action' == substr($method, -6)) {
  494. // If the action method was not found, render the error
  495. // template
  496. return $this->render('error');
  497. }
  498. // all other methods throw an exception
  499. throw new Exception('Invalid method "'
  500. . $method
  501. . '" called',
  502. 500);
  503. }
  504. }
  505. ]]></programlisting>
  506. <para>
  507. Another possibility is that you may want to forward on to a
  508. default controller page:
  509. </para>
  510. <programlisting language="php"><![CDATA[
  511. class MyController extends Zend_Controller_Action
  512. {
  513. public function indexAction()
  514. {
  515. $this->render();
  516. }
  517. public function __call($method, $args)
  518. {
  519. if ('Action' == substr($method, -6)) {
  520. // If the action method was not found, forward to the
  521. // index action
  522. return $this->_forward('index');
  523. }
  524. // all other methods throw an exception
  525. throw new Exception('Invalid method "'
  526. . $method
  527. . '" called',
  528. 500);
  529. }
  530. }
  531. ]]></programlisting>
  532. </example>
  533. <para>
  534. Besides overriding <methodname>__call()</methodname>, each of the
  535. initialization, utility, accessor, view, and dispatch hook methods
  536. mentioned previously in this chapter may be overridden in order to
  537. customize your controllers. As an example, if you are storing your
  538. view object in a registry, you may want to modify your
  539. <methodname>initView()</methodname> method with code resembling the following:
  540. </para>
  541. <programlisting language="php"><![CDATA[
  542. abstract class My_Base_Controller extends Zend_Controller_Action
  543. {
  544. public function initView()
  545. {
  546. if (null === $this->view) {
  547. if (Zend_Registry::isRegistered('view')) {
  548. $this->view = Zend_Registry::get('view');
  549. } else {
  550. $this->view = new Zend_View();
  551. $this->view->setBasePath(dirname(__FILE__) . '/../views');
  552. }
  553. }
  554. return $this->view;
  555. }
  556. }
  557. ]]></programlisting>
  558. <para>
  559. Hopefully, from the information in this chapter, you can see the
  560. flexibility of this particular component and how you can shape it to
  561. your application's or site's needs.
  562. </para>
  563. </sect2>
  564. </sect1>
  565. <!--
  566. vim:se ts=4 sw=4 et:
  567. -->