ContextSwitch и AjaxContext Помощник действий ContextSwitch предназначен для облегчения возврата ответов в различных форматах. Помощник AjaxContext является специализированной версией ContextSwitch, которая облегчает возврат ответа на запросы, произведенные посредством XmlHttpRequest. Для того, чтобы включить использование какого-либо из этих помощников, нужно указать в своем контроллере, какие действия могут отвечать в каком контексте. Если входящий запрос означает допустимый контекст для данного действия, то помощник производит следующие действия: Выключает макеты, если они включены. Устанавливает альтернативный суффикс вида, это позволяет эффективно разделять скрипты видов для различных контекстов. Отправляет HTTP-заголовки ответа, требуемые в данном контексте. Опционально вызывает предопределенные функции обратного вызова для установки контекста и/или пост-обработки. В качестве примера рассмотрим следующий контроллер: _forward('list'); } /** * Выводит список новостей */ public function listAction() { } /** * Просмотр одной новости */ public function viewAction() { } } ]]> Предположим, нам нужно, чтобы действие listAction() было доступно и в формате XML. Вместо того, чтобы создавать новое действие, мы можем указать, что действие listAction() может возвращать и ответ в формате XML: _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 позволяет задавать любой контекст, включая используемый суффикс вида, отправляемые заголовки ответа и функции обратного вызова для инициализации и пост-обработки. Доступные по умолчанию контексты По умолчанию через помощник ContextSwitch используются два контекста: json и xml. JSON. Контекст JSON устанавливает заголовок ответа 'Content-Type' в значение 'application/json' и суффикс скрипта вида в значение 'json.phtml'. Но по умолчанию использование скриптов вида не обязательно. Контекст просто сериализует все переменные вида и сразу возвращает ответ в формате JSON. Это поведение может быть отменено путем отключения автоматической сериализации JSON: _helper->contextSwitch()->setAutoJsonSerialization(false); ]]> XML. Контекст XML устанавливает заголовок ответа 'Content-Type' в значение 'text/xml' и суффикс скрипта вида в значение 'xml.phtml'. Для этого контекста нужно создавать скрипты вида. Создание своего контекста Иногда доступных по умолчанию контекстов может быть недостаточно. Например, нужно возвращать данные в формате 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(): удаляет все контексты. Установка контекстов для действий Есть два способа установки доступных для действий контекстов. Вы можете либо вручную создавать массивы в своем контроллере, либо использовать несколько методов в ContextSwitch для "сборки" таких массивов. addActionContext() является основным методом для добавления связей действие/контекст. Он принимает два аргумента: действие, к которому добавляется контекст, и имя контекста (либо массив контекстов). Для примера рассмотрим следующий класс контроллера: Предположим, что мы хотим добавить контекст XML к действию 'list', а к действию 'comments' - контексты XML и JSON. В этом случае мы можем добавить следующий код в метод init(): _helper->contextSwitch() ->addActionContext('list', 'xml') ->addActionContext('comments', array('xml', 'json')) ->initContext(); } } ]]> Мы можем также просто определить свойство $contexts в контроллере: 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 или из всех действий с контекстами. Инициализация переключения контекста Для того, чтобы инициализировать переключение контекста, необходимо вызвать метод initContext() в контроллере действий: _helper->contextSwitch()->initContext(); } } ]]> В некоторых случаях может потребоваться принудительное использование контекста - например, использовать только контекст XML, если переключение контекста включено. Вы можете осуществить это передачей контекста методу initContext(): initContext('xml'); ]]> Дополнительный функционал Для управления помощником ContextSwitch могут использоваться различные методы. Эти методы включают в себя: setAutoJsonSerialization($flag): По умолчанию контекст JSON будет сериализовать все переменные вида согласно нотации JSON и возвращать их в качестве ответа. Если вы хотите самостоятельно формировать ответ, то должны отключить автоматическую сериализацию через данный метод; это нужно делать до вызова initContext(). 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(). Функционал AjaxContext Помощник AjaxContext наследует от ContextSwitch, поэтому весь функционал, описанный для ContextSwitch, доступен и для него. Но есть несколько ключевых отличий. Во-первых, он использует другое свойство контроллера для определения контекстов, а именно $ajaxable. Поэтому вы можете одновременно использовать различные контексты для AJAX и обычных HTTP-запросов. Все методы *ActionContext*() помощника AjaxContext будут записывать в это свойство. Во-вторых, он будет запускаться только тогда, когда производится запрос XmlHttpRequest. Для определения того, был ли произведен запрос XmlHttpRequest, используется метод isXmlHttpRequest() объекта запроса. Таким образом, если параметр контекста ('format') был передан в запросе, но сам запрос был произведен не через XmlHttpRequest, то не будет произведено переключение контекста. И в-третьих, AjaxContext добавляет дополнительный контекст - HTML. В этом контексте он устанавливает суффикс в значение 'ajax.phtml' для отделения этого контекста от обычных запросов. Дополнительные заголовки не возвращаются. Установка действий для ответов на AJAX-запросы В следующем примере мы указываем, что действия 'view', 'form' и 'process' могут отвечать на AJAX-запросы. В действиях 'view' и 'form' мы будем возвращать куски HTML-кода для обновления страницы, в действии 'process' - данные в формате JSON. _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', то эти действия будут возвращать ответ в требуемом формате.