Zend_Controller-ActionController.xml 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- Reviewed: no -->
  3. <sect1 id="zend.controller.action">
  4. <title>Контроллеры действий</title>
  5. <sect2 id="zend.controller.action.introduction">
  6. <title>Введение</title>
  7. <para>
  8. <classname>Zend_Controller_Action</classname> - абстрактный класс,
  9. который можно использовать для реализации контроллеров действий
  10. для последующего их использования с фронт-контроллером при
  11. разработке сайта, основанного на паттерне Model-View-Controller
  12. (MVC).
  13. </para>
  14. <para>
  15. Для того, чтобы использовать <classname>Zend_Controller_Action</classname>,
  16. нужно создать его подкласс в своей действующей директории
  17. контроллеров (или расширить его для создания своего базового класса
  18. контроллеров действий). Работа с ним в основном сводится к
  19. созданию его подкласса и написании методов действий, соответствующих
  20. различным действиям, которые должен обрабатывать этот контроллер.
  21. Маршрутизатор и диспетчер компоненты <classname>Zend_Controller</classname>
  22. будут считать за методы действий все методы в классе
  23. контроллера с именем, заканчивающимся на 'Action'.
  24. </para>
  25. <para>
  26. Для примера предположим, что ваш класс определен следующим образом:
  27. </para>
  28. <programlisting language="php"><![CDATA[
  29. class FooController extends Zend_Controller_Action
  30. {
  31. public function barAction()
  32. {
  33. // делает что-нибудь
  34. }
  35. public function bazAction()
  36. {
  37. // делает что-нибудь
  38. }
  39. }
  40. ]]></programlisting>
  41. <para>
  42. Приведенный выше класс <code>FooController</code> (контроллер
  43. <code>foo</code>) определяет два действия - <code>bar</code> и
  44. <code>baz</code>.
  45. </para>
  46. <para>
  47. Класс может быть дополнен инициализирующим методом, методом
  48. действия по умолчанию (если не был вызван метод, либо
  49. вызван несуществующий метод), перехватчиками pre- и post-dispatch и
  50. различными вспомогательными методами. Этот раздел служит обзором
  51. функционала контроллера действий.
  52. </para>
  53. <note>
  54. <title>Поведение по умолчанию</title>
  55. <para>
  56. По умолчанию <link
  57. linkend="zend.controller.front">фронт-контроллер</link>
  58. активирует помощника действий <link
  59. linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer</link>.
  60. Этот помощник обеспечивает добавление объекта вида в контроллер
  61. и автоматический рендеринг видов. Вы можете отключить его в
  62. своем контроллере действия, используя один из следующих методов:
  63. </para>
  64. <programlisting language="php"><![CDATA[
  65. class FooController extends Zend_Controller_Action
  66. {
  67. public function init()
  68. {
  69. // Локально, только для данного контроллера:
  70. $this->_invokeArgs['noViewRenderer'] = true;
  71. // Глобально:
  72. $this->_helper->removeHelper('viewRenderer');
  73. // Тоже глобально, но должен использоваться вместе
  74. // с локальной версией для того, чтобы распространить
  75. // действие на данный контроллер:
  76. Zend_Controller_Front::getInstance()
  77. ->setParam('noViewRenderer', true);
  78. }
  79. }
  80. ]]></programlisting>
  81. <para>
  82. <code>initView()</code>, <code>getViewScript()</code>,
  83. <code>render()</code> и <code>renderScript()</code> служат
  84. посредниками для <code>ViewRenderer</code>, пока этот помощник
  85. находится в брокере помощников и не установлен флаг
  86. <code>noViewRenderer</code>.
  87. </para>
  88. <para>
  89. Вы можете также отключить рендеринг для отдельного вида
  90. посредством установки флага <code>noRender</code> в
  91. <code>ViewRenderer</code>:
  92. </para>
  93. <programlisting language="php"><![CDATA[
  94. class FooController extends Zend_Controller_Action
  95. {
  96. public function barAction()
  97. {
  98. // отключение авторендеринга для этого действия:
  99. $this->_helper->viewRenderer->setNoRender();
  100. }
  101. }
  102. ]]></programlisting>
  103. <para>
  104. Основные причины для отключения <code>ViewRenderer</code> - вам
  105. просто не нужен объект вида или если вы не производите рендеринг
  106. через скрипты вида (например, когда используется контроллер
  107. действий для обслуживания протоколов веб-сервисов, таких, как
  108. SOAP, XML-RPC, или REST). В большинстве случаев не
  109. нужно будет глобально отключать <code>ViewRenderer</code>,
  110. только избирательно в отдельных контроллерах или действиях.
  111. </para>
  112. </note>
  113. </sect2>
  114. <sect2 id="zend.controller.action.initialization">
  115. <title>Инициализация объекта</title>
  116. <para>
  117. Несмотря на то, что вы всегда можете переопределить конструктор
  118. контроллера действий, мы не рекомендуем делать это.
  119. <code>Zend_Controller_Action::__construct()</code>
  120. выполняет некоторые важные
  121. задачи, такие, как регистрация объектов запроса и ответа, аргументов
  122. вызова, переданных из фронт-контроллера. Если необходимо
  123. переопределить контроллер, то всегда вызывайте конструктор
  124. родительского класса <code>parent::__construct($request, $response,
  125. $invokeArgs)</code> в конструкторе подкласса.
  126. </para>
  127. <para>
  128. Более подходящим способом настройки инстанцирования
  129. является использование метода <code>init()</code>, который
  130. вызывается в конце выполнения <code>__construct()</code>. Например,
  131. если вы хотите устанавливать соединение с БД при инстанцировании:
  132. </para>
  133. <programlisting language="php"><![CDATA[
  134. class FooController extends Zend_Controller_Action
  135. {
  136. public function init()
  137. {
  138. $this->db = Zend_Db::factory('Pdo_Mysql', array(
  139. 'host' => 'myhost',
  140. 'username' => 'user',
  141. 'password' => 'XXXXXXX',
  142. 'dbname' => 'website'
  143. ));
  144. }
  145. }
  146. ]]></programlisting>
  147. </sect2>
  148. <sect2 id="zend.controller.action.prepostdispatch">
  149. <title>Перехватчики Pre- и Post-Dispatch</title>
  150. <para>
  151. <classname>Zend_Controller_Action</classname> определяет два метода, которые
  152. вызываются до и после требуемого действия,
  153. <code>preDispatch()</code> и <code>postDispatch()</code>. Они
  154. могут быть полезны в различных случаях - например, проверка
  155. аутентификации и списка управления доступом до запуска действия
  156. (при вызове метода <code>_forward()</code> в
  157. <code>preDispatch()</code> текущее действие будет пропущено) или
  158. размещение сгенерированного содержимого в шаблоне боковой части
  159. сайта (метод <code>postDispatch()</code>).
  160. </para>
  161. </sect2>
  162. <sect2 id="zend.controller.action.accessors">
  163. <title>Аксессоры</title>
  164. <para>
  165. С объектом контроллера регистрируется несколько объектов
  166. и переменных, они имеют свои методы-аксессоры.
  167. </para>
  168. <itemizedlist>
  169. <listitem><para>
  170. <emphasis>Объект запроса</emphasis>: через метод
  171. <code>getRequest()</code> извлекается объект запроса, который
  172. использовался для вызова данного действия.
  173. </para></listitem>
  174. <listitem>
  175. <para>
  176. <emphasis>Объект ответа</emphasis>:
  177. через метод <code>getResponse()</code> извлекается объект
  178. ответа, объединяющий в себе заголовки и содержимое ответа.
  179. Некоторые типичные вызовы могут выглядеть следующим образом:
  180. </para>
  181. <programlisting language="php"><![CDATA[
  182. $this->getResponse()->setHeader('Content-Type', 'text/xml');
  183. $this->getResponse()->appendBody($content);
  184. ]]></programlisting>
  185. </listitem>
  186. <listitem>
  187. <para>
  188. <emphasis>Аргументы вызова</emphasis>: фронт-контроллер
  189. может добавлять параметры в маршрутизатор, диспетчер и
  190. контроллер действий. Для их получения используйте
  191. <code>getInvokeArg($key)</code>, можно также извлечь весь список
  192. аргументов, используя метод <code>getInvokeArgs()</code>.
  193. </para>
  194. </listitem>
  195. <listitem>
  196. <para>
  197. <emphasis>Параметры запроса</emphasis>: Объект запроса
  198. заключает в себе параметры запроса, такие, как значения
  199. _GET, _POST, или пользовательские параметры, определенные в
  200. пути URL. Для их получения используйте
  201. <code>_getParam($key)</code> или
  202. <code>_getAllParams()</code>. Вы можете также установить
  203. параметры запроса, используя метод <code>_setParam()</code>,
  204. это полезно при перенаправлении на другие действия через
  205. метод <code>_forward()</code>.
  206. </para>
  207. <para>
  208. Для определения того, существует ли параметр или нет
  209. (полезно для логического ветвления), используйте
  210. <code>_hasParam($key)</code>.
  211. </para>
  212. <note>
  213. <para>
  214. <code>_getParam()</code> может принимать опциональный
  215. второй аргумент, содержащий значение по умолчанию,
  216. которое используется, если параметр не установлен или
  217. пустой. Его использование устраняет необходимость
  218. вызова <code>_hasParam()</code> до получения значения:
  219. </para>
  220. <programlisting language="php"><![CDATA[
  221. // Используется значение по умолчанию 1, если id не установлен
  222. $id = $this->_getParam('id', 1);
  223. // Вместо:
  224. if ($this->_hasParam('id') {
  225. $id = $this->_getParam('id');
  226. } else {
  227. $id = 1;
  228. }
  229. ]]></programlisting>
  230. </note>
  231. </listitem>
  232. </itemizedlist>
  233. </sect2>
  234. <sect2 id="zend.controller.action.viewintegration">
  235. <title>Интеграция вида</title>
  236. <note id="zend.controller.action.viewintegration.viewrenderer">
  237. <title>По умолчанию интеграция вида производится через ViewRenderer</title>
  238. <para>
  239. Изложенное в этом разделе действительно только в том случае,
  240. если вы явным образом отключили
  241. <link linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer</link>.
  242. Иначе вы можете спокойно пропустить этот раздел.
  243. </para>
  244. </note>
  245. <para>
  246. <classname>Zend_Controller_Action</classname> предоставляет простейший и
  247. гибкий механизм интеграции видов. Два метода осуществляют это:
  248. <code>initView()</code> и <code>render()</code>. Первый метод
  249. выполняет отложенную загрузку открытого свойства <varname>$view</varname>,
  250. второй выполняет рендеринг вида, основываясь на запрошенном в данный
  251. момент действии, используя иерархию директорий для определения пути
  252. к скрипту.
  253. </para>
  254. <sect3 id="zend.controller.action.viewintegration.initview">
  255. <title>Инициализация вида</title>
  256. <para>
  257. <code>initView()</code> инициализирует объект вида.
  258. <code>render()</code> вызывает <code>initView()</code> для
  259. извлечения объекта вида, но этот объект может быть
  260. инициализирован в любое время. По умолчанию
  261. <code>initView()</code> заполняет свойство <varname>$view</varname>
  262. объектом <classname>Zend_View</classname>, но может также использоваться
  263. любой класс, реализующий интерфейс
  264. <classname>Zend_View_Interface</classname>. Если <varname>$view</varname> уже
  265. инициализирован, то просто возвращается это свойство.
  266. </para>
  267. <para>
  268. Реализация, используемая по умолчанию, делает следующие
  269. предположения по структуре директорий:
  270. </para>
  271. <programlisting language="php"><![CDATA[
  272. applicationOrModule/
  273. controllers/
  274. IndexController.php
  275. views/
  276. scripts/
  277. index/
  278. index.phtml
  279. helpers/
  280. filters/
  281. ]]></programlisting>
  282. <para>
  283. Другими словами, предполагается, что скрипты вида находятся в
  284. поддиректории <code>views/scripts/</code> и поддиректория
  285. <code>views</code> должна содержать родственный функционал того
  286. же уровня (это могут быть помощники, фильтры). Когда
  287. определяется имя и путь к скрипту вида, то в качестве базового
  288. пути используется директория <code>views/scripts/</code>
  289. с директориями, именованными в соответствии с отдельными
  290. контроллерами, что дает иерархию скриптов вида.
  291. </para>
  292. </sect3>
  293. <sect3 id="zend.controller.action.viewintegration.render">
  294. <title>Рендеринг видов</title>
  295. <para>
  296. <code>render()</code> имеет следующую сигнатуру:
  297. </para>
  298. <programlisting language="php"><![CDATA[
  299. string render(string $action = null,
  300. string $name = null,
  301. bool $noController = false);
  302. ]]></programlisting>
  303. <para>
  304. <code>render()</code> рендерит скрипт вида. Если не были
  305. переданы аргументы, то предполагается, что запрашивается скрипт
  306. <code>[controller]/[action].phtml</code> (где
  307. <code>.phtml</code> - значение свойства
  308. <varname>$viewSuffix</varname>). Передача значения для
  309. <varname>$action</varname> вызовет генерацию этого шаблона в
  310. поддиректории <code>[controller]</code>. Для того, чтобы
  311. отменить использование поддиректории <code>[controller]</code>,
  312. передавайте значение true для <varname>$noController</varname>.
  313. Шаблоны рендерятся в объект ответа, если же вы хотите сохранить
  314. результат в
  315. <link linkend="zend.controller.response.namedsegments">именованный
  316. сегмент</link> объекта ответа, то передавайте значение для
  317. <varname>$name</varname>.
  318. </para>
  319. <note><para>
  320. Поскольку имена контроллера и действия могут содержать
  321. символы-ограничители слов, такие, как '_', '.', и '-', то
  322. <code>render()</code> нормализует их к '-', когда
  323. определяет имя скрипта.
  324. Внутри себя для такой нормализации он использует
  325. ограничители слов и путей для диспетчера. Таким образом,
  326. запрос к <code>/foo.bar/baz-bat</code> приведет к рендерингу
  327. скрипта <code>foo-bar/baz-bat.phtml</code>. Если ваш метод
  328. действия содержит camelCase, то следует иметь в виду, что
  329. при определении имени скрипта вида результатом будут
  330. разделенные '-' слова.
  331. </para></note>
  332. <para>
  333. Некоторые примеры:
  334. </para>
  335. <programlisting language="php"><![CDATA[
  336. class MyController extends Zend_Controller_Action
  337. {
  338. public function fooAction()
  339. {
  340. // Рендеринг my/foo.phtml
  341. $this->render();
  342. // Рендеринг my/bar.phtml
  343. $this->render('bar');
  344. // Рендеринг baz.phtml
  345. $this->render('baz', null, true);
  346. // Рендеринг my/login.phtml в сегмент 'form' объекта ответа
  347. $this->render('login', 'form');
  348. // Рендеринг site.phtml в сегмент 'page' объекта ответа,
  349. // при этом не используется поддиректория 'my/'
  350. $this->render('site', 'page', true);
  351. }
  352. public function bazBatAction()
  353. {
  354. // Рендеринг my/baz-bat.phtml
  355. $this->render();
  356. }
  357. }
  358. ]]></programlisting>
  359. </sect3>
  360. </sect2>
  361. <sect2 id="zend.controller.action.utilmethods">
  362. <title>Сервисные методы</title>
  363. <para>
  364. Кроме аксессоров и методов интеграции видов,
  365. <classname>Zend_Controller_Action</classname> имеет несколько сервисных
  366. методов для выполнения распространенных зачач в методах действий
  367. (или в методах pre- и post-dispatch).
  368. </para>
  369. <itemizedlist>
  370. <listitem>
  371. <para>
  372. <code>_forward($action, $controller = null, $module = null,
  373. array $params = null)</code>: выполяет другое действие.
  374. Если был вызван в <code>preDispatch()</code>, то
  375. запрошенноое в данный момент
  376. действие будет пропущено в пользу нового. Иначе
  377. действие, запрошенное в _forward(), будет выполнено после
  378. того, как было выполнено текущее действие.
  379. </para>
  380. </listitem>
  381. <listitem>
  382. <para>
  383. <code>_redirect($url, array $options =
  384. array())</code>: производит перенаправление по другому
  385. адресу. Этот метод принимает URL и опционально набор опций.
  386. По умолчанию он производит перенаправление HTTP 302.
  387. </para>
  388. <para>
  389. Опции могут включать в себя одну или более из следующих:
  390. </para>
  391. <itemizedlist>
  392. <listitem>
  393. <para>
  394. <emphasis>exit:</emphasis> производить или нет выход
  395. после этого. Если установлена, то будет произведены
  396. надлежащее закрытие всех открытых сессий и
  397. перенаправление.
  398. </para>
  399. <para>
  400. Вы можете установить эту опцию глобально в
  401. контроллере, используя аксессор
  402. <code>setRedirectExit()</code>.
  403. </para>
  404. </listitem>
  405. <listitem>
  406. <para>
  407. <emphasis>prependBase:</emphasis> добавлять или нет
  408. базовый URL из объекта запроса в начало данного URL.
  409. </para>
  410. <para>
  411. Вы можете установить эту опцию глобально в
  412. контроллере, используя аксессор
  413. <code>setRedirectPrependBase()</code>.
  414. </para>
  415. </listitem>
  416. <listitem>
  417. <para>
  418. <emphasis>code:</emphasis> какой код HTTP
  419. использовать при перенаправлении. По умолчанию
  420. используется HTTP 302. Могут использоваться любые
  421. коды от 301 до 306.
  422. </para>
  423. <para>
  424. Вы можете установить эту опцию глобально в
  425. контроллере, используя аксессор
  426. <code>setRedirectCode()</code>.
  427. </para>
  428. </listitem>
  429. </itemizedlist>
  430. </listitem>
  431. </itemizedlist>
  432. </sect2>
  433. <sect2 id="zend.controller.action.subclassing">
  434. <title>Создание подклассов контроллера действий</title>
  435. <para>
  436. Задумано, что в порядке создания контроллеров действий должны
  437. создаваться подклассы от <classname>Zend_Controller_Action</classname>.
  438. Как минимум, вам нужно будет определить методы действий, которые
  439. может вызывать контроллер.
  440. </para>
  441. <para>
  442. Помимо создания полезного функционала для своих веб-приложений, вы
  443. можете также обнаружить, что большинство установок или сервисных
  444. методов повторяются в ваших различных контроллерах. В этом случае
  445. создание общего базового контроллера, расширяющего
  446. <classname>Zend_Controller_Action</classname>, может решить проблему
  447. избыточности.
  448. </para>
  449. <example id="zend.controller.action.subclassing.example-call">
  450. <title>Обрабаботка обращений к несуществующим действиям</title>
  451. <para>
  452. Если сделан запрос к контроллеру, который содержит в себе
  453. не определенный в контроллере метод действия, то вызывается метод
  454. <code>Zend_Controller_Action::__call()</code>.
  455. <code>__call()</code> является магическим методом для перегрузки
  456. методов в PHP.
  457. </para>
  458. <para>
  459. По умолчанию этот метод бросает исключение
  460. <classname>Zend_Controller_Action_Exception</classname>, означающее, что
  461. требуемый метод не найден в контроллере. Если требуемый метод
  462. заканчивается строкой 'Action', то предполагается, что было
  463. запрошено действие и оно не существует; такая ошибка приводит к
  464. исключению с кодом 404. В остальных случаях бросается исключение
  465. с кодом 500. Это позволяет легко дифференцировать в обработчике
  466. ошибок случаи, когда страница не найдена, и когда произошла
  467. ошибка приложения.
  468. </para>
  469. <para>
  470. Например, если вы хотите выводить сообщение об ошибке, то можете
  471. написать нечто подобное:
  472. </para>
  473. <programlisting language="php"><![CDATA[
  474. class MyController extends Zend_Controller_Action
  475. {
  476. public function __call($method, $args)
  477. {
  478. if ('Action' == substr($method, -6)) {
  479. // Если метод действия не найден, то рендерится шаблон ошибки
  480. return $this->render('error');
  481. }
  482. // все другие методы бросают исключение
  483. throw new Exception('Invalid method "'
  484. . $method
  485. . '" called',
  486. 500);
  487. }
  488. }
  489. ]]></programlisting>
  490. <para>
  491. Другая возможность состоит в том, что вы можете
  492. производить переход на страницу контроллера по умолчанию:
  493. </para>
  494. <programlisting language="php"><![CDATA[
  495. class MyController extends Zend_Controller_Action
  496. {
  497. public function indexAction()
  498. {
  499. $this->render();
  500. }
  501. public function __call($method, $args)
  502. {
  503. if ('Action' == substr($method, -6)) {
  504. // Если метод действия не был найден, то производится
  505. // переход к действию index
  506. return $this->_forward('index');
  507. }
  508. // все другие методы бросают исключение
  509. throw new Exception('Invalid method "'
  510. . $method
  511. . '" called',
  512. 500);
  513. }
  514. }
  515. ]]></programlisting>
  516. </example>
  517. <para>
  518. Как и метод <code>__call()</code>, любые аксессоры,
  519. сервисные методы, методы инициализации, вида и перехвата, упомянутые
  520. ранее в этом разделе, могут быть переопределены для того, чтобы
  521. приспособить свои контроллеры под конкретные нужды. Например, если
  522. вы храните свои объекты вида в реестре, то можете модифицировать
  523. свой метод <code>initView()</code>:
  524. </para>
  525. <programlisting language="php"><![CDATA[
  526. abstract class My_Base_Controller extends Zend_Controller_Action
  527. {
  528. public function initView()
  529. {
  530. if (null === $this->view) {
  531. if (Zend_Registry::isRegistered('view')) {
  532. $this->view = Zend_Registry::get('view');
  533. } else {
  534. $this->view = new Zend_View();
  535. $this->view->setBasePath(dirname(__FILE__) . '/../views');
  536. }
  537. }
  538. return $this->view;
  539. }
  540. }
  541. ]]></programlisting>
  542. <para>
  543. Надеемся, из написанного в этом разделе вы смогли увидеть, насколько
  544. гибка эта компонента, и как можно заточить ее под нужды своего
  545. приложения или сайта.
  546. </para>
  547. </sect2>
  548. </sect1>
  549. <!--
  550. vim:se ts=4 sw=4 et:
  551. -->