Zend_Controller-ActionController.xml 23 KB

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