7.8. Помощники действий

7.8.1. Введение

Помощники действий (action helpers) дают разработчикам возможность добавлять функционал во время выполнения или по требованию в любые контроллеры действий, которые наследуют от Zend_Controller_Action. Помощники действий помогают снизить необходимость в наследовании от абстрактного контроллера действий при добавлении общего функционала в контроллер действий.

Есть несколько вариантов использования помощников действий. Помощники действий используют брокерскую систему (brokerage system), подобную той, которая используется в Zend_View_Helper и Zend_Controller_Plugin. Помощники действий (как и Zend_View_Helper) могут быть загружены и вызваны по требованию, либо инстанцироваться во время запроса (начальной загрузки) или создания контроллера действий (init()). Для того, чтобы лучше разобраться с этим, см. ниже раздел по использованию.

7.8.2. Инициализация помощника

Помощник может быть инициализирован несколькими различными способами, выбор способа зависит от ваших нужд и от функционала, предоставляемого этим помощником.

Брокер помощников хранится как член $_helper класса Zend_Controller_Action; используйте брокер для получения или вызова помощников. Методы для этого включают в себя:

  • Явное использование метода getHelper(). Просто передайте ему имя, и будет возвращен объект помощника:

    <?php
    $flashMessenger = $this->_helper->getHelper('FlashMessenger');
    $flashMessenger->addMessage('We did something in the last request');
                    
  • Используйте функционал "волшебного" метода __get() брокера помощников - извлекайте помощника так же, как если бы он был свойством этого брокера:

    <?php
    $flashMessenger = $this->_helper->FlashMessenger;
    $flashMessenger->addMessage('We did something in the last request');
                    
  • И наконец, большинство помощников действий реализует метод direct(), который будет вызывать особый, используемый по умолчанию метод в помощнике. Например, в случае FlashMessenger будет вызван метод addMessage():

    <?php
    $this->_helper->FlashMessenger('We did something in the last request');
                    
[Замечание] Замечание

Все примеры выше функционально эквивалентны.

Вы можете также явно инстанцировать помощников. Вы можете захотеть сделать это, если используете помощника вне контроллера действий, или если хотите передавать помощника брокеру для использования в любых действиях. Инстанцирование производится так же, как и для любого другого класса PHP.

7.8.3. Брокер помощников

Zend_Controller_Action_HelperBroker управляет регистрацией объектов помощников и путей к помощникам, а также извлечением помощников по требованию.

Для того, чтобы зарегистрировать помощника через брокер, используйте addHelper:

<?php
Zend_Controller_Action_HelperBroker::addHelper($helper);
        

Само собой, инстанцирование и передача помощников брокеру отнимают некоторое время и ресурсы, поэтому существуют два метода для некоторой автоматизации: addPrefix() и addPath().

  • addPrefix() принимает префикс класса и использует его для определения пути, по которому определен класс помощника. Подразумевается, что префикс следует соглашениям по именованию классов Zend Framework-а.

    <?php
    // Добавление помощников, начинающихся с My_Action_Helpers в My/Action/Helpers/
    Zend_Controller_Action_HelperBroker::addPrefix('My_Action_Helpers');
                    
  • addPath() принимает директорию в качестве первого аргумента и префикс класса в качестве второго (по умолчанию это 'Zend_Controller_Action_Helper'). Это позволяет поставить в соответствие определенным директориям собственные префиксы классов.

    <?php
    // Добавление помощников, начинающихся с Helper в Plugins/Helpers/
    Zend_Controller_Action_HelperBroker::addPath('./Plugins/Helpers', 'Helper');
                    

Поскольку эти методы статические, то они могут вызываться из любого места в цепочке контроллеров для динамического добавления помощников при необходимости.

Для определения того, есть ли помощник в брокере, используйте hasHelper($name), где $name - короткое имя помощника (без префикса):

<?php
// Check if 'redirector' helper is registered with the broker:
if (Zend_Controller_Action_HelperBroker::hasHelper('redirector')) {
    echo 'Redirector helper registered';
}
        

Есть также два статических метода для извлечения помощников из брокера помощников: getExistingHelper() и getStaticHelper(). getExistingHelper() будет извлекать помощника только если он был ранее вызван или явно зарегистрирован через брокер помощников, иначе бросается исключение. getStaticHelper() делает то же самое, что и getExistingHelper(), за тем исключением, что будет пытаться инстанцировать помощника, если он еще не был зарегистрирован в стеке помощников. getStaticHelper() является хорошим выбором, если нужно извлечь помощника для конфигурирования.

Оба метода принимают единственный аргумент, $name, который является коротким именем помощника (без префикса).

<?php
// Проверка того, что помощник 'redirector' зарегистрирован в брокере, и его извлечение:
if (Zend_Controller_Action_HelperBroker::hasHelper('redirector')) {
    $redirector = Zend_Controller_Action_HelperBroker::getExistingHelper('redirector');
}

// Или просто извлеките его, не заботясь о том, был ли он ранее зарегистрирован:
$redirector = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
}

Наконец, для удаления зарегистрированного помощника из брокера используйте removeHelper($name), где $name - короткое имя помощника (без префикса):

<?php
// Удаление помощника 'redirector' из брокера, помещенное в условную конструкцию
if (Zend_Controller_Action_HelperBroker::hasHelper('redirector')) {
    Zend_Controller_Action_HelperBroker::removeHelper('redirector')
}
        

7.8.4. Встроенные помощники действий

Zend Framework уже содержит в себе набор помощников действий: AutoComplete автоматизирует ответы для автозавершения ввода с использованием AJAX; ContextSwitch и AjaxContext для обслуживания альтернативных форматов ответов для ваших действий; FlashMessenger для управления сессионными сообщениями; Json для кодирования и отправки ответов JSON; Redirector, предоставляющий различные реализации перенаправления из вашего приложения на внутренние и внешние страницы; и ViewRenderer, автоматизирующий процесс настройки объекта вида в контроллерах и рендеринга видов.

7.8.4.1. ActionStack

Помощник ActionStack позволяет помещать в стек запросы к плагину ActionStack фронт-контроллера, помогая эффективно создавать очереди действий, выполняемых в течение запроса. Этот помощник позволяет добавлять действия посредством установки новых объектов запросов или наборов действие/контроллер/модуль.

[Замечание] Вызов помощника ActionStack инициализирует плагин ActionStack

При вызове помощника ActionStack неявным образом регистрируется плагин ActionStack. Это значит, что нет необходимости явным образом регистрировать плагин ActionStack для того, чтобы использовать его функционал.

Пример 7.2. Добавление задачи с использованием имен действия, контроллера и модуля

Зачастую наиболее простым способом будет указание действия, контроллера и модуля (и необязательных параметров запроса), почти так же, как если бы вы вызывали Zend_Controller_Action::_forward():

<?php class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // Добавление двух действий в стек
        // Добавление вызова к /foo/baz/bar/baz
        // (FooController::bazAction() с переменной запроса bar == baz)
        $this->_helper->actionStack('baz', 'foo', 'default', array('bar' => 'baz'));

        // Добавление вызова к /bar/bat
        // (BarController::batAction())
        $this->_helper->actionStack('bat', 'bar');
    }
}
?>

Пример 7.3. Добавление задачи с использованием объекта запроса

Иногда имеет смысл использовать объект запроса, что более соответствует духу ООП. Объект запроса тоже можно передавать помощнику ActionStack.

<?php class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // Добавление двух действий в стек
        // Добавление вызова к /foo/baz/bar/baz
        // (FooController::bazAction() с переменной запроса bar == baz)
        $request = clone $this->getRequest();
        $request->setActionName('baz')              // не устанавливайте контроллер и
                ->setParams(array('bar' => 'baz')); // модуль; используются текущие значения
        $this->_helper->actionStack($request);

        // Add call to /bar/bat
        // (BarController::batAction())
        $request = clone $this->getRequest();
        $request->setActionName('bat')      // не устанавливайте модуль;
                ->setControllerName('bar'); // используется текущее значение
        $this->_helper->actionStack($request);
    }
}
?>

