| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!-- Reviewed: no -->
- <sect1 id="zend.controller.action">
- <title>Контроллеры действий</title>
- <sect2 id="zend.controller.action.introduction">
- <title>Введение</title>
- <para>
- <classname>Zend_Controller_Action</classname> - абстрактный класс,
- который можно использовать для реализации контроллеров действий
- для последующего их использования с фронт-контроллером при
- разработке сайта, основанного на паттерне Model-View-Controller
- (MVC).
- </para>
- <para>
- Для того, чтобы использовать <classname>Zend_Controller_Action</classname>,
- нужно создать его подкласс в своей действующей директории
- контроллеров (или расширить его для создания своего базового класса
- контроллеров действий). Работа с ним в основном сводится к
- созданию его подкласса и написании методов действий, соответствующих
- различным действиям, которые должен обрабатывать этот контроллер.
- Маршрутизатор и диспетчер компоненты <classname>Zend_Controller</classname>
- будут считать за методы действий все методы в классе
- контроллера с именем, заканчивающимся на 'Action'.
- </para>
- <para>
- Для примера предположим, что ваш класс определен следующим образом:
- </para>
- <programlisting language="php"><![CDATA[
- class FooController extends Zend_Controller_Action
- {
- public function barAction()
- {
- // делает что-нибудь
- }
- public function bazAction()
- {
- // делает что-нибудь
- }
- }
- ]]></programlisting>
- <para>
- Приведенный выше класс <code>FooController</code> (контроллер
- <code>foo</code>) определяет два действия - <code>bar</code> и
- <code>baz</code>.
- </para>
- <para>
- Класс может быть дополнен инициализирующим методом, методом
- действия по умолчанию (если не был вызван метод, либо
- вызван несуществующий метод), перехватчиками pre- и post-dispatch и
- различными вспомогательными методами. Этот раздел служит обзором
- функционала контроллера действий.
- </para>
- <note>
- <title>Поведение по умолчанию</title>
- <para>
- По умолчанию <link
- linkend="zend.controller.front">фронт-контроллер</link>
- активирует помощника действий <link
- linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer</link>.
- Этот помощник обеспечивает добавление объекта вида в контроллер
- и автоматический рендеринг видов. Вы можете отключить его в
- своем контроллере действия, используя один из следующих методов:
- </para>
- <programlisting language="php"><![CDATA[
- class FooController extends Zend_Controller_Action
- {
- public function init()
- {
- // Локально, только для данного контроллера:
- $this->_invokeArgs['noViewRenderer'] = true;
- // Глобально:
- $this->_helper->removeHelper('viewRenderer');
- // Тоже глобально, но должен использоваться вместе
- // с локальной версией для того, чтобы распространить
- // действие на данный контроллер:
- Zend_Controller_Front::getInstance()
- ->setParam('noViewRenderer', true);
- }
- }
- ]]></programlisting>
- <para>
- <code>initView()</code>, <code>getViewScript()</code>,
- <code>render()</code> и <code>renderScript()</code> служат
- посредниками для <code>ViewRenderer</code>, пока этот помощник
- находится в брокере помощников и не установлен флаг
- <code>noViewRenderer</code>.
- </para>
- <para>
- Вы можете также отключить рендеринг для отдельного вида
- посредством установки флага <code>noRender</code> в
- <code>ViewRenderer</code>:
- </para>
- <programlisting language="php"><![CDATA[
- class FooController extends Zend_Controller_Action
- {
- public function barAction()
- {
- // отключение авторендеринга для этого действия:
- $this->_helper->viewRenderer->setNoRender();
- }
- }
- ]]></programlisting>
- <para>
- Основные причины для отключения <code>ViewRenderer</code> - вам
- просто не нужен объект вида или если вы не производите рендеринг
- через скрипты вида (например, когда используется контроллер
- действий для обслуживания протоколов веб-сервисов, таких, как
- SOAP, XML-RPC, или REST). В большинстве случаев не
- нужно будет глобально отключать <code>ViewRenderer</code>,
- только избирательно в отдельных контроллерах или действиях.
- </para>
- </note>
- </sect2>
- <sect2 id="zend.controller.action.initialization">
- <title>Инициализация объекта</title>
- <para>
- Несмотря на то, что вы всегда можете переопределить конструктор
- контроллера действий, мы не рекомендуем делать это.
- <code>Zend_Controller_Action::__construct()</code>
- выполняет некоторые важные
- задачи, такие, как регистрация объектов запроса и ответа, аргументов
- вызова, переданных из фронт-контроллера. Если необходимо
- переопределить контроллер, то всегда вызывайте конструктор
- родительского класса <code>parent::__construct($request, $response,
- $invokeArgs)</code> в конструкторе подкласса.
- </para>
- <para>
- Более подходящим способом настройки инстанцирования
- является использование метода <code>init()</code>, который
- вызывается в конце выполнения <code>__construct()</code>. Например,
- если вы хотите устанавливать соединение с БД при инстанцировании:
- </para>
- <programlisting language="php"><![CDATA[
- class FooController extends Zend_Controller_Action
- {
- public function init()
- {
- $this->db = Zend_Db::factory('Pdo_Mysql', array(
- 'host' => 'myhost',
- 'username' => 'user',
- 'password' => 'XXXXXXX',
- 'dbname' => 'website'
- ));
- }
- }
- ]]></programlisting>
- </sect2>
- <sect2 id="zend.controller.action.prepostdispatch">
- <title>Перехватчики Pre- и Post-Dispatch</title>
- <para>
- <classname>Zend_Controller_Action</classname> определяет два метода, которые
- вызываются до и после требуемого действия,
- <code>preDispatch()</code> и <code>postDispatch()</code>. Они
- могут быть полезны в различных случаях - например, проверка
- аутентификации и списка управления доступом до запуска действия
- (при вызове метода <code>_forward()</code> в
- <code>preDispatch()</code> текущее действие будет пропущено) или
- размещение сгенерированного содержимого в шаблоне боковой части
- сайта (метод <code>postDispatch()</code>).
- </para>
- </sect2>
- <sect2 id="zend.controller.action.accessors">
- <title>Аксессоры</title>
- <para>
- С объектом контроллера регистрируется несколько объектов
- и переменных, они имеют свои методы-аксессоры.
- </para>
- <itemizedlist>
- <listitem><para>
- <emphasis>Объект запроса</emphasis>: через метод
- <code>getRequest()</code> извлекается объект запроса, который
- использовался для вызова данного действия.
- </para></listitem>
- <listitem>
- <para>
- <emphasis>Объект ответа</emphasis>:
- через метод <code>getResponse()</code> извлекается объект
- ответа, объединяющий в себе заголовки и содержимое ответа.
- Некоторые типичные вызовы могут выглядеть следующим образом:
- </para>
- <programlisting language="php"><![CDATA[
- $this->getResponse()->setHeader('Content-Type', 'text/xml');
- $this->getResponse()->appendBody($content);
- ]]></programlisting>
- </listitem>
- <listitem>
- <para>
- <emphasis>Аргументы вызова</emphasis>: фронт-контроллер
- может добавлять параметры в маршрутизатор, диспетчер и
- контроллер действий. Для их получения используйте
- <code>getInvokeArg($key)</code>, можно также извлечь весь список
- аргументов, используя метод <code>getInvokeArgs()</code>.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>Параметры запроса</emphasis>: Объект запроса
- заключает в себе параметры запроса, такие, как значения
- _GET, _POST, или пользовательские параметры, определенные в
- пути URL. Для их получения используйте
- <code>_getParam($key)</code> или
- <code>_getAllParams()</code>. Вы можете также установить
- параметры запроса, используя метод <code>_setParam()</code>,
- это полезно при перенаправлении на другие действия через
- метод <code>_forward()</code>.
- </para>
- <para>
- Для определения того, существует ли параметр или нет
- (полезно для логического ветвления), используйте
- <code>_hasParam($key)</code>.
- </para>
- <note>
- <para>
- <code>_getParam()</code> может принимать опциональный
- второй аргумент, содержащий значение по умолчанию,
- которое используется, если параметр не установлен или
- пустой. Его использование устраняет необходимость
- вызова <code>_hasParam()</code> до получения значения:
- </para>
- <programlisting language="php"><![CDATA[
- // Используется значение по умолчанию 1, если id не установлен
- $id = $this->_getParam('id', 1);
- // Вместо:
- if ($this->_hasParam('id') {
- $id = $this->_getParam('id');
- } else {
- $id = 1;
- }
- ]]></programlisting>
- </note>
- </listitem>
- </itemizedlist>
- </sect2>
- <sect2 id="zend.controller.action.viewintegration">
- <title>Интеграция вида</title>
- <note id="zend.controller.action.viewintegration.viewrenderer">
- <title>По умолчанию интеграция вида производится через ViewRenderer</title>
- <para>
- Изложенное в этом разделе действительно только в том случае,
- если вы явным образом отключили
- <link linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer</link>.
- Иначе вы можете спокойно пропустить этот раздел.
- </para>
- </note>
- <para>
- <classname>Zend_Controller_Action</classname> предоставляет простейший и
- гибкий механизм интеграции видов. Два метода осуществляют это:
- <code>initView()</code> и <code>render()</code>. Первый метод
- выполняет отложенную загрузку открытого свойства <varname>$view</varname>,
- второй выполняет рендеринг вида, основываясь на запрошенном в данный
- момент действии, используя иерархию директорий для определения пути
- к скрипту.
- </para>
- <sect3 id="zend.controller.action.viewintegration.initview">
- <title>Инициализация вида</title>
- <para>
- <code>initView()</code> инициализирует объект вида.
- <code>render()</code> вызывает <code>initView()</code> для
- извлечения объекта вида, но этот объект может быть
- инициализирован в любое время. По умолчанию
- <code>initView()</code> заполняет свойство <varname>$view</varname>
- объектом <classname>Zend_View</classname>, но может также использоваться
- любой класс, реализующий интерфейс
- <classname>Zend_View_Interface</classname>. Если <varname>$view</varname> уже
- инициализирован, то просто возвращается это свойство.
- </para>
- <para>
- Реализация, используемая по умолчанию, делает следующие
- предположения по структуре директорий:
- </para>
- <programlisting language="php"><![CDATA[
- applicationOrModule/
- controllers/
- IndexController.php
- views/
- scripts/
- index/
- index.phtml
- helpers/
- filters/
- ]]></programlisting>
- <para>
- Другими словами, предполагается, что скрипты вида находятся в
- поддиректории <code>views/scripts/</code> и поддиректория
- <code>views</code> должна содержать родственный функционал того
- же уровня (это могут быть помощники, фильтры). Когда
- определяется имя и путь к скрипту вида, то в качестве базового
- пути используется директория <code>views/scripts/</code>
- с директориями, именованными в соответствии с отдельными
- контроллерами, что дает иерархию скриптов вида.
- </para>
- </sect3>
- <sect3 id="zend.controller.action.viewintegration.render">
- <title>Рендеринг видов</title>
- <para>
- <code>render()</code> имеет следующую сигнатуру:
- </para>
- <programlisting language="php"><![CDATA[
- string render(string $action = null,
- string $name = null,
- bool $noController = false);
- ]]></programlisting>
- <para>
- <code>render()</code> рендерит скрипт вида. Если не были
- переданы аргументы, то предполагается, что запрашивается скрипт
- <code>[controller]/[action].phtml</code> (где
- <code>.phtml</code> - значение свойства
- <varname>$viewSuffix</varname>). Передача значения для
- <varname>$action</varname> вызовет генерацию этого шаблона в
- поддиректории <code>[controller]</code>. Для того, чтобы
- отменить использование поддиректории <code>[controller]</code>,
- передавайте значение true для <varname>$noController</varname>.
- Шаблоны рендерятся в объект ответа, если же вы хотите сохранить
- результат в
- <link linkend="zend.controller.response.namedsegments">именованный
- сегмент</link> объекта ответа, то передавайте значение для
- <varname>$name</varname>.
- </para>
- <note><para>
- Поскольку имена контроллера и действия могут содержать
- символы-ограничители слов, такие, как '_', '.', и '-', то
- <code>render()</code> нормализует их к '-', когда
- определяет имя скрипта.
- Внутри себя для такой нормализации он использует
- ограничители слов и путей для диспетчера. Таким образом,
- запрос к <code>/foo.bar/baz-bat</code> приведет к рендерингу
- скрипта <code>foo-bar/baz-bat.phtml</code>. Если ваш метод
- действия содержит camelCase, то следует иметь в виду, что
- при определении имени скрипта вида результатом будут
- разделенные '-' слова.
- </para></note>
- <para>
- Некоторые примеры:
- </para>
- <programlisting language="php"><![CDATA[
- class MyController extends Zend_Controller_Action
- {
- public function fooAction()
- {
- // Рендеринг my/foo.phtml
- $this->render();
- // Рендеринг my/bar.phtml
- $this->render('bar');
- // Рендеринг baz.phtml
- $this->render('baz', null, true);
- // Рендеринг my/login.phtml в сегмент 'form' объекта ответа
- $this->render('login', 'form');
- // Рендеринг site.phtml в сегмент 'page' объекта ответа,
- // при этом не используется поддиректория 'my/'
- $this->render('site', 'page', true);
- }
- public function bazBatAction()
- {
- // Рендеринг my/baz-bat.phtml
- $this->render();
- }
- }
- ]]></programlisting>
- </sect3>
- </sect2>
- <sect2 id="zend.controller.action.utilmethods">
- <title>Сервисные методы</title>
- <para>
- Кроме аксессоров и методов интеграции видов,
- <classname>Zend_Controller_Action</classname> имеет несколько сервисных
- методов для выполнения распространенных зачач в методах действий
- (или в методах pre- и post-dispatch).
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <code>_forward($action, $controller = null, $module = null,
- array $params = null)</code>: выполяет другое действие.
- Если был вызван в <code>preDispatch()</code>, то
- запрошенноое в данный момент
- действие будет пропущено в пользу нового. Иначе
- действие, запрошенное в _forward(), будет выполнено после
- того, как было выполнено текущее действие.
- </para>
- </listitem>
- <listitem>
- <para>
- <code>_redirect($url, array $options =
- array())</code>: производит перенаправление по другому
- адресу. Этот метод принимает URL и опционально набор опций.
- По умолчанию он производит перенаправление HTTP 302.
- </para>
- <para>
- Опции могут включать в себя одну или более из следующих:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>exit:</emphasis> производить или нет выход
- после этого. Если установлена, то будет произведены
- надлежащее закрытие всех открытых сессий и
- перенаправление.
- </para>
- <para>
- Вы можете установить эту опцию глобально в
- контроллере, используя аксессор
- <code>setRedirectExit()</code>.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>prependBase:</emphasis> добавлять или нет
- базовый URL из объекта запроса в начало данного URL.
- </para>
- <para>
- Вы можете установить эту опцию глобально в
- контроллере, используя аксессор
- <code>setRedirectPrependBase()</code>.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>code:</emphasis> какой код HTTP
- использовать при перенаправлении. По умолчанию
- используется HTTP 302. Могут использоваться любые
- коды от 301 до 306.
- </para>
- <para>
- Вы можете установить эту опцию глобально в
- контроллере, используя аксессор
- <code>setRedirectCode()</code>.
- </para>
- </listitem>
- </itemizedlist>
- </listitem>
- </itemizedlist>
- </sect2>
- <sect2 id="zend.controller.action.subclassing">
- <title>Создание подклассов контроллера действий</title>
- <para>
- Задумано, что в порядке создания контроллеров действий должны
- создаваться подклассы от <classname>Zend_Controller_Action</classname>.
- Как минимум, вам нужно будет определить методы действий, которые
- может вызывать контроллер.
- </para>
- <para>
- Помимо создания полезного функционала для своих веб-приложений, вы
- можете также обнаружить, что большинство установок или сервисных
- методов повторяются в ваших различных контроллерах. В этом случае
- создание общего базового контроллера, расширяющего
- <classname>Zend_Controller_Action</classname>, может решить проблему
- избыточности.
- </para>
- <example id="zend.controller.action.subclassing.example-call">
- <title>Обрабаботка обращений к несуществующим действиям</title>
- <para>
- Если сделан запрос к контроллеру, который содержит в себе
- не определенный в контроллере метод действия, то вызывается метод
- <code>Zend_Controller_Action::__call()</code>.
- <code>__call()</code> является магическим методом для перегрузки
- методов в PHP.
- </para>
- <para>
- По умолчанию этот метод бросает исключение
- <classname>Zend_Controller_Action_Exception</classname>, означающее, что
- требуемый метод не найден в контроллере. Если требуемый метод
- заканчивается строкой 'Action', то предполагается, что было
- запрошено действие и оно не существует; такая ошибка приводит к
- исключению с кодом 404. В остальных случаях бросается исключение
- с кодом 500. Это позволяет легко дифференцировать в обработчике
- ошибок случаи, когда страница не найдена, и когда произошла
- ошибка приложения.
- </para>
- <para>
- Например, если вы хотите выводить сообщение об ошибке, то можете
- написать нечто подобное:
- </para>
- <programlisting language="php"><![CDATA[
- class MyController extends Zend_Controller_Action
- {
- public function __call($method, $args)
- {
- if ('Action' == substr($method, -6)) {
- // Если метод действия не найден, то рендерится шаблон ошибки
- return $this->render('error');
- }
- // все другие методы бросают исключение
- throw new Exception('Invalid method "'
- . $method
- . '" called',
- 500);
- }
- }
- ]]></programlisting>
- <para>
- Другая возможность состоит в том, что вы можете
- производить переход на страницу контроллера по умолчанию:
- </para>
- <programlisting language="php"><![CDATA[
- class MyController extends Zend_Controller_Action
- {
- public function indexAction()
- {
- $this->render();
- }
- public function __call($method, $args)
- {
- if ('Action' == substr($method, -6)) {
- // Если метод действия не был найден, то производится
- // переход к действию index
- return $this->_forward('index');
- }
- // все другие методы бросают исключение
- throw new Exception('Invalid method "'
- . $method
- . '" called',
- 500);
- }
- }
- ]]></programlisting>
- </example>
- <para>
- Как и метод <code>__call()</code>, любые аксессоры,
- сервисные методы, методы инициализации, вида и перехвата, упомянутые
- ранее в этом разделе, могут быть переопределены для того, чтобы
- приспособить свои контроллеры под конкретные нужды. Например, если
- вы храните свои объекты вида в реестре, то можете модифицировать
- свой метод <code>initView()</code>:
- </para>
- <programlisting language="php"><![CDATA[
- abstract class My_Base_Controller extends Zend_Controller_Action
- {
- public function initView()
- {
- if (null === $this->view) {
- if (Zend_Registry::isRegistered('view')) {
- $this->view = Zend_Registry::get('view');
- } else {
- $this->view = new Zend_View();
- $this->view->setBasePath(dirname(__FILE__) . '/../views');
- }
- }
- return $this->view;
- }
- }
- ]]></programlisting>
- <para>
- Надеемся, из написанного в этом разделе вы смогли увидеть, насколько
- гибка эта компонента, и как можно заточить ее под нужды своего
- приложения или сайта.
- </para>
- </sect2>
- </sect1>
- <!--
- vim:se ts=4 sw=4 et:
- -->
|