Zend_Controller-ActionController.xml 33 KB

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