7.8.4.2. AutoComplete

Многие JavaScript-библиотеки для AJAX предоставляют функционал для автодополнения, с его помощью отображается список возможных соответствий в то время, пока пользователь набирает текст. Помощник AutoComplete предназначен для облегчения возврата допустимых ответов для функционала такого рода.

Поскольку не все JavaScript-библиотеки реализуют автодополнение одинаково, то помощник AutoComplete предоставляет в абстрактном классе некоторый базовый функционал, необходимый для большинства библиотек, и конкретные реализации для отдельных библиотек. Возвращаемые данные в основном - JSON-массивы строк, JSON-массивы массивов (в которых каждый массив-член является ассоциативным массивом метаданных, используемых при создании списка), либо HTML.

Базовое использование одинаково для всех реализаций:

<?php class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // Выполнение некоторой логики...

        // Кодирование и отправка ответа:
        $this->_helper->autoCompleteDojo($data);

        // То же самое явным образом:
        $response = $this->_helper->autoCompleteDojo->sendAutoCompletion($data);

        // Или просто подготовьте ответ для автодополнения:
        $response = $this->_helper->autoCompleteDojo->prepareAutoCompletion($data);
    }
}
?>

По умолчанию этот помощник делает следующее:

  • Отключает макеты и ViewRenderer.

  • Устанавливает необходимые заголовки ответа.

  • Устанавливает тело ответа с закодированными данными в нужном формате для автодополнения.

  • Отправляет ответ.

Доступные методы помощника включают в себя:

  • disableLayouts() может использоваться для отключения макетов и ViewRenderer. Обычно он вызывается в prepareAutoCompletion().

  • encodeJson($data, $keepLayouts = false) будет кодировать данные в формат JSON, при этом можно опционально включать или отключать макеты. Обычно этот метод вызывается в prepareAutoCompletion().

  • prepareAutoCompletion($data, $keepLayouts = false) используется для подготовки ответа в формате, необходимом для конкретной реализации, при этом можно опционально включать или отключать макеты. Возвращаемое значение может варьироваться в зависимости от используемой реализации.

  • sendAutoCompletion($data, $keepLayouts = false) используется для отправки данных в формате, необходимом для конкретной реализации. Он вызывает prepareAutoCompletion() и затем отправляет ответ.

  • direct($data, $sendNow = true, $keepLayouts = false) используется, когда помощник вызывается как метод брокера помощников. Флаг $sendNow используется для определения того, какой метод вызывать - sendAutoCompletion() или prepareAutoCompletion(). По умолчанию ответ отправляется сразу.

В настоящее время AutoComplete поддерживает AJAX-библиотеки Dojo и Scriptaculous.

7.8.4.2.1. Автодополнение c Dojo

В Dojo нет собственно виджета для автодополнения, но он имеет два виджета, которые могут производить автодополнение: ComboBox и FilteringSelect. В обоих случаях они требуют использования хранилища данных, который реализует QueryReadStore, читайте документацию по dojo.data для получения более подробной информации по этой теме.

В Zend Framework вы можете передавать простой индексный массив для помощника AutoCompleteDojo, и он будет возвращен в формате, пригодном для использования с хранилищем в Dojo:

<?php
// within a controller action:
$this->_helper->autoCompleteDojo($data);

Пример 7.4. Автодополнение с Dojo и Zend MVC

Автодополнение с Dojo через Zend MVC требует реализации нескольких вещей: генерация объекта формы для того ComboBox, для которого нужно реализовать автодополнение, действие контроллера для обслуживания автодополнения, создание своего QueryReadStore для связи с этим действием и генерация javascript-кода для инициализации автодополнения.

Для начала рассмотрим, что нужно сделать по части javascript. Dojo представляет собой полный фреймворк для создания объектно-ориентированного кода на языке JavaScript, почти так же, как Zend Framework на языке PHP. Он позволяет создавать псевдопространства имен, используя для этого иерархию директорий. Создадим директорию 'custom' на том же уровне, что и директория Dojo. В этой директории создадим файл TestNameReadStore.js со следующим содержимым:

dojo.provide("custom.TestNameReadStore");
dojo.declare("custom.TestNameReadStore", dojox.data.QueryReadStore, {
    fetch:function (request) {
        request.serverQuery = { test:request.query.name };
        return this.inherited("fetch", arguments);
    }
});

Этот класс просто наследует от класса QueryReadStore фреймворка Dojo, который сам по себе является абстрактным. Мы просто определяем метод, по которому производится запрос, и присваиваем его элементу 'test'.

Далее создадим элемент формы, для которого хотим реализовать автодополнение:

<?php class TestController extends Zend_Controller_Action
{
    protected $_form;

    public function getForm()
    {   
        if (null === $this->_form) {
            require_once 'Zend/Form.php';
            $this->_form = new Zend_Form();
            $this->_form->setMethod('get')
                ->setAction($this->getRequest()->getBaseUrl() . '/test/process')
                ->addElements(array(
                    'test' => array('type' => 'text', 'options' => array(
                        'filters'        => array('StringTrim'),
                        'dojoType'       => array('dijit.form.ComboBox'),
                        'store'          => 'testStore',
                        'autoComplete'   => 'false',
                        'hasDownArrow'   => 'true',
                        'label' => 'Your input:',
                    )),
                    'go' => array('type' => 'submit', 'options' => array('label' => 'Go!'))
                ));
        }
        return $this->_form;
    }
}

Здесь мы просто создаем форму с методами 'test' и 'go'. Метод 'test' добавляет несколько специальных, специфических для Dojo атрибутов: dojoType, store, autoComplete и hasDownArrow. Через 'dojoType' мы указываем, что создается ComboBox, он связан с хранилищем данных (ключ 'store') 'testStore'. Устанавливая 'autoComplete' в false, мы говорим Dojo, чтобы он не выбирал автоматически первое соответствие, а вместо этого показывал список соответсвий. Наконец, 'hasDownArrow' создает стрелку наподобие той, что присутствует в выпадающем списке, чтобы можно было выводить и убирать список соответствий.

Добавим метод для отображения формы и действие для обработки автодополнения:

<?php class TestController extends Zend_Controller_Action
{
    // ...

    /**
     * Страница с формой
     */
    public function indexAction()
    {
        $this->view->form = $this->getForm();
    }

    public function autocompleteAction()
    {
        if ('ajax' != $this->_getParam('format', false)) {
            return $this->_helper->redirector('index');
        }
        if ($this->getRequest()->isPost()) {
            return $this->_helper->redirector('index');
        }

        $match = trim($this->getRequest()->getQuery('test', ''));

        $matches = array();
        foreach ($this->getData() as $datum) {
            if (0 === strpos($datum, $match)) {
                $matches[] = $datum;
            }
        }
        $this->_helper->autoCompleteDojo($matches);
    }
}

В методе autocompleteAction() мы делаем несколько вещей. Сначала мы проверяем, был ли произведен POST-запрос и имеет ли параметр 'format' значение 'ajax', это помогает отсечь большинство ложных запросов. Далее мы получаем параметр 'test' и сравниваем его с нашими данными (здесь я намеренно опустил реализацию метода getData(), источники данных могут быть любыми). В конце выполнения передаем найденные соответствия помощнику AutoCompletion.

Теперь на очереди написание скрипта вида для страницы с формой. В нем нужно установить хранилище данных, вывести форму и подключить все необходимые библиотеки Dojo, включая наше хранилище данных. Ниже приведен скрипт вида с комментариями:

<? // установка хранилища данных: ?>
<div dojoType="custom.TestNameReadStore" jsId="testStore"
    url="<?= $this->baseUrl() ?>/unit-test/autocomplete/format/ajax" requestMethod="get"></div>

