Zend_Controller-ActionController.xml 24 KB

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