Введение в Zend Framework

 Learning Zend Framework

appendix

 Zend Framework Reference


  • Zend_Gdata
  • Zend_Http
  • Zend_InfoCard
  • Zend_Json
  • Zend_Layout
  • Zend_Ldap
  • Zend_Loader
  • Zend_Locale
  • Zend_Log
  • Zend_Mail
  • Zend_Markup
  • Zend_Measure
  • Zend_Memory
  • Zend_Mime
  • Zend_Navigation
  • Zend_Oauth
  • Zend_OpenId
  • Zend_Paginator
  • Zend_Pdf
  • Zend_ProgressBar
  • Zend_Queue
  • Zend_Reflection
  • Zend_Registry
  • Zend_Rest

  • Zend_Search_Lucene
  • Zend_Serializer
  • Zend_Server
  • Zend_Service
  • Zend_Session
  • Zend_Soap
  • Zend_Tag
  • Zend_Test
  • Zend_Text
  • Zend_TimeSync
  • Zend_Tool
  • Zend_Tool_Framework
  • Zend_Tool_Project
  • Zend_Translate
  • Zend_Uri
  • Zend_Validate
  • Zend_Version
  • Zend_View
  • Zend_Wildfire
  • Zend_XmlRpc
  • ZendX_Console_Process_Unix
  • ZendX_JQuery
  • Ttranslation 27.4% Update 2010-11-28 - Revision 23238 - Version ZF 1.11.x

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

    24.8.1. Введение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Zend_Controller_Action_HelperBroker::addHelper($helper);

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

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

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

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

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

    Внутри себя брокер помощников использует экземпляр PluginLoader для поддержки путей. Вы можете извлечь PluginLoader, используя статический метод getPluginLoader(), или добавить свой экземпляр PluginLoader, используя setPluginLoader().

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

    // Проверка, зарегистрирован ли помощник 'redirector' в брокере:
    if (Zend_Controller_Action_HelperBroker::hasHelper('redirector')) {
        echo 
    'Redirector helper registered';
    }

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

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

    // Проверка, зарегистрирован ли помощник '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 - короткое имя помощника без префикса:

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

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

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

    24.8.4.1. ActionStack

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

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

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

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

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

    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');
        }
    }

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

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

    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);
        }
    }

    24.8.4.2. AutoComplete

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

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

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

    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.

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

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

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

    // в контроллере действий:
    $this->_helper->autoCompleteDojo($data);

    Пример 24.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'.

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

    class TestController extends Zend_Controller_Action
    {
        protected 
    $_form;

        public function 
    getForm()
        {
            if (
    null === $this->_form) {
                
    $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' создает стрелку наподобие той, что присутствует в выпадающем списке, чтобы можно было выводить и убирать список соответствий.

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

    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 (
    === strpos($datum$match)) {
                    
    $matches[] = $datum;
                }
            }
            
    $this->_helper->autoCompleteDojo($matches);
        }
    }

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

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

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

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

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

    <?php // подключение javascript в HTML-заголовке, включая все
       // необходимые библиотеки для Dojo: 
    ?>
    <?php $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");
    <?php $this->headScript()->captureEnd() ?>

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

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


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

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

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

    24.8.4.3. ContextSwitch и AjaxContext

    Помощник действий ContextSwitch предназначен для облегчения возврата ответов в различных форматах. Помощник AjaxContext является специализированной версией ContextSwitch, которая облегчает возврат ответа на запросы, произведенные посредством XmlHttpRequest.

    Для того, чтобы включить использование какого-либо из этих помощников, нужно указать в своем контроллере, какие действия могут отвечать в каком контексте. Если входящий запрос означает допустимый контекст для данного действия, то помощник производит следующие действия:

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

    • Устанавливает альтернативный суффикс вида, это позволяет эффективно разделять скрипты видов для различных контекстов.

    • Отправляет HTTP-заголовки ответа, требуемые в данном контексте.

    • Опционально вызывает предопределенные функции обратного вызова для установки контекста и/или пост-обработки.

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

    class NewsController extends Zend_Controller_Action
    {
        
    /**
         * Начальная страница, производится переход к listAction()
         */
        
    public function indexAction()
        {
            
    $this->_forward('list');
        }

        
    /**
         * Выводит список новостей
         */
        
    public function listAction()
        {
        }

        
    /**
         * Просмотр одной новости
         */
        
    public function viewAction()
        {
        }
    }

    Предположим, нам нужно, чтобы действие listAction() было доступно и в формате XML. Вместо того, чтобы создавать новое действие, мы можем указать, что действие listAction() может возвращать и ответ в формате XML:

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

        
    // ...
    }

    Этим будет:

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

    • Изменено значение суффикса вида на 'xml.phtml' (или 'xml.[your suffix]', если вы используете другой суффикс вида).

    Теперь нужно создать новый скрипт вида - 'news/list.xml.phtml', в котором формируется код XML.

    Для определения того, нужно ли переключение контекста, помощник проверяет метку в объекте запроса. По умолчанию он проверяет параметр 'format', но это поведение может быть изменено. Это значит, что в большинстве случаев для того, чтобы инициировать переключение контекста, достаточно добавить параметр 'format' в запрос одним из двух способов:

    • Через параметр URL: /news/list/format/xml (напоминаем, используемый по умолчанию механизм маршрутизации разрешает добавление произвольных пар ключ/значение после имени действия в URL)

    • Через параметр GET: /news/list?format=xml

    ContextSwitch позволяет задавать любой контекст, включая используемый суффикс вида, отправляемые заголовки ответа и функции обратного вызова для инициализации и пост-обработки.

    24.8.4.3.1. Доступные по умолчанию контексты

    По умолчанию через помощник ContextSwitch используются два контекста: json и xml.

    • JSON. Контекст JSON устанавливает заголовок ответа 'Content-Type' в значение 'application/json' и суффикс скрипта вида в значение 'json.phtml'.

      Но по умолчанию использование скриптов вида не обязательно. Контекст просто сериализует все переменные вида и сразу возвращает ответ в формате JSON.

      Это поведение может быть отменено путем отключения автоматической сериализации JSON:

      $this->_helper->contextSwitch()->setAutoJsonSerialization(false);
    • XML. Контекст XML устанавливает заголовок ответа 'Content-Type' в значение 'text/xml' и суффикс скрипта вида в значение 'xml.phtml'. Для этого контекста нужно создавать скрипты вида.

    24.8.4.3.2. Создание своего контекста

    Иногда доступных по умолчанию контекстов может быть недостаточно. Например, нужно возвращать данные в формате YAML, сериализованный PHP, ленты RSS, ATOM и т.д. ContextSwitch позволяет добавлять новые контексты.

    Наиболее простым способом добавления нового контекста является использование метода addContext(). Этот метод принимает два аргумента - имя контекста и массив спецификации. Массив должен включать в себя один или более элементов из следующих:

    • suffix: суффикс, который должен добавляться перед суффиксом, зарегистрированным во ViewRenderer.

    • headers: массив пар заголовок/значение, которые требуется отправлять в ответе.

    • callbacks: массив, который содержит ключи 'init' и 'post' (один из них или оба). Ключи должны указывать на действующие функции обратного вызова, которые могут использоваться для инициализации и пост-обработки контекста.

      Вызов функции инициализации производится сразу после того, как контекст определен помощником ContextSwitch. Вы можете использовать его для выполнения произвольной логики. Например, контекст JSON использует функцию обратного вызова для отключения ViewRenderer, если включена автоматическая сериализация JSON.

      Вызов функции пост-обработки производится во время операции postDispatch() и может использоваться для выполнения произвольной логики. Например, контекст JSON использует функцию обратного вызова для определения того, включена ли автоматическая сериализация JSON; если включена, то помощник сериализует переменные вида в JSON и отправляет ответ, иначе включается ViewRenderer.

    Для взаимодействия с контекстом есть несколько методов:

    • addContext($context, array $spec): добавляет новый контекст. Бросает исключение, если контекст уже существует.

    • setContext($context, array $spec): добавляет новый контекст или переопределяет существующий. Использует ту же спецификацию, что и addContext().

    • addContexts(array $contexts): добавляет несколько контекстов одновременно. Массив $contexts должен содержать пары контекст/спецификация. Если какой-либо из контекстов уже существует, то бросается исключение.

    • setContexts(array $contexts): добавляет новые контексты и переопределяет существующие. Использует ту же спецификацию, что и addContexts().

    • hasContext($context): если контекст с этим именем уже существует, то метод возвращает true, иначе false.

    • getContext($context): возвращает контекст по его имени в виде массива, следующего спецификации, используемой в addContext().

    • getContexts(): возвращает все контексты в виде массива пар контекст/спецификация.

    • removeContext($context): удаляет контекст по его имени. Возвращает true в случае успеха, false - если контекст не найден.

    • clearContexts(): удаляет все контексты.

    24.8.4.3.3. Установка контекстов для действий

    Есть два способа установки доступных для действий контекстов. Вы можете либо вручную создавать массивы в своем контроллере, либо использовать несколько методов в ContextSwitch для "сборки" таких массивов.

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

    class FooController extends Zend_Controller_Action
    {
        public function 
    listAction()
        {
        }

        public function 
    viewAction()
        {
        }

        public function 
    commentsAction()
        {
        }

        public function 
    updateAction()
        {
        }
    }

    Предположим, что мы хотим добавить контекст XML к действию 'list', а к действию 'comments' - контексты XML и JSON. В этом случае мы можем добавить следующий код в метод init():

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

    Мы можем также просто определить свойство $contexts в контроллере:

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

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

    Этот способ менее трудоемкий, но заключает в себе больше потенциальных ошибок.

    Следующие методы могут использоваться для построения связей действие/контекст:

    • addActionContext($action, $context): помечает один или более контекстов как доступные для действия $action. Если связи уже существуют, то производится добавление к ним. $context может быть как одним контекстом, так и массивом контекстов.

      Если $context имеет значение TRUE, то все доступные в ContextSwitch контексты помечаются как доступные для действия.

      Пустое значение аргумента $context отключит все контексты для данного действия.

    • setActionContext($action, $context): помечает один или более контекстов как доступные для действия $action. Если связи уже существуют, то метод заменяет их указанными. $context может быть как одним контекстом, так и массивом контекстов.

    • addActionContexts(array $contexts): добавляет одновременно несколько пар действие/контекст. $contexts должен быть ассоциативным массивом пар действие/контекст. Использует метод addActionContext(), это означает, что если связи для данного действия уже существуют, то указанные в $contexts связи добавляются к ним.

    • setActionContexts(array $contexts): действует аналогично addActionContexts(), но переопределяет пары действие/контекст, если они уже существуют.

    • hasActionContext($action, $context): используется для определения того, имеет ли действие $action данный контекст.

    • getActionContexts($action = null): возвращает все контексты для действия $action или все пары действие/контекст.

    • removeActionContext($action, $context): удаляет один или более контекстов из действия $action. $context может быть как одним контекстом, так и массивом контекстов.

    • clearActionContexts($action = null): удаляет все контексты из действия $action или из всех действий с контекстами.

    24.8.4.3.4. Инициализация переключения контекста

    Для того, чтобы инициализировать переключение контекста, необходимо вызвать метод initContext() в контроллере действий:

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

    В некоторых случаях может потребоваться принудительное использование контекста - например, использовать только контекст XML, если переключение контекста включено. Вы можете осуществить это передачей контекста методу initContext():

    $contextSwitch->initContext('xml');
    24.8.4.3.5. Дополнительный функционал

    Для управления помощником ContextSwitch могут использоваться различные методы. Эти методы включают в себя:

    • setAutoJsonSerialization($flag): По умолчанию контекст JSON будет сериализовать все переменные вида согласно нотации JSON и возвращать их в качестве ответа. Если вы хотите самостоятельно формировать ответ, то должны отключить автоматическую сериализацию через данный метод; это нужно делать до вызова initContext().

      $contextSwitch->setAutoJsonSerialization(false);
      $contextSwitch->initContext();

      Вы можете получить значение этого флага через метод getAutoJsonSerialization().

    • setSuffix($context, $suffix, $prependViewRendererSuffix): Используя этот метод, вы можете задать другой суффикс для использования в контексте $context. Третий аргумент используется для указания того, добавлять ли или нет суффикс из помощника ViewRenderer к новому суффиксу; этот флаг установлен по умолчанию.

      Передача пустого значения в качестве суффикса приведет к тому, что будет использоваться только суффикс из ViewRenderer.

    • addHeader($context, $header, $content): Добавляет заголовок ответа для контекста $context. $header является именем заголовка, а $content - значением для этого заголовка.

      Любой контекст может иметь несколько заголовков. addHeader() добавляет дополнительные заголовки в стек заголовков для данного контекста.

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

    • setHeader($context, $header, $content): setHeader() действует аналогично addHeader() за исключением того, что позволяет переопределять существующие для данного контекста заголовки.

    • addHeaders($context, array $headers): Добавляет несколько заголовков к контексту $context. Использует addHeader(), поэтому если заголовок уже существует, то бросается исключение. $headers является массивом пар заголовок/значение.

    • setHeaders($context, array $headers.): действует аналогично addHeaders() за исключением того, что использует setHeader(), что позволяет перезаписывать существующие заголовки.

    • getHeader($context, $header): возвращает значение заголовка для данного контекста. Если заголовок не найден, то возвращается null.

    • removeHeader($context, $header): удаляет один заголовок для данного контекста.

    • clearHeaders($context): удаляет все заголовки для данного контекста.

    • setCallback($context, $trigger, $callback): устанавливает функцию обратного вызова для триггера $trigger и контекста $context. Триггерами могут быть 'init' или 'post' (выбор триггера определяет, когда будет вызвана функция - во время инициализации контекста или операции postDispatch()). $callback должен быть действующей PHP-функцией обратного вызова.

    • setCallbacks($context, array $callbacks): устанавливает несколько функций обратного вызова для данного контекста. $callbacks должен быть массивом пар триггер/функция обратного вызова. В действительности можно зарегистрировать максимум две функции обратного вызова, одна для инициализации, другая для пост-обработки.

    • getCallback($context, $trigger): возвращает функцию обратного вызова для триггера $trigger и контекста $context.

    • getCallbacks($context): возвращает все функции обратного вызова в виде массива пар триггер/функция обратного вызова.

    • removeCallback($context, $trigger): удаляет функцию обратного вызова для триггера $trigger и контекста $context.

    • clearCallbacks($context): удаляет все функции обратного вызова для данного контекста.

    • setContextParam($name): устанавливает параметр запроса, используемый для переключения контекста. По умолчанию установлено значение 'format', но его можно изменить с помощью данного аксессора.

      getContextParam() может использоваться для получения текущего значения.

    • setAutoDisableLayout($flag): По умолчанию макеты отключаются, когда производится переключение контекста. Это потому, что макеты, как правило, используются только с обычным выводом и их использование не имеет смысла для альтернативного контекста. Тем не менее, если вы хотите использовать макеты, то можете изменить это поведение, передав значение FALSE методу setAutoDisableLayout(). Вы должны делать это до вызова initContext().

      Для получения значения этого флага используйте аксессор getAutoDisableLayout().

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

    24.8.4.3.6. Функционал AjaxContext

    Помощник AjaxContext наследует от ContextSwitch, поэтому весь функционал, описанный для ContextSwitch, доступен и для него. Но есть несколько ключевых отличий.

    Во-первых, он использует другое свойство контроллера для определения контекстов, а именно $ajaxable. Поэтому вы можете одновременно использовать различные контексты для AJAX и обычных HTTP-запросов. Все методы *ActionContext*() помощника AjaxContext будут записывать в это свойство.

    Во-вторых, он будет запускаться только тогда, когда производится запрос XmlHttpRequest. Для определения того, был ли произведен запрос XmlHttpRequest, используется метод isXmlHttpRequest() объекта запроса. Таким образом, если параметр контекста ('format') был передан в запросе, но сам запрос был произведен не через XmlHttpRequest, то не будет произведено переключение контекста.

    И в-третьих, AjaxContext добавляет дополнительный контекст - HTML. В этом контексте он устанавливает суффикс в значение 'ajax.phtml' для отделения этого контекста от обычных запросов. Дополнительные заголовки не возвращаются.

    Пример 24.5. Установка действий для ответов на AJAX-запросы

    В следующем примере мы указываем, что действия 'view', 'form' и 'process' могут отвечать на AJAX-запросы. В действиях 'view' и 'form' мы будем возвращать куски HTML-кода для обновления страницы, в действии 'process' - данные в формате JSON.

    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()
        {
            
    // Извлекает один комментарий для просмотра
            // Если определен контекст AjaxContext, то используется
            // скрипт вида comment/view.ajax.phtml
        
    }

        public function 
    formAction()
        {
            
    // Рендерит форму для добавления нового комментария
            // Если определен контекст AjaxContext, то используется
            // скрипт вида comment/form.ajax.phtml
        
    }

        public function 
    processAction()
        {
            
    // Обрабатывает новый комментарий
            // Возвращает результат в формате JSON. Просто присвойте значения
            // переменным вида и эти значения будет возвращены в формате JSON.
        
    }
    }

    На клиентской стороне посредством AJAX-библиотеки производятся запросы к конечным точкам '/comment/view', '/comment/form', '/comment/process' и передается параметр 'format': '/comment/view/format/html', '/comment/form/format/html', '/comment/process/format/json' (можно также передавать параметр через строку запроса, например: "?format=json").

    Если ваша AJAX-библиотека передает заголовок 'X-Requested-With: XmlHttpRequest', то эти действия будут возвращать ответ в требуемом формате.


    24.8.4.4. FlashMessenger

    24.8.4.4.1. Введение

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

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

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

    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()
        {
            
    /**
             * используемый по умолчанию метод для получения экземпляра
             * Zend_Controller_Action_Helper_FlashMessenger
             */
            
    $this->_flashMessenger->addMessage('Record Saved!');
        }

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

    24.8.4.5. JSON

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

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

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

    • Опционально в качестве второго аргумента можно передавать массив опций для Zend_Json::encode(). Этот масив опций позволяет включать макеты и кодирование с использованием Zend_Json_Expr.

      $this->_helper->json($data, array('enableJsonExprFinder' => true));
    • Отключает ViewRenderer, если он включен.

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

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

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

    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 позволит оставить макеты включенными:

    $this->_helper->json($datatrue);

    Вы можете также передавать массив через второй параметр. Этот массив может содержать набор опций, включая опцию keepLayouts:

    $this->_helper->json($data, array('keepLayouts' => true);
    [Замечание] Включение кодирования через Zend_Json_Expr

    Zend_Json::encode() позволяет закодировывать "родные" выражения JSON с использованием объектов Zend_Json_Expr. Эта опция по умолчанию отключена. Для того, чтобы включить эту опцию, передавайте булево значение TRUE через опцию enableJsonExprFinder:

    $this->_helper->json($data, array('enableJsonExprFinder' => true);

    При использовании этой опции вы должны передавать массив в качестве второго параметра. Это также позволяет комбинировать опции друг с другом - например, с опцией keepLayouts. Все эти опции потом передаются Zend_Json::encode().

    $this->_helper->json($data, array(
        
    'enableJsonExprFinder' => true,
        
    'keepLayouts'          => true,
    ));

    24.8.4.6. Redirector

    24.8.4.6.1. Введение

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

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

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

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

    • setGotoSimple() может использоваться для установки URL, используемого по умолчанию, если методу gotoSimple() не был передан URL. Использует интерфейс Zend_Controller_Action::_forward(): setGotoSimple($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 есть различные методы, выполняющие текущие перенаправления:

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

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

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

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

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

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

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

    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)
                              ->
    setGotoSimple("this-action",
                                              
    "some-controller");
        }

        public function 
    myAction()
        {
            
    /* делаем что-то */

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

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

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

    // АЛЬТЕРНАТИВНЫЙ ПРИМЕР
    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;
        }

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

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

    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->gotoSimple('my-action',
                                           
    'my-controller',
                                           
    null,
                                           array(
    'param1' => 'test',
                                                 
    'param2' => 'test2'
                                                 
    )
                                           );
        }
    }

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

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

    $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.

    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'
            
    );
        }
    }

    24.8.4.7. ViewRenderer

    24.8.4.7.1. Введение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');

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

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

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

    // Класс контроллера, модуль 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';
        }
    }

    ...

    // в одном из ваших скриптов вида:
    $this->foo(); // вызывает 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().

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

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

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

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

    ...

    // Модуль '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 имитирует иерархию директорий контроллеров.

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

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

    // Класс контроллера 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 - автоматическая установка объекта вида.

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

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

    // Класс контроллера 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;
            }

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

    }

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

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

    // Класс контроллера 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');
        }
    }

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

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

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

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

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

    // Другая реализация вида
    $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);

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

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

    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 автоматически производит
            // рендеринг скрипта вида
        
    }
    }

    24.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(), что позволяет использовать помощника следующим образом:

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

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

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

    digg delicious meneame google twitter technorati facebook

    Comments

    Loading...