<? // рендеринг формы: ?>
<?= $this->form ?>

<? // подключение css для Dojo в HTML-заголовке: ?>
<? $this->headStyle()->captureStart() ?>
@import "<?= $this->baseUrl() ?>/javascript/dijit/themes/tundra/tundra.css";
@import "<?= $this->baseUrl() ?>/javascript/dojo/resources/dojo.css";
<? $this->headStyle()->captureEnd() ?>

<? // подключение javascript в HTML-заголовке, включая библиотеки для Dojo: ?>
<? $this->headScript()
        ->setAllowArbitraryAttributes(true)
        ->appendFile($this->baseUrl() . '/javascript/dojo/dojo.js', 
            'text/javascript', 
            array('djConfig' => 'parseOnLoad: true'))
        ->captureStart() ?>
djConfig.usePlainJson=true;
dojo.registerModulePath("custom","../custom");
dojo.require("dojo.parser");
dojo.require("dojox.data.QueryReadStore");
dojo.require("dijit.form.ComboBox");
dojo.require("custom.TestNameReadStore");
<? $this->headScript()->captureEnd() ?>

Обратите внимание на вызовы помощников видов, таких, как headStyle м headScript, - это метки заполнения, которые могут затем рендериться в HTML-заголовке скрипта макета.

Теперь у нас есть всё для того, чтобы автодополнение с Dojo заработало.


7.8.4.2.2. Автодополнение с Scriptaculous

Scriptaculous ожидает HTML-ответ в определенном формате.

С этой библиотекой используется помощник 'AutoCompleteScriptaculous'. Просто передавайте ему массив данных, и он создает ответ HTML, совместимый с Ajax.Autocompleter.

7.8.4.3. ContextSwitch and AjaxContext

The ContextSwitch action helper is intended for facilitating returning different response formats on request. The AjaxContext helper is a specialized version of ContextSwitch that facilitates returning responses to XmlHttpRequests.

To enable either one, you must provide hinting in your controller as to what actions can respond to which contexts. If an incoming request indicates a valid context for the given action, the helper will then:

  • Disable layouts, if enabled.

  • Set an alternate view suffix, effectively requiring a separate view script for the context.

  • Send approprite response headers for the context desired.

  • Optionally, call specified callbacks to setup the context and/or perform post-processing.

As an example, let's consider the following controller:

<?php class NewsController extends Zend_Controller_Action
{
    /**
     * Landing page; forwards to listAction()
     */
    public function indexAction()
    {
        $this->_forward('list');
    }

    /**
     * List news items
     */
    public function listAction()
    {
    }

    /**
     * View a news item
     */
    public function viewAction()
    {
    }
}
?>

Let's say that we want the listAction() to also be available in an XML format. Instead of creating a different action, we can hint that it can return an XML response:

<?php class NewsController extends Zend_Controller_Action
{
    public function init()
    {
        $contextSwitch = $this->_helper->getHelper('contextSwitch');
        $contextSwitch->addActionContext('list', 'xml')
                      ->initContext();
    }

    // ...
}
?>

What this will do is:

  • Set the 'Content-Type' response header to 'text/xml'.

  • Change the view suffix to 'xml.phtml' (or, if you use an alternate view suffix, 'xml.[your suffix]').

Now, you'll need to create a new view script, 'news/list.xml.phtml', which will create and render the XML.

To determine if a request should initiate a context switch, the helper checks for a token in the request object. By default, it looks for the 'format' parameter, though this may be configured. This means that, in most cases, to trigger a context switch, you can add a 'format' parameter to your request:

  • Via URL parameter: /news/list/format/xml (recall, the default routing schema allows for arbitrary key/value pairs following the action)

  • Via GET parameter: /news/list?format=xml

ContextSwitch allows you to specify arbitrary contexts, including what suffix change will occur (if any), any response headers that should be sent, and arbitrary callbacks for initialization and post processing.

7.8.4.3.1. Default Contexts Available

By default, two contexts are available to the ContextSwitch helper: json and xml.

  • JSON. The JSON context sets the 'Content-Type' response header to 'application/json', and the view script suffix to 'json.phtml'.

    By default, however, no view script is required. It will simply serialize all view variables, and emit the JSON response immediately.

    This behaviour can be disabled by turning off auto-JSON serialization:

    <?php
    $this->_helper->contextSwitch()->setAutoJsonSerialization(false);
    ?>
  • XML. The XML context sets the 'Content-Type' response header to 'text/xml', and the view script suffix to 'xml.phtml'. You will need to create a new view script for the context.

7.8.4.3.2. Creating Custom Contexts

Sometimes, the default contexts are not enough. For instance, you may wish to return YAML, or serialized PHP, an RSS or ATOM feed, etc. ContextSwitch allows you to do so.

The easiest way to add a new context is via the addContext() method. This method takes two arguments, the name of the context, and an array specification. The specification should include one or more of the following:

  • suffix: the suffix to prepend to the default view suffix as registered in the ViewRenderer.

  • headers: an array of header/value pairs you wish sent as part of the response.

  • callbacks: an array containing one or more of the keys 'init' or 'post', pointing to valid PHP callbacks that can be used for context initialization and post processing.

    Initialization callbacks occur when the context is detected by ContextSwitch. You can use it to perform arbitrary logic that should occur. As an example, the JSON context uses a callback to disable the ViewRenderer when auto-JSON serialization is on.

    Post processing occurs during the action's postDispatch() routine, and can be used to perform arbitrary logic. As an example, the JSON context uses a callback to determine if auto-JSON serialization is on; if so, it serializes the view variables to JSON and sends the response, but if not, it re-enables the ViewRenderer.

There are a variety of methods for interacting with contexts:

  • addContext($context, array $spec): add a new context. Throws an exception if the context already exists.

  • setContext($context, array $spec): add a new context or overwrite an existing context. Uses the same specification as addContext().

  • addContexts(array $contexts): add many contexts at once. The $contexts array should be an array of context/specification pairs. If any of the contexts already exists, it will throw an exception.

  • setContexts(array $contexts): add new contexts and overwrite existing ones. Uses the same specification as addContexts().

  • hasContext($context): returns true if the context exists, false otherwise.

  • getContext($context): retrieve a single context by name. Returns an array following the specification used in addContext().

  • getContexts(): retrieve all contexts. Returns an array of context/specification pairs.

  • removeContext($context): remove a single context by name. Returns true if successful, false if the context was not found.

  • clearContexts(): remove all contexts.

7.8.4.3.3. Setting Contexts Per Action

There are two mechanisms for setting available contexts. You can either manually create arrays in your controller, or use several methods in ContextSwitch to assemble them.

The principle method for adding action/context relations is addActionContext(). It expects two arguments, the action to which the context is being added, and either the name of a context or an array of contexts. As an example, consider the following controller class:

<?php class FooController extends Zend_Controller_Action
{
    public function listAction()
    {
    }

    public function viewAction()
    {
    }

    public function commentsAction()
    {
    }

    public function updateAction()
    {
    }
}
?>

Let's say we wanted to add an XML context to the 'list' action, and XML and JSON contexts to the 'comments' action. We could do so in the init() method:

<?php class FooController extends Zend_Controller_Action
{
    public function init()
    {
        $this->_helper->contextSwitch()
             ->addActionContext('list', 'xml')
             ->addActionContext('comments', array('xml', 'json'))
             ->initContext();
    }
}
?>

Alternately, you could simply define the array property $contexts:

<?php class FooController extends Zend_Controller_Action
{
    public $contexts = array(
        'list'     => array('xml'),
        'comments' => array('xml', 'json')
    );

    public function init()
    {
        $this->_helper->contextSwitch()->initContext();
    }
}
?>

The above is less overhead, but also prone to potential errors.

