Помощники действий в Zend Framework
Опубликовано: 14.05.2009
|
Содержание
- От переводчиков
- Вступление
- Основы
- Метод direct()
- Перехватчики событий
- Регистрация помощников с брокером
- Возвращение помощников из брокера, статическим образом
- Создание вашего собственного помощника
- Пример: Помощник Получения Формы
От переводчиков
Данный текст - перевод статьи
Вступление
Существует мнение, что
Основы
Многие статьи по ZF советуют создавать базовый класс, расширяющий Zend_Controller_Action, для обеспечения ваших контроллеров общими и часто используемыми функциями:
/**
* My_Controller_Action расширяется вашими контроллерами
*/
abstract class My_Controller_Action extends Zend_Controller_Action
{
// Здесь создаем свои методы…
}
Тем не менее, в этом не только нет необходимости, но и зачастую это не лучшее решение с точки зрения расширяемости. В будущем, вы можете обнаружить, что конкретным контроллерам нужны лишь некоторые методы из базового класса, или что вы постоянно добавляете новые методы, нужные только лишь нескольким контроллерам, раздувая при этом базовый класс.
Лучшим решением будет использование помощников действий. Помощники действий предназначены для обеспечения функционала, используемого в контроллерах действий по необходимости во время выполнения. Другими словами, вы можете использовать их, если вы в них нуждаетесь, но не загружать по умолчанию.
Работа помощников действия обеспечивается брокером. Zend_Controller_Action_HelperBroker содержит статитеческий регистр зарегистрированных помощников, а также служит фабрикой для загрузки помощников по требованию. Свойство класса Zend_Controller_Action - $_helper по умолчанию содержит экземпляр брокера.
При инициализации контроллера действий, инициализируется и брокер. Верно также и обратное, при инициализации брокера в нем регистрируется объект контроллера действий. Работая с помощниками вы получаете доступ к контроллеру, и, таким образом, можете взаимодействовать с ним. Поэтому, например, вы можете настроить публичные свойства или вызывать публичные методы в контроллере действия, как с помощью помощника, так и наоборот.
В основном, вы получаете ваш помощник, используя последнюю часть имени класса. Поэтому, например, если ваш помощник называется 'Foo_Helper_Bar', вам следует ссылаться на 'bar'. У вас есть 2 варианта для его вызова: через свойство брокера или с помощью метода getHelper():
$bar = $this->_helper->bar;
$bar = $this->_helper->getHelper('bar');
Однако, это лишь вершина айсберга.
Метод direct()
Помощники действий могут использовать паттерн Стратегию (
Один хороший пример лучше тысячи слов. Давайте взглянем на Url помощника, который возвращает URL, основанный на введенных данных.
$url = $this->_helper->url('bar', 'foo'); // "/foo/bar"
Применение метода direct() в помощниках действия - это самый легкий путь добавления виртуальных функций в ваши помощники.
Перехватчики событий
Если этого было недостаточно, то у помощников действий также есть несколько перехватчиков событий, которые помогают автоматизировать функциональность. Предусмотрено три перехватчика:
- init(): вызывается когда происходит инициализация контроллера действия (но только если в брокере уже содержится экземпляр помощника)
- preDispatch(): вызывается после операций preDispatch()-перехватчика плагина (но до операций preDispatch()-перехватчика контроллера действия) и только если в брокере уже содержится экземпляр помощника.
- postDispatch(): вызывается после операций postDispatch()-перехватчика контроллера действия (но до операций postDispatch()-перехватчика плагина) и только если в брокере уже содержится экземпляр помощника.
(Прим. переводчика: для лучшего понимания работы всех возможных перехватчиков в Zend Framework, вы можете взглянуть на графическую иллюстрацию в
Заметьте, в каждом из них есть обязательное условие: только если в брокере уже содержится экземпляр помощника. Как правило, вы будете загружать помощники по требованию, то есть, только когда они вам нужны. Однако, в некоторых случаях, вы, может быть, захотите добавить автоматизма схожего с плагинами, но со способностью анализа текущего контроллера. Тогда пригодятся перехватчики помощника действия.
Например, помощник действия
- init(): инициализирует объект вида, устанавливает соответствующие текущему контроллеру пути к скриптам, помощникам вида и фильтрам, и сохраняет ссылку на текущий объект вида в свойство 'view' контроллера;
- postDispatch(): в этом перехватчике определяется, нужно ли рендерить что-либо, и, если так, производит рендеринг скрипта вида, определяемого по текущему (или запрошенному) действию в установленном сегменте объекта ответа.
Другой пример - вы можете добавить в помощник действия перехватчик preDispatch(), который проверяет публичное свойство вашего контроллера действия для определения какие действия требуют аутентификацию и в случае совпадений производит редирект на форму "авторизации". Это лучше, чем использование стандартного плагина - такой метод позволяет вам хранить информацию о требованиях аутентификации контроллером, там где это непосредственно нужно.
Регистрация помощников с брокером
Если вы хотите использовать перехватчики, то вам нужно зарегистрировать ваши помощники заранее, как правило, в момент загрузки или в раннее запускаемом плагине. Чтобы сделать это, вам нужно зарегистрировать их с брокером:
Zend_Controller_Action_HelperBroker::addHelper(
new Foo_Helper_Bar()
);
Однако, есть другая причина регистрации с брокером - уверенность в том, что ваши собственные помощники будут найдены в любой момент. Для этого, вы можете просто сообщить брокеру префикс класса ваших помощников - тогда он будет знать, где искать их:
// Указываем префикс класса:
Zend_Controller_Action_HelperBroker::addPrefix('Foo_Helper');
// Другой способ, это предоставлять путь к классам с определенным
// префиксом, если они не находятся в include_path
Zend_Controller_Action_HelperBroker::addPath($path, 'Foo_Helper');
Добавление пути или префикса только говорит брокеру где искать файлы помощников - это не значит, что он инстанциирует их. Если вы хотите, чтобы помощник грузился до запуска цикла диспетчеризации, так, чтобы перехватчики событий могли быть использованы, вам всё ещё будет нужно добавить помощник в брокер или попытаться вызвать его (в этом случае в брокере будет создан экземпляр помощника). Рассмотрим это в следующем разделе.
Статическое возвращение помощников из брокера
В некоторых случаях, вам может понадобится использование помощника действия вне контроллера действия. Возможно - сконфигурировать его или потому что нужна его некоторая функциональность. Это позволяет сделать статический метод брокера getStaticHelper().
Например, мне часто бывает нужно сконфигурировать объект помощника ViewRenderer, для установки некоторых путей к видам по-умолчанию:
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer');
$viewRenderer->initView(); // убеждаемся, что объект вида иницилизован
$viewRenderer->view->addHelperPath($path); // устанавливаем путь, где могут находится помощники
Скорее всего, вам не слишком часто будет нужен этот функционал. Однако, это действительно удобно, так как в заданный момент времени в брокере может быть зарегистрирован только один экземпляр какого-то конкретного помощника, то вы можете быть уверены, что когда бы вы не конфигурировали ваши помощники, вы делаете это "глобально".
Создание вашего собственного помощника
Помощники действий должны наследовать класс Zend_Controller_Action_Helper_Abstract. Этот класс содержит следующие сервисные методы:
- setActionController(): устанавливает текущий контроллер действия;
- getActionController(): получает текущий контроллер действия;
- getFrontController(): получает экземпляр текущего фронт-контроллера;
- getRequest(): получает текущий объект ответа(использует вначале контроллер действия, который затем ищет во фронт-контроллере);
- getResponse(): получает текущий ответный объект (использует вначале контроллер действия, который затем ищет во фронт-контроллере)
- getName(): возвращает имя помощника;
К тому же, вы можете определить любые методы перехватчика событий, описанных ранее.
Ваш помощник действия может делать всё, что угодно. Если это будет действие общего характера, то вы можете захотеть реализовать его с помощью метода direct(), который был описан ранее. В другом случае - вы можете реализовывать всё так как сами захотите.
Пример: Помощник Получения Формы
Теперь, когда мы многое узнали о помощниках действий, давайте создадим свой. В нашем примере, скажем, у нас будет набор контроллеров, которые используют одну или несколько форм; кроме того, определенная форма может быть использована в нескольких контроллерах. Мы создадим помощник, который позволит вам извлекать форму по имени класса.
Предположим, что классы форм сохраняются в поддиректории «forms» текущего модуля. Мы также предположим, что они в одном пространстве имен с текущим модулем (если мы не в модуле по умолчанию), с прибавлением префикса «Form_»; Например, если у нас есть модуль новости - формы будут с префиксом «News_Form_ ». После этого мы используем имя, переданное помощнику, для определения какой класс формы должен был загружен, используя префикс. Главным образом мы будем использовать метод direct() для взаимодействия с помощником, от которого нам по-настоящему нужно только одно - мы хотим, чтобы помощник загружал формы.
/**
* Помощник действия для загрузки форм
*
* @uses Zend_Controller_Action_Helper_Abstract
*/
class My_Helper_FormLoader extends Zend_Controller_Action_Helper_Abstract
{
/**
* @var Zend_Loader_PluginLoader
*/
public $pluginLoader;
/**
* Конструктор: инициализирует плагин загрузки
*
* @return void
*/
public function __construct()
{
$this->pluginLoader = new Zend_Loader_PluginLoader();
}
/**
* Загружает форму с выбранными опциями
*
* @param string $name
* @param array|Zend_Config $options
* @return Zend_Form
*/
public function loadForm($name, $options = null)
{
$module = $this->getRequest()->getModuleName();
$front = $this->getFrontController();
$default = $front->getDispatcher()
->getDefaultModule();
if (empty($module)) {
$module = $default;
}
$moduleDirectory = $front->getControllerDirectory($module);
$formsDirectory = dirname($moduleDirectory) . '/forms';
$prefix = (('default' == $module) ? '' : ucfirst($module) . '_')
. 'Form_';
$this->pluginLoader->addPrefixPath($prefix, $formsDirectory);
$name = ucfirst((string) $name);
$formClass = $this->pluginLoader->load($name);
return new $formClass($options);
}
/**
* Паттерн Стратегии: вызываем помощник как метод брокера
*
* @param string $name
* @param array|Zend_Config $options
* @return Zend_Form
*/
public function direct($name, $options = null)
{
return $this->loadForm($name, $options);
}
}
Поместим этот код в файл с именем «FormLoader.php» в директорию «My/Helper/» вашей библиотеки (или любую директорию вашего include_path).
Как же мы это будем использовать? Предположим, что в LoginController в модуле по умолчанию, мы хотим загружать форму «login». Назовем ее «Form_Login» и поместим файл класса в «forms/Login.php» вашей директории приложений:
application/
controllers/
LoginController.php
forms/
Login.php - содержит класс 'Form_Login'
В загрузочном файле или ранне загружаемом плагине, мы должны сообщить брокеру, где найти помощники:
Zend_Controller_Action_HelperBroker::addPrefix('My_Helper');
После этого в нашем контроллере мы можем извлечь нашу форму, используя помощник:
$loginForm = $this->_helper->formLoader('login');
Кажется, что слишком много работы ради обычной загрузки формы? Однако, теперь вы можете использовать помощник FormLoader во всех контроллерах, где требуются формы. Например, у вас есть UserController и нужно получить форму регистрации:
$regForm = $this->_helper->formLoader('registration');
Кроме того, когда вы зарегистрируете префикс помощника (например, «My_Helper»), вы можете положить остальные помощники туда же, и они автоматически будут найдены брокером, у вас не будет дополнительных настроек помимо добавления префикса брокеру.
Смысл заключается в том, что помощники действия помогут вам избегать повторений в коде - вы помещаете фрагменты вашего кода, который предполагается к многократному использованию в контроллерах действий, в ваши помощники действий. Через некоторое время, у вас будет библиотека контроллеро-связанных функций, которую можно использовать для других проектов, не нуждающуюся в вашем собственном базовом классе для контроллеров действия, который по сути лишает вашу библиотеку гибкости и расширяемости.
Тема для обсуждения на форуме.
- Спрашивать почему у вас не работает код, для этого есть тема форума закрепленная за статьей.
- Спрашивать как реализовать ту или иную функциональность, для этого необходимо создать свою тему на форуме.
Комментарии для того чтобы: высказать свое аргументированное мнение о статье, указать какие участки вызывают непонимание, что нужно исправить/улучшить, просто сказать спасибо.
Комментарии имеют древовидную структуру.
Если вы хотите ответить на определенный комментарий - нажмите на ссылку "Ответить" возле этого комментария.