Zend_Controller-ActionController.xml 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  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><para>
  193. <emphasis>Request Object</emphasis>: <methodname>getRequest()</methodname>
  194. may be used to retrieve the request object used to call the
  195. action.
  196. </para></listitem>
  197. <listitem>
  198. <para>
  199. <emphasis>Response Object</emphasis>:
  200. <methodname>getResponse()</methodname> may be used to retrieve the
  201. response object aggregating the final response. Some typical
  202. calls might look like:
  203. </para>
  204. <programlisting language="php"><![CDATA[
  205. $this->getResponse()->setHeader('Content-Type', 'text/xml');
  206. $this->getResponse()->appendBody($content);
  207. ]]></programlisting>
  208. </listitem>
  209. <listitem>
  210. <para>
  211. <emphasis>Invocation Arguments</emphasis>: the front
  212. controller may push parameters into the router, dispatcher,
  213. and action controller. To retrieve these, use
  214. <methodname>getInvokeArg($key)</methodname>; alternatively, fetch the
  215. entire list using <methodname>getInvokeArgs()</methodname>.
  216. </para>
  217. </listitem>
  218. <listitem>
  219. <para>
  220. <emphasis>Request parameters</emphasis>: The request object
  221. aggregates request parameters, such as any <constant>_GET</constant> or
  222. <constant>_POST</constant> parameters, or user parameters specified in the
  223. <acronym>URL</acronym>'s path information. To retrieve these, use
  224. <methodname>_getParam($key)</methodname> or
  225. <methodname>_getAllParams()</methodname>. You may also set request
  226. parameters using <methodname>_setParam()</methodname>; this is useful
  227. when forwarding to additional actions.
  228. </para>
  229. <para>
  230. To test whether or not a parameter exists (useful for
  231. logical branching), use <methodname>_hasParam($key)</methodname>.
  232. </para>
  233. <note>
  234. <para>
  235. <methodname>_getParam()</methodname> may take an optional second
  236. argument containing a default value to use if the
  237. parameter is not set or is empty. Using it eliminates
  238. the need to call <methodname>_hasParam()</methodname> prior to
  239. retrieving a value:
  240. </para>
  241. <programlisting language="php"><![CDATA[
  242. // Use default value of 1 if id is not set
  243. $id = $this->_getParam('id', 1);
  244. // Instead of:
  245. if ($this->_hasParam('id') {
  246. $id = $this->_getParam('id');
  247. } else {
  248. $id = 1;
  249. }
  250. ]]></programlisting>
  251. </note>
  252. </listitem>
  253. </itemizedlist>
  254. </sect2>
  255. <sect2 id="zend.controller.action.viewintegration">
  256. <title>View Integration</title>
  257. <note id="zend.controller.action.viewintegration.viewrenderer">
  258. <title>Default View Integration is Via the ViewRenderer</title>
  259. <para>
  260. The content in this section is only valid when you have explicitly disabled the
  261. <link linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer</link>.
  262. Otherwise, you can safely skip over this section.
  263. </para>
  264. </note>
  265. <para>
  266. <classname>Zend_Controller_Action</classname> provides a rudimentary and
  267. flexible mechanism for view integration. Two methods accomplish
  268. this, <methodname>initView()</methodname> and <methodname>render()</methodname>; the
  269. former method lazy-loads the <varname>$view</varname> public property, and the
  270. latter renders a view based on the current requested action, using
  271. the directory hierarchy to determine the script path.
  272. </para>
  273. <sect3 id="zend.controller.action.viewintegration.initview">
  274. <title>View Initialization</title>
  275. <para>
  276. <methodname>initView()</methodname> initializes the view object.
  277. <methodname>render()</methodname> calls <methodname>initView()</methodname> in
  278. order to retrieve the view object, but it may be initialized at any time;
  279. by default it populates the <varname>$view</varname> property with a
  280. <classname>Zend_View</classname> object, but any class implementing
  281. <classname>Zend_View_Interface</classname> may be used. If
  282. <varname>$view</varname> is already initialized, it simply returns
  283. that property.
  284. </para>
  285. <para>
  286. The default implementation makes the following assumption of
  287. the directory structure:
  288. </para>
  289. <programlisting language="php"><![CDATA[
  290. applicationOrModule/
  291. controllers/
  292. IndexController.php
  293. views/
  294. scripts/
  295. index/
  296. index.phtml
  297. helpers/
  298. filters/
  299. ]]></programlisting>
  300. <para>
  301. In other words, view scripts are assumed to be in the
  302. <filename>/views/scripts/</filename> subdirectory, and the
  303. <filename>/views/</filename> subdirectory is assumed to contain sibling
  304. functionality (helpers, filters). When determining the view
  305. script name and path, the <filename>/views/scripts/</filename> directory
  306. will be used as the base path, with directories named after the
  307. individual controllers providing a hierarchy of view scripts.
  308. </para>
  309. </sect3>
  310. <sect3 id="zend.controller.action.viewintegration.render">
  311. <title>Rendering Views</title>
  312. <para>
  313. <methodname>render()</methodname> has the following signature:
  314. </para>
  315. <programlisting language="php"><![CDATA[
  316. string render(string $action = null,
  317. string $name = null,
  318. bool $noController = false);
  319. ]]></programlisting>
  320. <para>
  321. <methodname>render()</methodname> renders a view script. If no arguments are
  322. passed, it assumes that the script requested is
  323. <filename>[controller]/[action].phtml</filename> (where
  324. <filename>.phtml</filename> is the value of the <varname>$viewSuffix</varname>
  325. property). Passing a value for <varname>$action</varname> will render
  326. that template in the <filename>/[controller]/</filename> subdirectory. To
  327. override using the <filename>/[controller]/</filename> subdirectory, pass
  328. a <constant>TRUE</constant> value for <varname>$noController</varname>. Finally,
  329. templates are rendered into the response object; if you wish to render to
  330. a specific <link
  331. linkend="zend.controller.response.namedsegments">named
  332. segment</link> in the response object, pass a value to
  333. <varname>$name</varname>.
  334. </para>
  335. <note><para>
  336. Since controller and action names may contain word delimiter
  337. characters such as '_', '.', and '-', <methodname>render()</methodname>
  338. normalizes these to '-' when determining the script name. Internally,
  339. it uses the dispatcher's word and path delimiters to do this
  340. normalization. Thus, a request to
  341. <filename>/foo.bar/baz-bat</filename> will render the script
  342. <filename>foo-bar/baz-bat.phtml</filename>. If your action method
  343. contains camelCasing, please remember that this will result
  344. in '-' separated words when determining the view script
  345. file name.
  346. </para></note>
  347. <para>
  348. Some examples:
  349. </para>
  350. <programlisting language="php"><![CDATA[
  351. class MyController extends Zend_Controller_Action
  352. {
  353. public function fooAction()
  354. {
  355. // Renders my/foo.phtml
  356. $this->render();
  357. // Renders my/bar.phtml
  358. $this->render('bar');
  359. // Renders baz.phtml
  360. $this->render('baz', null, true);
  361. // Renders my/login.phtml to the 'form' segment of the
  362. // response object
  363. $this->render('login', 'form');
  364. // Renders site.phtml to the 'page' segment of the response
  365. // object; does not use the 'my/' subirectory
  366. $this->render('site', 'page', true);
  367. }
  368. public function bazBatAction()
  369. {
  370. // Renders my/baz-bat.phtml
  371. $this->render();
  372. }
  373. }
  374. ]]></programlisting>
  375. </sect3>
  376. </sect2>
  377. <sect2 id="zend.controller.action.utilmethods">
  378. <title>Utility Methods</title>
  379. <para>
  380. Besides the accessors and view integration methods,
  381. <classname>Zend_Controller_Action</classname> has several utility methods for
  382. performing common tasks from within your action methods (or from
  383. pre- and post-dispatch).
  384. </para>
  385. <itemizedlist>
  386. <listitem>
  387. <para>
  388. <methodname>_forward($action, $controller = null, $module = null,
  389. array $params = null)</methodname>: perform another action. If
  390. called in <methodname>preDispatch()</methodname>, the currently
  391. requested action will be skipped in favor of the new one.
  392. Otherwise, after the current action is processed, the action
  393. requested in <methodname>_forward()</methodname> will be executed.
  394. </para>
  395. </listitem>
  396. <listitem>
  397. <para>
  398. <methodname>_redirect($url, array $options =
  399. array())</methodname>: redirect to another location. This
  400. method takes a <acronym>URL</acronym> and an optional set of options. By
  401. default, it performs an <acronym>HTTP</acronym> 302 redirect.
  402. </para>
  403. <para>
  404. The options may include one or more of the following:
  405. </para>
  406. <itemizedlist>
  407. <listitem>
  408. <para>
  409. <emphasis>exit:</emphasis> whether or not to exit
  410. immediately. If requested, it will cleanly close any
  411. open sessions and perform the redirect.
  412. </para>
  413. <para>
  414. You may set this option globally within the
  415. controller using the <methodname>setRedirectExit()</methodname>
  416. accessor.
  417. </para>
  418. </listitem>
  419. <listitem>
  420. <para>
  421. <emphasis>prependBase:</emphasis> whether or not to
  422. prepend the base <acronym>URL</acronym> registered with the request
  423. object to the <acronym>URL</acronym> provided.
  424. </para>
  425. <para>
  426. You may set this option globally within the
  427. controller using the
  428. <methodname>setRedirectPrependBase()</methodname> accessor.
  429. </para>
  430. </listitem>
  431. <listitem>
  432. <para>
  433. <emphasis>code:</emphasis> what <acronym>HTTP</acronym> code to utilize
  434. in the redirect. By default, an <acronym>HTTP</acronym> 302 is
  435. utilized; any code between 301 and 306 may be used.
  436. </para>
  437. <para>
  438. You may set this option globally within the
  439. controller using the
  440. <methodname>setRedirectCode()</methodname> accessor.
  441. </para>
  442. </listitem>
  443. </itemizedlist>
  444. </listitem>
  445. </itemizedlist>
  446. </sect2>
  447. <sect2 id="zend.controller.action.subclassing">
  448. <title>Subclassing the Action Controller</title>
  449. <para>
  450. By design, <classname>Zend_Controller_Action</classname> must be subclassed
  451. in order to create an action controller. At the minimum, you will
  452. need to define action methods that the controller may call.
  453. </para>
  454. <para>
  455. Besides creating useful functionality for your web applications, you
  456. may also find that you're repeating much of the same setup or
  457. utility methods in your various controllers; if so, creating a
  458. common base controller class that extends
  459. <classname>Zend_Controller_Action</classname> could solve such redundancy.
  460. </para>
  461. <example id="zend.controller.action.subclassing.example-call">
  462. <title>Handling Non-Existent Actions</title>
  463. <para>
  464. If a request to a controller is made that includes an undefined
  465. action method, <methodname>Zend_Controller_Action::__call()</methodname>
  466. will be invoked. <methodname>__call()</methodname> is, of course,
  467. <acronym>PHP</acronym>'s magic method for method overloading.
  468. </para>
  469. <para>
  470. By default, this method throws a
  471. <classname>Zend_Controller_Action_Exception</classname> indicating the
  472. requested method was not found in the controller. If the method
  473. requested ends in 'Action', the assumption is that an action was
  474. requested and does not exist; such errors result in an exception
  475. with a code of 404. All other methods result in an exception
  476. with a code of 500. This allows you to easily differentiate
  477. between page not found and application errors in your error
  478. handler.
  479. </para>
  480. <para>
  481. You should override this functionality if you wish to perform
  482. other operations. For instance, if you wish to display an error
  483. message, you might write something like this:
  484. </para>
  485. <programlisting language="php"><![CDATA[
  486. class MyController extends Zend_Controller_Action
  487. {
  488. public function __call($method, $args)
  489. {
  490. if ('Action' == substr($method, -6)) {
  491. // If the action method was not found, render the error
  492. // template
  493. return $this->render('error');
  494. }
  495. // all other methods throw an exception
  496. throw new Exception('Invalid method "'
  497. . $method
  498. . '" called',
  499. 500);
  500. }
  501. }
  502. ]]></programlisting>
  503. <para>
  504. Another possibility is that you may want to forward on to a
  505. default controller page:
  506. </para>
  507. <programlisting language="php"><![CDATA[
  508. class MyController extends Zend_Controller_Action
  509. {
  510. public function indexAction()
  511. {
  512. $this->render();
  513. }
  514. public function __call($method, $args)
  515. {
  516. if ('Action' == substr($method, -6)) {
  517. // If the action method was not found, forward to the
  518. // index action
  519. return $this->_forward('index');
  520. }
  521. // all other methods throw an exception
  522. throw new Exception('Invalid method "'
  523. . $method
  524. . '" called',
  525. 500);
  526. }
  527. }
  528. ]]></programlisting>
  529. </example>
  530. <para>
  531. Besides overriding <methodname>__call()</methodname>, each of the
  532. initialization, utility, accessor, view, and dispatch hook methods
  533. mentioned previously in this chapter may be overridden in order to
  534. customize your controllers. As an example, if you are storing your
  535. view object in a registry, you may want to modify your
  536. <methodname>initView()</methodname> method with code resembling the following:
  537. </para>
  538. <programlisting language="php"><![CDATA[
  539. abstract class My_Base_Controller extends Zend_Controller_Action
  540. {
  541. public function initView()
  542. {
  543. if (null === $this->view) {
  544. if (Zend_Registry::isRegistered('view')) {
  545. $this->view = Zend_Registry::get('view');
  546. } else {
  547. $this->view = new Zend_View();
  548. $this->view->setBasePath(dirname(__FILE__) . '/../views');
  549. }
  550. }
  551. return $this->view;
  552. }
  553. }
  554. ]]></programlisting>
  555. <para>
  556. Hopefully, from the information in this chapter, you can see the
  557. flexibility of this particular component and how you can shape it to
  558. your application's or site's needs.
  559. </para>
  560. </sect2>
  561. </sect1>
  562. <!--
  563. vim:se ts=4 sw=4 et:
  564. -->