The following methods can be used to build the context mappings:

  • addActionContext($action, $context): marks one or more contexts as available to an action. If mappings already exists, simply appends to those mappings. $context may be a single context, or an array of contexts.

    A value of true for the context will mark all available contexts as available for the action.

    An empty value for $context will disable all contexts for the given action.

  • setActionContext($action, $context): marks one or more contexts as available to an action. If mappings already exists, it replaces them with those specified. $context may be a single context, or an array of contexts.

  • addActionContexts(array $contexts): add several action/context pairings at once. $contexts should be an associative array of action/context pairs. It proxies to addActionContext(), meaning that if pairings already exist, it appends to them.

  • setActionContexts(array $contexts): acts like addActionContexts(), but overwrites existing action/context pairs.

  • hasActionContext($action, $context): determine if a particular action has a give context.

  • getActionContexts($action = null): returns either all contexts for a given action, or all action/context pairs.

  • removeActionContext($action, $context): remove one or more contexts from a given action. $context may be a single context or an array of contexts.

  • clearActionContexts($action = null): remove all contexts from a given action, or from all actions with contexts.

7.8.4.3.4. Initializizing Context Switching

To initialize context switching, you need to call initContext() in your action controller:

<?php class NewsController extends Zend_Controller_Action
{
    public function init()
    {
        $this->_helper->contextSwitch()->initContext();
    }
}
?>

In some cases, you may want to force the context used; for instance, you may only want to allow the XML context if context switching is activated. You can do so by passing the context to initContext():

<?php
$contextSwitch->initContext('xml');
?>
7.8.4.3.5. Additional Functionality

A variety of methods can be used to alter the behaviour of the ContextSwitch helper. These include:

  • setAutoJsonSerialization($flag): By default, JSON contexts will serialize any view variables to JSON notation and return this as a response. If you wish to create your own response, you should turn this off; this needs to be done prior to the call to initContext().

    <?php
    $contextSwitch->setAutoJsonSerialization(false);
    $contextSwitch->initContext();
    ?>

    You can retrieve the value of the flag with getAutoJsonSerialization().

  • setSuffix($context, $suffix, $prependViewRendererSuffix): With this method, you can specify a different suffix to use for a given context. The third argument is used to indicate whether or not to prepend the current ViewRenderer suffix with the new suffix; this flag is enabled by default.

    Passing an empty value to the suffix will cause only the ViewRenderer suffix to be used.

  • addHeader($context, $header, $content): Add a response header for a given context. $header is the header name, and $content is the value to pass for that header.

    Each context can have multiple headers; addHeader() adds additional headers to the context's header stack.

    If the $header specified already exists for the context, an exception will be thrown.

  • setHeader($context, $header, $content): setHeader() acts just like addHeader(), except it allows you to overwrite existing context headers.

  • addHeaders($context, array $headers): Add multiple headers at once to a given context. Proxies to addHeader(), so if the header already exists, an exception will be thrown. $headers is an array of header/context pairs.

  • setHeaders($context, array $headers.): like addHeaders(), except it proxies to setHeader(), allowing you to overwrite existing headers.

  • getHeader($context, $header): retrieve the value of a header for a given context. Returns null if not found.

  • removeHeader($context, $header): remove a single header for a given context.

  • clearHeaders($context, $header): remove all headers for a given context.

  • setCallback($context, $trigger, $callback): set a callback at a given trigger for a given context. Triggers may be either 'init' or 'post' (indicating callback will be called at either context initialization or postDispatch). $callback should be a valid PHP callback.

  • setCallbacks($context, array $callbacks): set multiple callbacks for a given context. $callbacks should be trigger/callback pairs. In actuality, the most callbacks that can be registered are two, one for initialization and one for post processing.

  • getCallback($context, $trigger): retrieve a callback for a given trigger in a given context.

  • getCallbacks($context): retrieve all callbacks for a given context. Returns an array of trigger/callback pairs.

  • removeCallback($context, $trigger): remove a callback for a given trigger and context.

  • clearCallbacks($context): remove all callbacks for a given context.

  • setContextParam($name): set the request parameter to check when determining if a context switch has been requested. The value defaults to 'format', but this accessor can be used to set an alternate value.

    getContextParam() can be used to retrieve the current value.

  • setAutoDisableLayout($flag): By default, layouts are disabled when a context switch occurs; this is because typically layouts will only be used for returning normal responses, and have no meaning in alternate contexts. However, if you wish to use layouts (perhaps you may have a layout for the new context), you can change this behaviour by passing a true value to setAutoDisableLayout(). You should do this before calling initContext().

    To get the value of this flag, use the accessor getAutoDisableLayout().

  • getCurrentContext() can be used to determine what context was detected, if any. This returns null if no context switch occurred, or if called before initContext() has been invoked.

7.8.4.3.6. AjaxContext Functionality

The AjaxContext helper extends ContextSwitch, so all of the functionality listed for ContextSwitch is available to it. There are a few key differences, however.

First, it uses a different action controller property for determining contexts, $ajaxable. This is so you can have different contexts used for AJAX versus normal HTTP requests. The various *ActionContext*() methods of AjaxContext will write to this property.

Second, it will only trigger if an XmlHttpRequest has occurred, as determined by the request object's isXmlHttpRequest() method. Thus, if the context parameter ('format') is passed in the request, but the request was not made as an XmlHttpRequest, no context switch will trigger.

Third, AjaxContext adds an additional context, HTML. In this context, it sets the suffix to 'ajax.phtml' in order to differentiate the context from a normal request. No additional headers are returned.

Пример 7.5. Allowing Actions to Respond To Ajax Requests

In this following example, we're allowing requests to the actions 'view', 'form', and 'process' to respond to AJAX requests. In the first two cases, 'view' and 'form', we'll return HTML snippets with which to update the page; in the latter, we'll return JSON.

<?php class CommentController extends Zend_Controller_Action
{
    public function init()
    {
        $ajaxContext = $this->_helper->getHelper('AjaxContext');
        $ajaxContext->addActionContext('view', 'html')
                    ->addActionContext('form', 'html')
                    ->addActionContext('process', 'json')
                    ->initContext();
    }

    public function viewAction()
    {
        // Pull a single comment to view.
        // When AjaxContext detected, uses the comment/view.ajax.phtml 
        // view script.
    }

    public function formAction()
    {
        // Render the "add new comment" form.
        // When AjaxContext detected, uses the comment/form.ajax.phtml 
        // view script.
    }

    public function processAction()
    {
        // Process a new comment
        // Return the results as JSON; simply assign the results as view
        // variables, and JSON will be returned.
    }
}
?>

On the client end, your AJAX library will simply request the endpoints '/comment/view', '/comment/form', and '/comment/process', and pass the 'format' parameter: '/comment/view/format/html', '/comment/form/format/html', '/comment/process/format/json'. (Or you can pass the parameter via GET: e.g., "?format=json".)

Assuming your library passes the 'X-Requested-With: XmlHttpRequest' header, these actions will then return the appropriate response format.


7.8.4.4. FlashMessenger

7.8.4.4.1. Введение

Помощник FlashMessenger позволяет передавать сообщения, которые нужно отобразить пользователю при следующем запросе. Для хранения сообщений до следующего запроса FlashMessenger использует Zend_Session_Namespace . Как правило, лучше всего использовать тот Zend_Session или Zend_Session_Namespace, который вы инициализировали с помощью Zend_Session::start() в своем файле загрузки. (За более подробной информацией об использовании см. Zend Session).

7.8.4.4.2. Базовый пример использования

Пример использования ниже демонстрирует простейший случай использования мессенджера. Когда вызывается действие /some/my, оно добавляет мгновенное сообщение "Record Saved!". Последующий запрос к действию /some/my-next-request получит это сообщение (и удалит его).

