Zend_Controller_Action
- абстрактный класс,
который можно использовать для реализации контроллеров действий
для последующего их использования со фронт-контроллером при
разработке сайта, основанного на паттерне Model-View-Controller
(MVC).
Для того, чтобы использовать Zend_Controller_Action
,
нужно создать его подкласс в своей действующей директории
контроллеров (или расширить его для создания своего базового класса
контроллеров действий). Работа с ним в основном сводится к
созданию его подкласса и написании методов действий, соответствующих
различным действиям, которые должен обрабатывать этот контроллер.
Маршрутизатор и диспетчер компоненты Zend_Controller
будут считать за методы действий все методы в классе
контроллера с именем, заканчивающимся на 'Action'.
Для примера предположим, что ваш класс определен следующим образом:
class FooController extends Zend_Controller_Action { public function barAction() { // делает что-нибудь } public function bazAction() { // делает что-нибудь } }
Приведенный выше класс FooController
(контроллер
foo
) определяет два действия - bar
и
baz
.
Класс может быть дополнен инициализирующим методом, методом действия по умолчанию (если не был вызван метод, либо вызван несуществующий метод), перехватчиками pre- и post-dispatch и различными вспомогательными методами. Этот раздел служит обзором функционала контроллера действий.
Поведение по умолчанию | |
---|---|
По умолчанию фронт-контроллер активирует помощника действий ViewRenderer. Этот помощник обеспечивает добавление объекта вида в контроллер и автоматический рендеринг видов. Вы можете отключить его в своем контроллере действия, используя один из следующих методов: <?php class FooController extends Zend_Controller_Action { public function init() { // Локально, только для данного контроллера: $this->_invokeArgs['noViewRenderer'] = true; // Глобально: $this->_helper->removeHelper('viewRenderer'); // Тоже глобально, но должен использоваться вместе с локальной версией // для того, чтобы распространить действие на данный контроллер: Zend_Controller_Front::getInstance()->setParam('noViewRenderer', true); } }
Вы можете также отключить рендеринг для отдельного вида
посредством установки флага <?php class FooController extends Zend_Controller_Action { public function barAction() { // отключение авторендеринга для этого действия: $this->_helper->viewRenderer->setNoRender(); } }
Основные причины для отключения |
Несмотря на то, что вы всегда можете переопределить конструктор
контроллера действий, мы не рекомендуем делать это.
Zend_Controller_Action::__construct() выполняет некоторые важные
задачи, такие, как регистрация объектов запроса и ответа, аргументов
вызова, переданных из фронт-контроллера. Если необходимо
переопределить контроллер, то всегда вызывайте конструктор
родительского класса parent::__construct($request, $response,
$invokeArgs)
в конструкторе подкласса.
Более подходящим способом настройки инстанцирования
является использование метода init()
, который
вызывается в конце выполнения __construct()
. Например,
если вы хотите устанавливать соединение с БД при инстанцировании:
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' )); } }
Zend_Controller_Action
определяет два метода, которые
вызываются до и после требуемого действия,
preDispatch()
и postDispatch()
. Они
могут быть полезны в различных случаях - например, проверка
аутентификации и списка управления доступом до запуска действия
(при вызове метода _forward()
в
preDispatch()
текущее действие будет пропущено) или
размещение сгенерированного содержимого в шаблоне боковой части
сайта (метод postDispatch()
).
С объектом контроллера регистрируется несколько объектов и переменных, они имеют свои методы-аксессоры.
Объект запроса: через метод
getRequest()
извлекается объект запроса, который использовался для вызова данного действия.-
Объект ответа: через метод
getResponse()
извлекается объект ответа, объединяющий в себе заголовки и содержимое ответа. Некоторые типичные вызовы могут выглядеть следующим образом:$this->getResponse()->setHeader('Content-Type', 'text/xml'); $this->getResponse()->appendBody($content);
Аргументы вызова: фронт-контроллер может добавлять параметры в маршрутизатор, диспетчер и контроллер действий. Для их получения используйте
getInvokeArg($key)
, можно также извлечь весь список аргументов, используя методgetInvokeArgs()
.-
Параметры запроса: Объект запроса заключает в себе параметры запроса, такие, как значения _GET, _POST, или пользовательские параметры, определенные в пути URL. Для их получения используйте
_getParam($key)
или_getAllParams()
. Вы можете также установить параметры запроса, используя метод_setParam()
, это полезно при перенаправлении на другие действия через метод_forward()
.Для определения того, существует ли параметр или нет (полезно для логического ветвления), используйте
_hasParam($key)
.Замечание _getParam()
может принимать опциональный второй аргумент, содержащий значение по умолчанию, которое используется, если параметр не установлен или пустой. Его использование устраняет необходимость вызова_hasParam()
до получения значения:<?php // Используется значение по умолчанию 1, если id не установлен $id = $this->_getParam('id', 1); // Вместо: if ($this->_hasParam('id') { $id = $this->_getParam('id'); } else { $id = 1; }
Zend_Controller_Action
предоставляет простейший и
гибкий механизм интеграции видов. Два метода осуществляют это:
initView()
и render()
. Первый метод
выполняет отложенную загрузку открытого свойства $view
,
второй выполняет рендеринг вида, основываясь на запрошенном в данный
момент действии, используя иерархию директорий для определения пути
к скрипту.
initView()
инициализирует объект вида.
render()
вызывает initView()
для
извлечения объекта вида, но этот объект может быть
инициализирован в любое время. По умолчанию
initView()
заполняет свойство $view
объектом Zend_View
, но может также использоваться
любой класс, реализующий интерфейс
Zend_View_Interface
. Если $view
уже
инициализирован, то просто возвращается это свойство.
Реализация, используемая по умолчанию, делает следующие предположения по структуре директорий:
applicationOrModule/ controllers/ IndexController.php views/ scripts/ index/ index.phtml helpers/ filters/
Другими словами, предполагается, что скрипты вида находятся в
поддиректории views/scripts/
и поддиректория
views
должна содержать родственный функционал того
же уровня (это могут быть помощники, фильтры). Когда
определяется имя и путь к скрипту вида, то в качестве базового
пути используется директория views/scripts/
с директориями, именованными в соответствии с отдельными
контроллерами, что дает иерархию скриптов вида.
render()
имеет следующую сигнатуру:
<?php string render(string $action = null, string $name = null, bool $noController = false);
render()
рендерит скрипт вида. Если не были
переданы аргументы, то предполагается, что запрашивается скрипт
[controller]/[action].phtml
(где
.phtml
- значение свойства
$viewSuffix
). Передача значения для
$action
вызовет генерацию этого шаблона в
поддиректории [controller]
. Для того, чтобы
отменить использование поддиректории [controller]
,
передавайте значение true для $noController
.
Шаблоны рендерятся в объект ответа, если же вы хотите сохранить
результат в
именованный
сегмент объекта ответа, то передавайте значение для
$name
.
Замечание | |
---|---|
Поскольку имена контроллера и действия могут содержать
символы-ограничители слов, такие, как '_', '.', и '-', то
render() нормализует их к '-', когда определяет имя скрипта.
Внутри себя для такой нормализации он использует
ограничители слов и путей для диспетчера. Таким образом,
запрос к |
Некоторые примеры:
<?php 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(); } }
Кроме аксессоров и методов интеграции видов,
Zend_Controller_Action
имеет несколько сервисных
методов для выполнения распространенных зачач в методах действий
(или в методах pre- и post-dispatch).
_forward($action, $controller = null, $module = null, array $params = null)
: выполяет другое действие. Если был вызван вpreDispatch()
, то запрошенноое в данный момент действие будет пропущено в пользу нового. Иначе действие, запрошенное в _forward(), будет выполнено после того, как было выполнено текущее действие.-
_redirect($url, array $options = array())
: производит перенаправление по другому адресу. Этот метод принимает URL и опционально набор опций. По умолчанию он производит перенаправление HTTP 302.Опции могут включать в себя одну или более из следующих:
-
exit: производить или нет выход после этого. Если установлена, то будет произведены надлежащее закрытие всех открытых сессий и перенаправление.
Вы можете установить эту опцию глобально в контроллере, используя аксессор
setRedirectExit()
. -
prependBase: добавлять или нет базовый URL из объекта запроса в начало данного URL.
Вы можете установить эту опцию глобально в контроллере, используя аксессор
setRedirectPrependBase()
. -
code: какой код HTTP использовать при перенаправлении. По умолчанию используется HTTP 302. Могут использоваться любые коды от 301 до 306.
Вы можете установить эту опцию глобально в контроллере, используя аксессор
setRedirectCode()
.
-
Задумано, что в порядке создания контроллеров действий должны
создаваться подклассы от Zend_Controller_Action
.
Как минимум, вам нужно будет определить методы действий, которые
может вызывать контроллер.
Помимо создания полезного функционала для своих веб-приложений, вы
можете также обнаружить, что большинство установок или сервисных
методов повторяются в ваших различных контроллерах. В этом случае
создание общего базового контроллера, расширяющего
Zend_Controller_Action
, может решить проблему
избыточности.
Пример 7.1. Как обрабатывать случаи несуществующих действий
Если сделан такой запрос к контроллеру, который содержит в себе
неопределенный в контроллере метод действия, то вызывается метод
Zend_Controller_Action::__call()
.
__call()
является магическим методом для перегрузки
методов в PHP.
По умолчанию этот метод бросает исключение
Zend_Controller_Action_Exception
, означающее, что
требуемый метод не найден в контроллере. Если требуемый метод
заканчивается строкой 'Action', то предполагается, что было
запрошено действие и оно не существует; такая ошибка приводит к
исключению с кодом 404. В остальных случаях бросается исключение
с кодом 500. Это позволяет легко дифференцировать в обработчике
ошибок случаи, когда страница не найдена, и когда произошла
ошибка приложения.
Например, если вы хотите выводить сообщение об ошибке, то можете написать нечто подобное:
<?php 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); } }
Другая возможность состоит в том, что вы можете производить переход на страницу контроллера по умолчанию:
<?php 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); } }
Как и метод __call()
, любые аксессоры,
сервисные методы, методы инициализации, вида и перехвата, упомянутые
ранее в этом разделе, могут быть переопределены для того, чтобы
приспособить свои контроллеры под конкретные нужды. Например, если
вы храните свои объекты вида в реестре, то можете модифицировать
свой метод initView()
:
<?php 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; } } }
Надеемся, из написанного в этом разделе вы смогли увидеть, насколько гибка эта компонента, и как можно заточить ее под нужды своего приложения или сайта.