1 <?xml version="1.0" encoding="UTF-8"?>
3 <sect3 id="zend.controller.actionhelpers.contextswitch">
4 <title>ContextSwitch и AjaxContext</title>
7 Помощник действий <code>ContextSwitch</code> предназначен для облегчения
8 возврата ответов в различных форматах.
9 Помощник <code>AjaxContext</code> является специализированной версией
10 <code>ContextSwitch</code>, которая облегчает возврат ответа на запросы,
11 произведенные посредством XmlHttpRequest.
15 Для того, чтобы включить использование какого-либо из этих помощников,
16 нужно указать в своем контроллере, какие действия могут отвечать в каком
17 контексте. Если входящий запрос означает допустимый контекст для данного
18 действия, то помощник производит следующие действия:
23 Выключает макеты, если они включены.
27 Устанавливает альтернативный суффикс вида, это позволяет эффективно
28 разделять скрипты видов для различных контекстов.
32 Отправляет HTTP-заголовки ответа, требуемые в данном контексте.
36 Опционально вызывает предопределенные функции обратного вызова для
37 установки контекста и/или пост-обработки.
42 В качестве примера рассмотрим следующий контроллер:
45 <programlisting language="php"><![CDATA[
46 class NewsController extends Zend_Controller_Action
49 * Начальная страница, производится переход к listAction()
51 public function indexAction()
53 $this->_forward('list');
57 * Выводит список новостей
59 public function listAction()
64 * Просмотр одной новости
66 public function viewAction()
73 Предположим, нам нужно, чтобы действие <code>listAction()</code> было
74 доступно и в формате XML. Вместо того, чтобы создавать новое действие,
75 мы можем указать, что действие <code>listAction()</code> может
76 возвращать и ответ в формате XML:
79 <programlisting language="php"><![CDATA[
80 class NewsController extends Zend_Controller_Action
82 public function init()
84 $contextSwitch = $this->_helper->getHelper('contextSwitch');
85 $contextSwitch->addActionContext('list', 'xml')
99 Установлен заголовок ответа 'Content-Type' со значением
104 Изменено значение суффикса вида на 'xml.phtml' (или
105 'xml.[your suffix]', если вы используете другой суффикс вида).
110 Теперь нужно создать новый скрипт вида - 'news/list.xml.phtml',
111 в котором формируется код XML.
115 Для определения того, нужно ли переключение контекста,
116 помощник проверяет метку в объекте запроса. По умолчанию он
117 проверяет параметр 'format', но это поведение может быть изменено.
118 Это значит, что в большинстве случаев для того, чтобы инициировать
119 переключение контекста, достаточно добавить параметр 'format' в
120 запрос одним из двух способов:
125 Через параметр URL: <code>/news/list/format/xml</code>
126 (напоминаем, используемый по умолчанию механизм маршрутизации
127 разрешает добавление произвольных пар ключ/значение после
128 имени действия в URL)
132 Через параметр GET: <code>/news/list?format=xml</code>
137 <code>ContextSwitch</code> позволяет задавать любой контекст, включая
138 используемый суффикс вида, отправляемые заголовки ответа и функции
139 обратного вызова для инициализации и пост-обработки.
142 <sect4 id="zend.controller.actionhelpers.contextswitch.contexts">
143 <title>Доступные по умолчанию контексты</title>
146 По умолчанию через помощник <code>ContextSwitch</code> используются
147 два контекста: json и xml.
153 <emphasis>JSON</emphasis>. Контекст JSON устанавливает
154 заголовок ответа 'Content-Type' в значение
155 'application/json' и суффикс скрипта вида в значение
160 Но по умолчанию использование скриптов вида не обязательно.
161 Контекст просто сериализует все переменные вида и сразу
162 возвращает ответ в формате JSON.
166 Это поведение может быть отменено путем отключения
167 автоматической сериализации JSON:
170 <programlisting language="php"><![CDATA[
171 $this->_helper->contextSwitch()->setAutoJsonSerialization(false);
177 <emphasis>XML</emphasis>. Контекст XML устанавливает
178 заголовок ответа 'Content-Type' в значение 'text/xml' и
179 суффикс скрипта вида в значение 'xml.phtml'. Для этого
180 контекста нужно создавать скрипты вида.
186 <sect4 id="zend.controller.actionhelpers.contextswitch.custom">
187 <title>Создание своего контекста</title>
190 Иногда доступных по умолчанию контекстов может быть недостаточно.
191 Например, нужно возвращать данные в формате YAML, сериализованный
192 PHP, ленты RSS, ATOM и т.д. <code>ContextSwitch</code> позволяет
193 добавлять новые контексты.
197 Наиболее простым способом добавления нового контекста является
198 использование метода <code>addContext()</code>. Этот метод
199 принимает два аргумента - имя контекста и массив спецификации.
200 Массив должен включать в себя один или более элементов
206 <para><emphasis>suffix</emphasis>: суффикс, который должен
207 добавляться перед суффиксом, зарегистрированным во
212 <para><emphasis>headers</emphasis>: массив пар
213 заголовок/значение, которые требуется отправлять в
218 <para><emphasis>callbacks</emphasis>: массив, который содержит
219 ключи 'init' и 'post' (один из них или оба).
220 Ключи должны указывать на действующие функции обратного вызова,
221 которые могут использоваться для инициализации и пост-обработки
224 <para>Вызов функции инициализации производится сразу после
225 того, как контекст определен помощником
226 <code>ContextSwitch</code>. Вы можете использовать его для
227 выполнения произвольной логики. Например, контекст JSON
228 использует функцию обратного вызова для отключения
229 ViewRenderer, если включена автоматическая сериализация
232 <para>Вызов функции пост-обработки производится во время
233 операции <code>postDispatch()</code> и может использоваться для
234 выполнения произвольной логики. Например, контекст JSON
235 использует функцию обратного вызова для определения того,
236 включена ли автоматическая сериализация JSON; если включена, то
237 помощник сериализует переменные вида в JSON и отправляет ответ,
238 иначе включается ViewRenderer.</para>
243 Для взаимодействия с контекстом есть несколько методов:
248 <code>addContext($context, array $spec)</code>: добавляет новый
249 контекст. Бросает исключение, если контекст уже существует.
253 <code>setContext($context, array $spec)</code>: добавляет новый
254 контекст или переопределяет существующий. Использует ту же
255 спецификацию, что и <code>addContext()</code>.
259 <code>addContexts(array $contexts)</code>: добавляет несколько
260 контекстов одновременно. Массив <varname>$contexts</varname> должен
261 содержать пары контекст/спецификация. Если какой-либо из
262 контекстов уже существует, то бросается исключение.
266 <code>setContexts(array $contexts)</code>: добавляет новые
267 контексты и переопределяет существующие. Использует ту же
268 спецификацию, что и <code>addContexts()</code>.
272 <code>hasContext($context)</code>: если контекст с этим именем
273 уже существует, то метод возвращает true, иначе false.
277 <code>getContext($context)</code>: возвращает контекст по его
278 имени в виде массива, следующего спецификации, используемой
279 в <code>addContext()</code>.
283 <code>getContexts()</code>: возвращает все контексты в виде
284 массива пар контекст/спецификация.
288 <code>removeContext($context)</code>: удаляет контекст
289 по его имени. Возвращает true в случае успеха, false - если
294 <code>clearContexts()</code>: удаляет все контексты.
299 <sect4 id="zend.controller.actionhelpers.contextswitch.actions">
300 <title>Установка контекстов для действий</title>
303 Есть два способа установки доступных для действий контекстов.
304 Вы можете либо вручную создавать массивы в своем контроллере, либо
305 использовать несколько методов в <code>ContextSwitch</code> для
306 "сборки" таких массивов.
310 <code>addActionContext()</code> является основным методом для
311 добавления связей действие/контекст. Он принимает два аргумента:
312 действие, к которому добавляется контекст, и имя контекста (либо
313 массив контекстов). Для примера рассмотрим следующий класс
317 <programlisting language="php"><![CDATA[
318 class FooController extends Zend_Controller_Action
320 public function listAction()
324 public function viewAction()
328 public function commentsAction()
332 public function updateAction()
339 Предположим, что мы хотим добавить контекст XML к действию 'list',
340 а к действию 'comments' - контексты XML и JSON. В этом случае мы
341 можем добавить следующий код в метод <code>init()</code>:
344 <programlisting language="php"><![CDATA[
345 class FooController extends Zend_Controller_Action
347 public function init()
349 $this->_helper->contextSwitch()
350 ->addActionContext('list', 'xml')
351 ->addActionContext('comments', array('xml', 'json'))
358 Мы можем также просто определить свойство <varname>$contexts</varname>
362 <programlisting language="php"><![CDATA[
363 class FooController extends Zend_Controller_Action
365 public $contexts = array(
366 'list' => array('xml'),
367 'comments' => array('xml', 'json')
370 public function init()
372 $this->_helper->contextSwitch()->initContext();
378 Этот способ менее трудоемкий, но заключает в себе больше
379 потенциальных ошибок.
383 Следующие методы могут использоваться для построения связей
390 <code>addActionContext($action, $context)</code>: помечает
391 один или более контекстов как доступные для действия
392 $action. Если связи уже существуют, то производится
393 добавление к ним. <varname>$context</varname> может быть как
394 одним контекстом, так и массивом контекстов.
398 Если <varname>$context</varname> имеет значение <constant>TRUE</constant>,
399 то все доступные в <code>ContextSwitch</code> контексты
400 помечаются как доступные для действия.
404 Пустое значение аргумента <varname>$context</varname> отключит
405 все контексты для данного действия.
410 <code>setActionContext($action, $context)</code>: помечает
411 один или более контекстов как доступные для действия
412 $action. Если связи уже существуют, то метод заменяет их
414 <varname>$context</varname> может быть как одним контекстом, так и
419 <code>addActionContexts(array $contexts)</code>: добавляет
420 одновременно несколько пар действие/контекст.
421 <varname>$contexts</varname> должен быть ассоциативным массивом
422 пар действие/контекст. Использует метод
423 <code>addActionContext()</code>, это означает, что если
424 связи для данного действия уже существуют, то указанные в
425 <varname>$contexts</varname> связи добавляются к ним.
429 <code>setActionContexts(array $contexts)</code>: действует
430 аналогично <code>addActionContexts()</code>, но
431 переопределяет пары действие/контекст, если они уже
436 <code>hasActionContext($action, $context)</code>:
437 используется для определения того, имеет ли действие $action
442 <code>getActionContexts($action = null)</code>: возвращает
443 все контексты для действия $action или все пары
448 <code>removeActionContext($action, $context)</code>:
449 удаляет один или более контекстов из действия $action.
450 <varname>$context</varname> может быть как
451 одним контекстом, так и массивом контекстов.
455 <code>clearActionContexts($action = null)</code>: удаляет
456 все контексты из действия $action или из всех
457 действий с контекстами.
462 <sect4 id="zend.controller.actionhelpers.contextswitch.initcontext">
463 <title>Инициализация переключения контекста</title>
466 Для того, чтобы инициализировать переключение контекста, необходимо
467 вызвать метод <code>initContext()</code> в контроллере действий:
470 <programlisting language="php"><![CDATA[
471 class NewsController extends Zend_Controller_Action
473 public function init()
475 $this->_helper->contextSwitch()->initContext();
481 В некоторых случаях может потребоваться принудительное использование
482 контекста - например, использовать только контекст XML, если
483 переключение контекста включено. Вы можете осуществить это
484 передачей контекста методу <code>initContext()</code>:
487 <programlisting language="php"><![CDATA[
488 $contextSwitch->initContext('xml');
492 <sect4 id="zend.controller.actionhelpers.contextswitch.misc">
493 <title>Дополнительный функционал</title>
496 Для управления помощником <code>ContextSwitch</code> могут
497 использоваться различные методы. Эти методы включают в себя:
503 <code>setAutoJsonSerialization($flag)</code>: По умолчанию
504 контекст JSON будет сериализовать все переменные вида
505 согласно нотации JSON и возвращать их в качестве ответа.
506 Если вы хотите самостоятельно формировать ответ, то должны
507 отключить автоматическую сериализацию через данный метод;
508 это нужно делать до вызова <code>initContext()</code>.
511 <programlisting language="php"><![CDATA[
512 $contextSwitch->setAutoJsonSerialization(false);
513 $contextSwitch->initContext();
517 Вы можете получить значение этого флага через метод
518 <code>getAutoJsonSerialization()</code>.
524 <code>setSuffix($context, $suffix,
525 $prependViewRendererSuffix)</code>: Используя этот
526 метод, вы можете задать другой суффикс для использования в
527 контексте $context. Третий аргумент используется для
528 указания того, добавлять ли или нет суффикс из
529 помощника ViewRenderer к новому суффиксу; этот флаг
530 установлен по умолчанию.
534 Передача пустого значения в качестве суффикса приведет к
535 тому, что будет использоваться только суффикс из
542 <code>addHeader($context, $header, $content)</code>:
543 Добавляет заголовок ответа для контекста $context.
544 <varname>$header</varname> является именем заголовка, а
545 <varname>$content</varname> - значением для этого заголовка.
549 Любой контекст может иметь несколько заголовков.
550 <code>addHeader()</code> добавляет дополнительные
551 заголовки в стек заголовков для данного контекста.
555 Если переданный заголовок уже существует для данного
556 контекста, то бросается исключение.
562 <code>setHeader($context, $header, $content)</code>:
563 <code>setHeader()</code> действует аналогично
564 <code>addHeader()</code> за исключением того, что позволяет
565 переопределять существующие для данного контекста заголовки.
571 <code>addHeaders($context, array $headers)</code>:
572 Добавляет несколько заголовков к контексту $context.
573 Использует <code>addHeader()</code>, поэтому
574 если заголовок уже существует, то бросается исключение.
575 <varname>$headers</varname> является массивом пар
582 <code>setHeaders($context, array $headers.)</code>:
583 действует аналогично <code>addHeaders()</code> за
584 исключением того, что использует <code>setHeader()</code>,
585 что позволяет перезаписывать существующие заголовки.
591 <code>getHeader($context, $header)</code>: возвращает
592 значение заголовка для данного контекста. Если заголовок
593 не найден, то возвращается null.
599 <code>removeHeader($context, $header)</code>: удаляет
600 один заголовок для данного контекста.
606 <code>clearHeaders($context)</code>: удаляет все
607 заголовки для данного контекста.
613 <code>setCallback($context, $trigger, $callback)</code>:
614 устанавливает функцию обратного вызова для триггера $trigger
615 и контекста $context. Триггерами могут быть 'init' или
616 'post' (выбор триггера определяет, когда будет вызвана
617 функция - во время инициализации контекста или операции
618 <code>postDispatch()</code>). <varname>$callback</varname> должен
619 быть действующей PHP-функцией обратного вызова.
625 <code>setCallbacks($context, array $callbacks)</code>:
626 устанавливает несколько функций обратного вызова для
627 данного контекста. <varname>$callbacks</varname> должен быть
628 массивом пар триггер/функция обратного вызова. В
629 действительности можно зарегистрировать максимум две
630 функции обратного вызова, одна для инициализации, другая для
637 <code>getCallback($context, $trigger)</code>: возвращает
638 функцию обратного вызова для триггера $trigger и контекста
645 <code>getCallbacks($context)</code>: возвращает все функции
646 обратного вызова в виде массива пар триггер/функция
653 <code>removeCallback($context, $trigger)</code>: удаляет
654 функцию обратного вызова для триггера $trigger и контекста
661 <code>clearCallbacks($context)</code>: удаляет все функции
662 обратного вызова для данного контекста.
668 <code>setContextParam($name)</code>: устанавливает параметр
669 запроса, используемый для переключения контекста. По
670 умолчанию установлено значение 'format', но его можно
671 изменить с помощью данного аксессора.
675 <code>getContextParam()</code> может использоваться для
676 получения текущего значения.
682 <code>setAutoDisableLayout($flag)</code>: По умолчанию
683 макеты отключаются, когда производится переключение
684 контекста. Это потому, что макеты, как правило, используются
685 только с обычным выводом и их использование не имеет смысла
686 для альтернативного контекста. Тем не менее, если вы хотите
687 использовать макеты, то можете изменить это поведение,
688 передав значение <constant>FALSE</constant> методу
689 <code>setAutoDisableLayout()</code>. Вы должны делать это
690 <emphasis>до</emphasis> вызова <code>initContext()</code>.
694 Для получения значения этого флага используйте аксессор
695 <code>getAutoDisableLayout()</code>.
701 <code>getCurrentContext()</code> может использоваться
702 для определения того, какой контекст был установлен.
703 Возвращает null, если не было переключения контекста,
704 или метод был вызван до <code>initContext()</code>.
710 <sect4 id="zend.controller.actionhelpers.contextswitch.ajaxcontext">
711 <title>Функционал AjaxContext</title>
714 Помощник <code>AjaxContext</code> наследует от
715 <code>ContextSwitch</code>, поэтому весь функционал, описанный
716 для <code>ContextSwitch</code>, доступен и для него. Но есть
717 несколько ключевых отличий.
721 Во-первых, он использует другое свойство контроллера для
722 определения контекстов, а именно <varname>$ajaxable</varname>. Поэтому вы
723 можете одновременно использовать различные контексты для AJAX и
724 обычных HTTP-запросов. Все методы
725 <code>*ActionContext*()</code> помощника <code>AjaxContext</code>
726 будут записывать в это свойство.
730 Во-вторых, он будет запускаться только тогда, когда производится
731 запрос XmlHttpRequest. Для определения того, был ли произведен
732 запрос XmlHttpRequest, используется метод
733 <code>isXmlHttpRequest()</code> объекта запроса.
734 Таким образом, если параметр контекста ('format') был передан в
735 запросе, но сам запрос был произведен не через
736 XmlHttpRequest, то не будет произведено переключение контекста.
740 И в-третьих, <code>AjaxContext</code> добавляет дополнительный
741 контекст - HTML. В этом контексте он устанавливает суффикс в
742 значение 'ajax.phtml' для отделения этого контекста от обычных
743 запросов. Дополнительные заголовки не возвращаются.
746 <example id="zend.controller.actionhelpers.contextswitch.ajaxcontext.example">
747 <title>Установка действий для ответов на AJAX-запросы</title>
750 В следующем примере мы указываем, что действия 'view', 'form' и
751 'process' могут отвечать на AJAX-запросы. В действиях
752 'view' и 'form' мы будем возвращать куски HTML-кода для
753 обновления страницы, в действии 'process' - данные в формате
757 <programlisting language="php"><![CDATA[
758 class CommentController extends Zend_Controller_Action
760 public function init()
762 $ajaxContext = $this->_helper->getHelper('AjaxContext');
763 $ajaxContext->addActionContext('view', 'html')
764 ->addActionContext('form', 'html')
765 ->addActionContext('process', 'json')
769 public function viewAction()
771 // Извлекает один комментарий для просмотра
772 // Если определен контекст AjaxContext, то используется
773 // скрипт вида comment/view.ajax.phtml
776 public function formAction()
778 // Рендерит форму для добавления нового комментария
779 // Если определен контекст AjaxContext, то используется
780 // скрипт вида comment/form.ajax.phtml
783 public function processAction()
785 // Обрабатывает новый комментарий
786 // Возвращает результат в формате JSON. Просто присвойте значения
787 // переменным вида и эти значения будет возвращены в формате JSON.
793 На клиентской стороне посредством AJAX-библиотеки производятся
794 запросы к конечным точкам '/comment/view',
795 '/comment/form', '/comment/process' и передается параметр
796 'format': '/comment/view/format/html',
797 '/comment/form/format/html', '/comment/process/format/json'
798 (можно также передавать параметр
799 через строку запроса, например: "?format=json").
803 Если ваша AJAX-библиотека передает заголовок 'X-Requested-With:
804 XmlHttpRequest', то эти действия будут возвращать ответ в