<?php class SomeController extends Zend_Controller_Action
{
    /**
     * FlashMessenger
     *
     * @var Zend_Controller_Action_Helper_FlashMessenger
     */
    protected $_flashMessenger = null;
    
    public function init()
    {
        $this->_flashMessenger = $this->_helper->getHelper('FlashMessenger');
        $this->initView();
    }
    
    public function myAction()
    {
        /**
         * default method of getting Zend_Controller_Action_Helper_FlashMessenger
         * instance on-demand
         */
        $this->_flashMessenger->addMessage('Record Saved!');
    }

    public function myNextRequestAction()
    {
        $this->view->messages = $this->_flashMessenger->getMessages();
        $this->render();
    }
}
        

7.8.4.5. JSON

JSON быстро становится предпочтительным форматом для использования с AJAX-запросами, которые подразумевают ответы с данными. Синтаксический разбор JSON может производиться сразу на стороне клиента, что приводит к большей производительности.

Помощник действий JSON выполняет несколько функций:

  • Отключает макеты, если они включены.

  • Отключает ViewRenderer, если он включен.

  • Устанавливает заголовок ответа 'Content-Type' со значением 'application/json'.

  • По умолчанию сразу возвращает ответ, не дожидаясь завершения выполнения действия.

Использование помощника довольно простое - вызывайте его как метод брокера помощников или вызывайте один из его методов encodeJson() или sendJson():

<?php class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // произведение некоторых действий...

        // Отправка ответа JSON:
        $this->_helper->json($data);

        // или...
        $this->_helper->json->sendJson($data);

        // либо получение данных в формате json:
        $json = $this->_helper->json->encodeJson($data);
    }
}
?>
[Замечание] Использование с макетами

Для того, чтобы использовать отдельные макеты для JSON-ответов (например, для того, чтобы помещать JSON-ответы в некоторый контекст), все методы в помощнике JSON принимают второй опциональный параметр - флаг для включения и отключения макетов. Передача значения true сохранит макеты включенными:

<?php class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // Получение данных в формате json, макеты остаются включенными
        $json = $this->_helper->json->encodeJson($data, true);
    }
}
?>

7.8.4.6. Redirector

7.8.4.6.1. Введение

Помощник Redirector позволяет использовать объект Redirector для удовлетворения нужд в перенаправлении на новые URL. Он имеет многие преимущества по сравнению с методом _redirect(), такие, как возможность предварительной конфигурации поведения на стороне сайта в объекте Redirector или использование встроенного интерфейса goto($action, $controller, $module, $params), подобного интерфейсу Zend_Controller_Action::_forward().

Redirector имеет набор методов, которые могут использоваться для управления поведением при перенаправлении:

  • setCode() может использоваться для установки кода ответа HTTP, используемого при перенаправлении.

  • setExit() может использоваться для установки принудительного вызова exit() после перенаправления. По умолчанию он установлен в true.

  • setGoto() может использоваться для установки URL, используемого по умолчанию, если методу goto() не был передан URL. Использует интерфейс Zend_Controller_Action::_forward(): setgoto($action, $controller = null, $module = null, array $params = array());

  • setGotoRoute() может использоваться для установки URL, основываясь на зарегистрированном маршруте. Ему должен передаваться массив пар ключ/значение и имя маршрута, из них будет собран URL в соответствии с типом и определением маршрута.

  • setGotoUrl() может использоваться для установки URL, используемого по умолчанию, если методу gotoUrl() не был передан URL. Принимает единственную строку URL в качестве аргумента.

  • setPrependBase() может использоваться для добавления базового URL объекта запроса в начало URL, заданного через методы setGotoUrl(), gotoUrl(), или gotoUrlAndExit().

  • setUseAbsoluteUri() может использоваться для принуждения Redirector-а применять абсолютные URI при произведении перенаправления. Когда эта опция установлена, то используются значения $_SERVER['HTTP_HOST'], $_SERVER['SERVER_PORT'] и $_SERVER['HTTPS'] для формирования полного URI из URL, определенного одним из методов перенаправления. Эта опция по умолчанию отключена, но может быть включена по умолчанию в последующих релизах.

Кроме этого, в Redirector есть различные методы, выполняющие текущие перенаправления:

  • goto() использует setGoto() (интерфейс, подобный _forward()) для построения URL и перенаправления на него.

  • gotoRoute() использует setGotoRoute() (сборка маршрута) для построения URL и перенаправления на него.

  • gotoUrl() использует setGotoUrl() (строка URL) для построения URL и перенаправления на него.

Наконец, можно в любое время получить текущий URL для перенаправления, используя getRedirectUrl().

7.8.4.6.2. Базовые примеры использования

Пример 7.6. Опции настройки

В этом примере переопределяются несколько опций, включая код статуса HTTP, используемого при перенаправлении ('303'), и определение URL, используемое по умолчанию при перенаправлении.

<?php class SomeController extends Zend_Controller_Action
{
    /**
     * Редиректор - определен для полноты кода
     *
     * @var Zend_Controller_Action_Helper_Redirector
     */
    protected $_redirector = null;
    
    public function init()
    {
        $this->_redirector = $this->_helper->getHelper('Redirector');
        
        // Установка опций по умолчанию для редиректора
        // Поскольку объект зарегистрирован в брокере помощников, то эти опции
        // будут действительными для всех последующих действий
        $this->_redirector->setCode('303')
                          ->setExit(false)
                          ->setGoto("this-action", "some-controller");
    }
    
    public function myAction()
    {
        /* делаем что-то */

        // Перенаправление на ранее зарегистрированный URL и
        // принудительное завершение исполнения скрипта:
        $this->_redirector->redirectAndExit();
        return; // никогда не будет достигнуто
    }
}
}
            

Пример 7.7. Использование по умолчанию

Этот пример предполагает, что используются значения по умолчанию, это означает, что после любых перенаправлений будет производиться выход exit().

<?php
// АЛЬТЕРНАТИВНЫЙ ПРИМЕР class AlternativeController extends Zend_Controller_Action
{
    /**
     * Редиректор - определен для полноты кода
     *
     * @var Zend_Controller_Action_Helper_Redirector
     */
    protected $_redirector = null;
    
    public function init()
    {
        $this->_redirector = $this->_helper->getHelper('Redirector');
    }
    
    public function myAction()
    {
        /* делаем что-то */

        $this->_redirector->gotoUrl('/my-controller/my-action/param1/test/param2/test2');
        // это место никогда не будет достигнуто,
        // т.к. по умолчанию производится переход и завершение выполнения
        return; 
    }
}
            

Пример 7.8. Использование интерфейса _forward() для goto()

Метод goto() копирует интерфейс метода Zend_Controller_Action::_forward(). Основное отличие состоит в том, что он строит URL из переданных параметров и использует формат :module/:controller/:action/* маршрутизатора по умолчанию. Затем он производит перенаправление вместо добавления действия в цепочку.

<?php class ForwardController extends Zend_Controller_Action
{
    /**
     * Редиректор - определен для полноты кода
     *
     * @var Zend_Controller_Action_Helper_Redirector
     */
    protected $_redirector = null;
    
    public function init()
    {
        $this->_redirector = $this->_helper->getHelper('Redirector');
    }
    
    public function myAction()
    {
        /* Делаем что-то */

        // Перенаправление на действие 'my-action' контроллера 'my-controller'
        // в текущем модуле с использованием параметров param1 => test и
        // param2 => test2
        $this->_redirector->goto('my-action', 'my-controller', null, array('param1' => 'test', 'param2' => 'test2'));
    }
}
            

Пример 7.9. Использование маршрута с gotoRoute()

Следующий пример использует метод assemble() маршрута для создания URL, основанного на переданном ассоциативном массиве параметров. Этот пример предполагает, что был зарегистрирован следующий маршрут:

<?php
$route = new Zend_Controller_Router_Route(
    'blog/:year/:month/:day/:id',
    array('controller' => 'archive', 'module' => 'blog', 'action' => 'view')
);
$router->addRoute('blogArchive', $route);
            

При заданном массиве, в котором year (год), month (месяц), и day (день) установлены в 2006, 4 и 24 соответственно, будет построен URL /blog/2006/4/24/42.

<?php class BlogAdminController extends Zend_Controller_Action
{
    /**
     * Редиректор - определен для полноты кода
     *
     * @var Zend_Controller_Action_Helper_Redirector
     */
    protected $_redirector = null;
    
    public function init()
    {
        $this->_redirector = $this->_helper->getHelper('Redirector');
    }
    
    public function returnAction()
    {
        /* делаем что-то */

        // Перенаправление в архив блога. Строит URL
        // /blog/2006/4/24/42
        
        $this->_redirector->gotoRoute(
            array('year' => 2006, 'month' => 4, 'day' => 24, 'id' => 42),
            'blogArchive'
        );
    }
}
            

7.8.4.7. ViewRenderer

7.8.4.7.1. Введение

Помощник ViewRenderer предназначен для решения следующих задач:

  • Устранение необходимости инстанцирования объектов вида внутри контроллеров; объекты вида будут автоматически регистрироваться вместе с контроллером.

  • Автоматическая установка путей к скриптам вида, помощникам и фильтрам, основанная на текущем модуле, и автоматическое присоединение имени текущего модуля в качестве префикса имен классов помощников и фильтров.

  • Создание глобально доступного объекта вида для всех запускаемых контроллеров и действий.

  • Возможность устанавливать используемые по умолчанию опции рендеринга для всех контроллеров.

  • Возможность автоматического рендеринга скрипта вида, не требующего от разработчика каких-либо действий.

  • Возможность создавать собственные спецификации базового пути вида и путей к скриптам видов.

[Замечание] Замечание

Если вы вручную производите _forward(), перенаправление или render, то авторендеринг не будет произведен, поскольку выполнение любых этих операций говорит помощнику ViewRenderer, что вы определили свой собственный вывод.

[Замечание] Замечание

ViewRenderer включен по умолчанию. Вы можете отключить его через параметр фронт-контроллера noViewRenderer ($front->setParam('noViewRenderer', true)) или посредством удаления помощника из стека брокера помощников (Zend_Controller_Action_HelperBroker::removeHelper('viewRenderer')).

Если вы хотите изменить настройки ViewRenderer до начала диспетчеризации, то можете сделать это одним из двух способов:

  • Инстанцировать и зарегистрировать свой объект ViewRenderer, а затем передать его брокеру помощников:

    <?php
    $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
    $viewRenderer->setView($view)
                 ->setViewSuffix('php');
    Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
                        
  • Инициализировать и/или извлечь по запросу объект ViewRenderer через брокер помощников:

    <?php
    $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
    $viewRenderer->setView($view)
                 ->setViewSuffix('php');
                        
7.8.4.7.2. API

В простейшем варианте использования вы просто инстанцируете ViewRenderer и передаете его брокеру помощников. Наиболее легким способом его инстанцирования и регистрации является использование метода getStaticHelper() брокера помощников:

<?php
Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
        

Во время инстанцирования контроллера действий производится вызов ViewRenderer для инстанцирования объекта вида. Каждый раз, когда инстанцируется контроллер, вызывается метод init() помощника ViewRenderer, что приводит к установке свойства $view данного контроллера действий и вызову метода addScriptPath() с путем относительно текущего модуля; он будет вызван с префиксом класса, соответствующим имени текущего модуля, что эффективно разделяет пространства имен всех классов помощников и фильтров, определенных для этого модуля.

Каждый раз, когда вызывается postDispatch(), он будет вызывать render() для текущего действия.

В качестве примера рассмотрим следующий класс:

<?php
// Класс контроллера, модуль foo: class Foo_BarController extends Zend_Controller_Action
{
    // Рендеринг bar/index.phtml по умолчанию;
    // не требуется производить какие-либо операции
    public function indexAction()
    {
    }

    // Рендеринг bar/populate.phtml с переменной 'foo', установленной в 'bar'.
    // Поскольку объект вида определен в preDispatch(), то он всегда доступен.
    public function populateAction()
    {
        $this->view->foo = 'bar';
    }
}

...

// в одном из ваших скриптов вида:
<?php $this->foo(); // call Foo_View_Helper_Foo::foo()
        

ViewRenderer также определяет несколько аксессоров для того, чтобы можно было устанавливать и извлекать опции видов:

  • setView($view) позволяет установить объект вида для ViewRenderer. Объект сохраняется в открытом свойстве $view класса.

  • setNeverRender($flag = true) может использоваться для отключения или включения авторендеринга глобально, т.е. для всех контроллеров. Если установлен в true, то postDispatch() не будет автоматически вызывать render() в текущем контроллере. getNeverRender() возвращает текущее значение.

  • setNoRender($flag = true) может использоваться для отключения или включения авторендеринга. Если установлен в true, то postDispatch() не будет автоматически вызывать render() в текущем контроллере. Эта установка сбрасывается каждый раз во время вызова preDispatch() (т.е. нужно устанавливать этот флаг для каждого контроллера, для которого вы не хотите производить авторендеринг). getNoRender() возвращает текущее значение.

  • setNoController($flag = true) может использоваться для того, чтобы указать методу render(), чтобы он не искал скрипт вида в поддиректории с именем контроллера (что является поведением по умолчанию). getNoController() возвращает текущее значение.

  • setNeverController($flag = true) является аналогом setNoController(), но работает на глобальном уровне - т.е. он не будет сбрасываться с каждым обработанным действием. getNeverController() возвращает текущее значение.

  • setScriptAction($name) может использоваться для того, чтобы указать скрипт действия для рендеринга. $name должен быть именем скрипта без суффикса (и без поддиректории контроллера, за исключением того случая, когда включен noController). Если не задан, то ищется скрипт вида с именем, аналогичным имени действия в объекте запроса. getScriptAction() возвращает текущее значение.

  • setResponseSegment($name) может использоваться для указания того, в какой именованный сегмент объекта ответа следует сохранить результат рендеринга. Если не указан, то выходные данные сохраняются в сегменте, используемом по умолчанию. getResponseSegment() возвращает текущее значение.

  • initView($path, $prefix, $options может вызываться для указания базового пути вида, префикса классов помощников и фильтров, опций помощника ViewRenderer. Вы можете передавать любые из следующих флагов: neverRender, noRender, noController, scriptAction и responseSegment.

  • setRender($action = null, $name = null, $noController = false) позволяет установить scriptAction, responseSegment, или noController за один проход. direct() является псевдонимом для этого метода, что дает возможность легко вызывать этот метод из вашего контроллера.

    // Рендеринг 'foo' вместо текущего скрипта вида
    $this->_helper->viewRenderer('foo');
    
    // Рендеринг form.phtml в сегмент ответа 'html' в обход
    // поддиректории:
    $this->_helper->viewRenderer('form', 'html', true);
    
                    
    [Замечание] Замечание

    setRender() и direct() в действительности не производят рендеринг скрипта вида, а устанавливают закрытые свойства помощника, которые postDispatch() и render() будут использовать при рендеринге скрипта вида.

Конструктор позволяет опционально передать объект вида и опции ViewRenderer. Он использует те же флаги, что и initView():

$view    = new Zend_View(array('encoding' => 'UTF-8'));
$options = array('noController' => true, 'neverRender' => true);
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view, $options);

        

ViewRenderer имеет несколько дополнительных методов для создания пользовательских спецификаций пути, используемых для определения базового пути вида, добавляемого в объект вида, и пути к определенному скрипту вида, используемого при автоматическом определении скрипта вида для рендеринга. Все эти методы принимают одну или более меток заполнения:

  • :moduleDir ссылается на текущую базовую директорию модуля (по соглашению это директория, родительская по отношению к директории контроллеров модуля).

  • :module ссылается на имя текущего модуля.

  • :controller ссылается на имя текущего контроллера.

  • :action ссылается на имя текущего действия.

  • :suffix ссылается на суффикс скрипта вида (который может быть установлен через setViewSuffix()).

Методы для управления спецификациями пути:

  • setViewBasePathSpec($spec) позволяет изменить спецификацию пути, используемую для определения базового пути, добавляемого в объект вида. По умолчанию используется спецификация :moduleDir/views. Вы можете в любое время получить текущую спецификацицию, используя метод getViewBasePathSpec().

  • setViewScriptPathSpec($spec) позволяет изменить спецификацию пути, используемую для определения пути к отдельному скрипту вида (без базового пути скрипта вида). По умолчанию используется спецификация :controller/:action.:suffix. Вы можете в любое время получить текущую спецификацию, используя метод getViewScriptPathSpec().

  • setViewScriptPathNoControllerSpec($spec) позволяет изменить спецификацию пути, используемую для определения пути к отдельному скрипту вида, когда действует noController (без базового пути скрипта вида). По умолчанию используется спецификация :action.:suffix. Вы можете в любое время получить текущую спецификацию, используя метод getViewScriptPathNoControllerSpec().

Для более детального управления спецификациями путей вы можете использовать Zend_Filter_Inflector. Внутри себя ViewRenderer уже использует инфлектор для поиска соответствий. Для взаимодействия с инфлектором - установки своего собственного инфлектора или изменения используемого по умолчанию - могут использоваться следующие методы:

  • getInflector() возвращает инфлектор. Если в ViewRenderer его нет, то метод создает его, используя правила по умолчанию.

    По умолчанию он использует ссылки на статические правила для суффикса и директории модуля, так же, как и статическую цель. Это дает различным свойствам ViewRenderer возможность динамически изменять инфлектор.

  • setInflector($inflector, $reference) позволяет устанавливать свой инфлектор для использования с ViewRenderer. Если $reference равен true, то суффикс и директория модуля будут установлены как статические ссылки на свойства ViewRenderer, так же, как и цель.

[Замечание] Используемые по умолчанию соглашения по поиску

ViewRenderer производит некоторую нормализацию пути для облегчения поиска скрипта вида. Используемые по умолчанию правила:

  • :module: СловаНачинающиесяСЗаглавнойБуквы (CamelCase) разделяются тире и вся строка приводится к нижнему регистру. Например: "FooBarBaz" преобразуется в "foo-bar-baz".

    Внутри себя инфлектор использует фильтры Zend_Filter_Word_CamelCaseToDash и Zend_Filter_StringToLower.

  • :controller: СловаНачинающиесяСЗаглавнойБуквы (CamelCase) разделяются тире, знаки подчеркивания преобразуются в разделители директорий и вся строка приводится к нижнему регистру. Например: "FooBar" преобразуется в "foo-bar"; "FooBar_Admin" преобразуется в "foo-bar/admin".

    Внутри себя инфлектор использует фильтры Zend_Filter_Word_CamelCaseToDash, Zend_Filter_Word_UnderscoreToSeparator и Zend_Filter_StringToLower.

  • :action: СловаНачинающиесяСЗаглавнойБуквы (CamelCase) разделяются тире, символы, не являющиеся буквенно-цифровыми, переводятся в тире и вся строка приводится к нижнему регистру. Например: "fooBar" преобразуется в "foo-bar"; "foo-barBaz" преобразуется в "foo-bar-baz".

    Внутри себя инфлектор использует фильтры Zend_Filter_Word_CamelCaseToDash, Zend_Filter_PregReplace и Zend_Filter_StringToLower.

Последними рассматриваемыми элементами в API ViewRenderer-а являются методы для собственно определения путей к скриптам вида и рендеринга видов. Эти методы включают в себя:

  • renderScript($script, $name) позволяет производить рендеринг скрипта по указанному пути, в опционально заданный именованный сегмент. Если используется этот метод, то ViewRenderer не производит автоматическое определение имени скрипта, вместо этого он напрямую передает аргумент $script методу render() объекта вида.

    [Замечание] Замечание

    После того, как был произведен рендеринг вида в объект ответа, устанавливается noRender для предотвращения случайного повторного рендеринга того же скрипта вида.

    [Замечание] Замечание

    По умолчанию Zend_Controller_Action::renderScript() вызывает метод renderScript() помощника ViewRenderer.

  • getViewScript($action, $vars) создает путь к скрипту вида, основываясь на переданном действии $action и/или переменных, переданных в $vars. Этот массив может включать в себя ключи спецификаций пути ('moduleDir', 'module', 'controller', 'action' и 'suffix'). Если была передана переменная, то она будет использована, иначе будут использоваться значения из текущего запроса.

    getViewScript() будет использовать viewScriptPathSpec, либо viewScriptPathNoControllerSpec, в зависимости от значения флага noController.

    Разделители слов в именах модуля, контроллера или действия будут заменены на тире ('-'). Таким образом, если вы имеете контроллер с именем 'foo.bar' и действие 'baz:bat', то при использовании спецификации по умолчанию результатом будет путь 'foo-bar/baz-bat.phtml' к скрипту вида.

    [Замечание] Замечание

    По умолчанию Zend_Controller_Action::getViewScript() вызывает метод getViewScript() ViewRenderer-а.

  • render($action, $name, $noController) сначала проверяет, были ли переданы параметры $name или $noController, и если были переданы, то устанавливает соответствующие флаги (responseSegment и noController соответственно) в ViewRenderer. Затем он передает параметр $action (если есть) методу getViewScript(). Наконец, он передает полученный путь к скрипту вида методу renderScript().

    [Замечание] Замечание

    Следует помнить о побочных эффектах использования render(): значения, передаваемые для имени сегмента ответа и флага noController, сохраняются в объекте. Кроме этого, по окончании рендеринга будет установлен noRender.

    [Замечание] Замечание

    По умолчанию Zend_Controller_Action::render() вызывает метод render() помощника ViewRenderer.

  • renderBySpec($action, $vars, $name) позволяет передавать переменные спецификации пути для определения создаваемого пути к скрипту вида. Он передает $action и $vars методу getScriptPath(), затем передает полученный путь и $name методу renderScript().

7.8.4.7.3. Примеры базового использования

Пример 7.10. Базовое использование

В простейшем случае вы просто инициализируете и регистрируете помощник ViewRenderer через брокер помощников в своем файле загрузки и затем устанавливаете переменные в своих методах действий.

<?php
// В вашем файле загрузки:
Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');

...

<?php
// Модуль 'foo', контроллер 'bar': class Foo_BarController extends Zend_Controller_Action
{
    // По умолчанию производится рендеринг bar/index.phtml;
    // дополнительные действия не требуются
    public function indexAction()
    {
    }

    // Рендеринг bar/populate.phtml с переменной 'foo', установленной в 'bar'.
    // Поскольку объект вида был определен в preDispatch(), то он уже
    // доступен для использования.
    public function populateAction()
    {
        $this->view->foo = 'bar';
    }

    // Ничего не рендерится, т.к. производится переход на другое действие;
    // это другое действие может производить рендеринг
    public function bazAction()
    {
        $this->_forward('index');
    }

    // Ничего не рендерится, т.к. производится перенаправление по другому адресу
    public function batAction()
    {
        $this->_redirect('/index');
    }
}
            

[Замечание] Соглашения по именованию: Разделители слов в именах контроллера и действия

Если имена вашего контроллера и действия состоят из нескольких слов, то диспетчер требует, чтобы в URL они были разделены определенными символами-разделителями слов и путей. ViewRenderer при создании путей заменяет все найденные в имени контроллера разделители путей действующим разделителем путей ('/') и все разделители слов - чертой ('-'). Таким образом, вызов действия /foo.bar/baz.bat должен быть преобразован в вызов метода FooBarController::bazBatAction() в FooBarController.php, который в свою очередь произведет рендеринг скрипта вида foo-bar/baz-bat.phtml. Вызов действия /bar_baz/baz-bat должен быть преобразован в вызов Bar_BazController::bazBatAction() в Bar/BazController.php (обратите внимание на разделение путей), при этом производится рендеринг bar/baz/baz-bat.phtml.

Во втором примере обратите внимание на то, что по-прежнему используется модуль по умолчанию, но из-за наличия разделителя путей получается имя контроллера Bar_BazController в файле Bar/BazController.php. ViewRenderer имитирует иерархию директорий контроллеров.

Пример 7.11. Отключение авторендеринга

Может потребоваться отключить авторендеринг для некоторых действий или контроллеров - например, если вы хотите производить вывод другого типа (XML, JSON и т.д.), или просто не хотите ничего выводить. Есть два варианта - либо полностью отключить авторендеринг (setNeverRender()), либо отключить его для текущего действия (setNoRender()).

<?php
// Класс контроллера baz, модуль bar: class Bar_BazController extends Zend_Controller_Action
{
    public function fooAction()
    {
        // Не производить авторендеринг в этом действии
        $this->_helper->viewRenderer->setNoRender();
    }
}

// Класс контроллера bat, модуль bar: class Bar_BatController extends Zend_Controller_Action
{
    public function preDispatch()
    {
        // Не производить авторендеринг во всех действиях этого контроллера
        $this->_helper->viewRenderer->setNoRender();
    }
}
            

[Замечание] Замечание

В большинстве случаев не имеет смысла глобально отключать авторендеринг (через setNeverRender()), поскольку единственная выгода, которую вы получаете в этом случае от использования ViewRenderer - автоматическая установка объекта вида.

Пример 7.12. Выбор другого скрипта вида

В некоторых случаях требуется, чтобы производился рендеринг скрипта с именем, отличным от имени действия. Например, если у вас есть контроллер, который имеет методы действий для добавления и редактирования, они оба могут отображать один и тот же вид 'форма', хоть и с разным набором значений. Вы легко можете изменить имя скрипта, используя методы setScriptAction() и setRender(), или вызывая помощника как метод брокера - этим будет произведен вызов метода setRender().

<?php
// Класс контроллера bar, модуль foo: class Foo_BarController extends Zend_Controller_Action
{
    public function addAction()
    {
        // Рендерить 'bar/form.phtml' вместо 'bar/add.phtml'
        $this->_helper->viewRenderer('form');
    }

    public function editAction()
    {
        // Рендерить 'bar/form.phtml' вместо 'bar/edit.phtml'
        $this->_helper->viewRenderer->setScriptAction('form');
    }

    public function processAction()
    {
        // произведение валидации...
        if (!$valid) {
            // Рендерить 'bar/form.phtml' вместо 'bar/process.phtml'
            $this->_helper->viewRenderer->setRender('form');
            return;
        }

        // иначе продолжение обработки...
    }

}
            

Пример 7.13. Изменение зарегистрированного объекта вида

А что, если нужно модифицировать объект вида - например, изменить пути к помощникам или кодировку? Вы можете делать это как через модификацию объекта вида, установленного в вашем контроллере, так и через извлечение объекта вида из ViewRenderer, оба они являются ссылками на один и тот же объект.

<?php
// Класс контроллера bar, модуль foo: class Foo_BarController extends Zend_Controller_Action
{
    public function preDispatch()
    {
        // Изменение кодировки вида
        $this->view->setEncoding('UTF-8');
    }

    public function bazAction()
    {
        // Получение объекта вида и указание 'htmlspecialchars'
        // в качестве функции для экранирования
        $view = $this->_helper->viewRenderer->view;
        $view->setEscape('htmlspecialchars');
    }
}
            

7.8.4.7.4. Примеры продвинутого использования

Пример 7.14. Изменение спецификаций пути

В некоторых случаях вы можете решить, что спецификации пути, используемые по умолчанию, не соответствуют требованиям вашего сайта. Например, вы можете захотеть иметь одно дерево шаблонов, к которому можно давать доступ дизайнерам (что, например, довольно типично в случае использованя Smarty). В таком случае вы можете захотеть задать жесткую спецификацию базового пути вида и создать альтернативную спецификацию для собственно путей к скриптам вида.

В рамках данного примера предположим, что базовый путь к скриптам вида - '/opt/vendor/templates', и вы хотите, чтобы обращение к скриптам вида производилось по схеме ':moduleDir/:controller/:action.:suffix'. Также предположим, что если флаг noController установлен, то нужно, чтобы использовался верхний уровень вместо поддиректории (':action.:suffix'). И наконец, вы хотите использовать 'tpl' в качестве суффикса имени скрипта вида.

<?php
/**
 * В вашем файле загрузки:
 */

// Другая реализация вида
$view = new ZF_Smarty();

$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view);
$viewRenderer->setViewBasePathSpec('/opt/vendor/templates')
             ->setViewScriptPathSpec(':module/:controller/:action.:suffix')
             ->setViewScriptPathNoControllerSpec(':action.:suffix')
             ->setViewSuffix('tpl');
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
            

Пример 7.15. Рендеринг нескольких скриптов вида из одного действия

Иногда бывает нужно произвести рендеринг нескольких скриптов вида из одного действия. Решение довольно очевидное - просто сделайте несколько вызовов метода render():

<?php class SearchController extends Zend_Controller_Action
{
    public function resultsAction()
    {
        // Предполагается, что $this->model - текущая модель
        $this->view->results = $this->model->find($this->_getParam('query', '');

        // render() по умолчанию использует ViewRenderer
        // Рендеринг формы поиска и затем результатов поиска
        $this->render('form');
        $this->render('results');
    }

    public function formAction()
    {
        // Ничего не делается. ViewRenderer автоматически производит
        // рендеринг скрипта вида
    }
}
            

7.8.5. Написание собственных помощников

Помощники действий расширяют Zend_Controller_Action_Helper_Abstract, абстрактный класс, дающий базовый интерфейс и функционал, который требуется для использования с брокером помощников. Он включает в себя следующие методы:

  • setActionController() используется для установки текущего контроллера действий.

  • init(), запускаемый брокером при инстанцировании, может использоваться для запуска инициализации в помощнике. Это может быть полезным для переустановки состояния, когда несколько контроллеров используют один и тот же помощник в цепочке действий.

  • preDispatch() запускается до того, как будет запущено действие.

  • postDispatch() запускается, когда выполнение действия завершилось - даже если плагин preDispatch() пропустил это действие. Полезно в основном для очистки.

  • getRequest() возвращает текущий объект запроса.

  • getResponse() возвращает текущий объект ответа.

  • getName() возвращает имя помощника. Он извлекает ту часть имени класса, которая следует после последнего символа подчеркивания, иначе возвращается полное имя класса. Например, если класс называется Zend_Controller_Action_Helper_Redirector, то он вернет Redirector, а если класс называется FooMessage то он просто вернет свое полное имя.

Вы можете опционально добавить метод direct() в свой класс помощника. Если он определен, то это позволит вам обращаться к помощнику как к методу брокера помощников, этим обеспечивается легкое единовременное использование помощника. Например, redirector определяет direct() как псевдоним метода goto(), что позволяет использовать помощника следующим образом:

<?php
// Перенаправление на /blog/view/item/id/42
$this->_helper->redirector('item', 'view', 'blog', array('id' => 42));
        

Метод брокера помощников __call() ищет помощника с именем redirector, затем смотрит, имеет ли помощник определенный метод direct, и, если есть, вызывает его с переданными аргументами.

Создав собственный класс помощника, вы можете предоставить доступ к нему, как описано в разделах выше.

    Поддержать сайт на родительском проекте КГБ