# ContextSwitch con AjaxContext

El ayudante de acción ContextSwitch está destinado a facilitar el regreso de respuestas de diferentes formatos de solicitud. El helper AjaxContext es una versión especializada de ContextSwitch que facilita el regreso de respuestas a XmlHttpRequests.

Para habilitar alguno, usted debe proporcionar indicios en su controlador de qué acciones pueden responder a que contextos. Si una solicitud entrante indica un contexto válido para la acción determinada, entonces el ayudante:

- Deshabilita los esquemas, si están habilitados.

- Establecer un sufijo de vista alternativo, requiriendo de manera efectiva un script de vista separado para el contexto.

- Envía las cabeceras de respuesta apropiadas para el contexto deseado.

- Opcionalmente, llama a llamadas de retorno especifícas para configurar el contexto y/o realizar post-procesamiento.

Como ejemplo, tomemos el siguiente controlador:

    class NewsController extends Zend_Controller_Action
    {
        /**
         * Página final; enviar a listAction()
         */
        public function indexAction()
        {
            $this->_forward('list');
        }

        /**
         * Lista nuevos items
         */
        public function listAction()
        {
        }

        /**
         * Vista del nuevo item
         */
        public function viewAction()
        {
        }
    }

Digamos que queremos que `listAction()` también esté disponible en un formato XML . En lugar de crear una acción diferente, podemos indicarle que puede devolver una respuesta XML :

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

        // ...
    }

Esto es lo que hará:

- Establecer la cabecera de respuesta 'Content-Type' a ' `text/xml` '.

- Cambiar el sufijo de vista de ' `xml.phtml` ' (o, si usa un sufifo de vista alternativo, 'xml.[su sufijo]').

Ahora, necesitará crear un nuevo script de vista, `news/list.xml.phtml` ', que creará y mostrará a XML .

Para determinar si una solicitud debe iniciar un cambio de contexto, el ayudante comprueba si hay un token en el objeto solicitud. Por defecto, busca el parámetro 'format', aunque esto puede ser configurado. Esto significa que, en muchos casos, para provocar un cambio de contexto, puede agregar un parámetro 'format' a su solicitud:

- Via parámetro URL : `/news/list/format/xml` (recordar que el valor por defecto del esquema de ruteo permite pares arbitrarios de clave/valor tras la acción).

- Via parámetro `GET` : `/news/list?format=xml`

ContextSwitch le permite especificar contextos arbitrarios, incluso qué sufijo cambiará (si hay alguno), cualquier cabecera de respuesta que deba ser enviada, y callbacks arbitrarios para la inicialización y posterior procesamiento.

## Contextos Disponibles por Defecto

Por defecto, dos contextos están a disposición del ayudante ContextSwitch : JSON y XML .

- JSON . El contexto JSON establece la cabecera de respuesta 'Content-Type' a ' `application/json` ', y el sufijo del script de vista a ' `json.phtml` '.

  Sin embargo, por defecto, no es necesario un script de vista. Simplemente serializará todas las variables de la vista, y emitirá la respuesta JSON inmediatamente.

  Este comportamiento puede ser desactivado apagando la serialización automática de JSON :

      $this->_helper->contextSwitch()->setAutoJsonSerialization(false);

- XML . El contexto XML establece la cabecera de respuesta 'Content-Type' a ' `text/xml` ', y el sufijo del script de vista a ' `xml.phtml` '. Necesitará crear un script de vista nuevo para el contexto.

## Creando Contextos Personalizados

A veces, los contextos por defecto no son suficientes. Por ejemplo, puede que desee devolver YAML , o PHP serializado, un RSS o ATOM feed, etc. ContextSwitch le permite hacerlo.

La forma más fácil para añadir un nuevo contexto es a través del método `addContext()` . Este método tiene dos argumentos, el nombre del contexto, y un array de especificación. La especificación debería incluir uno o más de los siguientes:

- suffix : el sufijo a anteponer al sufijo de la vista por defecto tal como está registrado en ViewRenderer.

- headers : un array de pares cabecera/valor que desea enviar como parte de la respuesta.

- callbacks : un array que contiene una o más de las claves 'init' o 'post', apuntando a callbacks PHP válidos que pueden utilizarse para inicializar el contexto y posterior procesamiento.

  La inicialización de callbacks ocurre cuando el contexto es detectado por ContextSwitch . Usted puede usarlo para ejecutar una lógica arbitraria. Por ejemplo, el contexto JSON utiliza un callback para desactivar a ViewRenderer cuando está activada la serialización auto-JSON.

  El post procesamiento ocurre durante la rutina de la acción `postDispatch()` , y puede ser utilizada para ejecutar una lógica arbitraria. Como ejemplo, el contexto JSON utiliza un callback para determinar si la serialización auto-JSON está activada; si así fuera, serializa las variables de la vista a JSON y envía la respuesta, pero si no, re-habilita a ViewRenderer.

Hay una variedad de métodos para interactuar con contextos:

- `addContext($context, array $spec)` : agrega un nuevo contexto. Lanza una excepción si el contexto ya existe.

- `setContext($context, array $spec)` : añade un nuevo contexto o sobrescribirá un contexto existente. Usa la misma especificación que `addContext()` .

- `addContexts(array $contexts)` : añade muchos contextos de una vez. El array `$contexts` debería ser un array de pares contexto/especificación. Si alguno de los contextos ya existe, arrojará una excepción.

- `setContexts(array $contexts)` : añade nuevos contextos y sobreescribe los existentes. Usa la misma especificación que `addContexts()` .

- `hasContext($context)` : devuelve `TRUE` si el contexto existe, `FALSE` de lo contrario.

- `getContext($context)` : recupera un único contexto por su nombre. Devuelve un array siguiendo la especificación usada en `addContext()` .

- `getContexts()` : recupera todos los contextos. Devuelve un array de pares contexto/especificación.

- `removeContext($context)` : elimina un único contexto por su nombre. Devuelve `TRUE` si tiene éxito, `FALSE` si el contexto no fue encontrado.

- `clearContexts()` : elimina todos los contextos.

## Estableciendo los Contextos por Acción

Hay dos mecanismos para establecer contextos disponibles. Puede crear manualmente los arrays en su controlador, o utilizar varios métodos en ContextSwitch para ensamblarlos.

El método principal para añadir relaciones acción/contexto es `addActionContext()` . Se esperan dos argumentos, la acción a la que el contexto se añade, y el nombre de un contexto o un array de contextos. Como ejemplo, considere la siguiente clase controlador:

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

        public function viewAction()
        {
        }

        public function commentsAction()
        {
        }

        public function updateAction()
        {
        }
    }

Supongamos que queremos añadir un contexto XML a la acción 'list', y contextos XML y JSON a la acción 'comments'. Podríamos hacerlo en el método `init()` :

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

Alternativamente, puede simplemente definir la propiedad del array `$context` :

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

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

El anterior es el menos sobrecargado, pero también está expuesto a posibles errores.

Los siguientes métodos pueden ser utilizados para construir los mapeos del contexto:

- `addActionContext($action, $context)` : marca uno o más contextos como disponibles para una acción. Si ya existen los mapeos, simplemente se añade a esos mapeos. `$context` puede ser un único contexto, o un array de contextos.

  Un valor de `TRUE` para el contexto marcará todos los contextos como disponibles para la acción.

  Un valor vacío de $contexto desactivará todos los contextos para la acción determinada.

- `setActionContext($action, $context)` : marca uno o más contextos como disponibles para una acción. Si el mapeo ya existe, se reemplaza con los especificados. `$context` puede ser un único contexto, o un array de contextos.

- `addActionContexts(array $contexts)` : agrega varios pares acción/contexto de una vez. `$contexts` debe ser un array asociativo de pares acción/contexto. Le pasa la petición a `addActionContext()` , lo que significa que si los emparejamientos ya existen, se añade a ellos.

- `setActionContexts(array $contexts)` : actúa como `addActionContexts()` , pero sobreescribe pares de acción/contexto existentes.

- `hasActionContext($action, $context)` : determina si una acción particular tiene un contexto determinado.

- `getActionContexts($action = null)` : devuelve o todos los contextos para una acción determinada, o todos los pares de acción/contexto.

- `removeActionContext($action, $context)` : elimina uno o más contextos de una acción determinada. `$context` puede ser un único contexto o un array de contextos.

- `clearActionContexts($action = null)` : elimina todos los contextos de una acción determinada, o de todas las acciones con contextos.

## Inicializando Conmutación de Contextos (Context Switching)

Para inicializar la conmutación de contexto, necesita llamar a `initContext()` en su controlador de acción:

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

En algunos casos, puede querer forzar el contexto utilizado; por ejemplo, puede que sólo quiera permitir el contexto XML si la conmutación de contexto está activada. Puede hacerlo pasando el contexto a `initContext()` :

    $contextSwitch->initContext('xml');

## Funcionalidad Adicional

Se pueden utilizar una variedad de métodos para alterar el comportamiento del ayudante ContextSwitch . Estos incluyen:

- `setAutoJsonSerialization($flag)` : Por defecto, los contextos JSON serializarán cualquier variable de vista a notación JSON y lo devolverán como una respuesta. Si usted desea crear su propia respuesta, debe deshabilitar esta opción; esto debe hacerse antes de llamar a `initContext()` .

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

  Puede recuperar el valor del flag con `getAutoJsonSerialization()` .

- `setSuffix($context, $suffix, $prependViewRendererSuffix)` : Con este método, puede especificar un sufijo diferente para utilizarlo en un contexto determinado. El tercer argumento es utilizado para indicar si anteponer o no el actual sufijo de ViewRenderer con el nuevo sufijo; este flag está activado por defecto.

  Pasando un valor vacío para el sufijo hará que sólo el sufijo ViewRenderer será utilizado.

- `addHeader($context, $header, $content)` : Añadir una cabecera de respuesta para un determinado contexto. `$header` es el nombre de la cabecera, y `$content` es el valor a pasar por esa cabecera.

  Cada contexto pueden tener múltiples cabeceras; `addHeader()` agrega cabeceras adicionales al stack de cabecera del contexto.

  Si el `$header` especificado ya existe para el contexto, arrojará una excepción.

- `setHeader($context, $header, $content)` : `setHeader()` actúa igual que `addHeader()` , excepto que le permite sobreescribir cabeceras del contexto actual.

- `addHeaders($context, array $headers)` : Añade varias cabeceras de una vez a un determinado contexto. Delega a `addHeader()` , así que si la cabecera ya existe, arrojará una excepción. `$headers` es un array de pares cabecera/contexto.

- `setHeaders($context, array $headers.)` : como `addHeaders()` , excepto que lo delegua a `setHeader()` , permitiéndole sobreescribir las cabeceras existentes.

- `getHeader($context, $header)` : recuperar el valor de una cabecera para un determinado contexto. Retorna null si no existe.

- `removeHeader($context, $header)` : eliminar una única cabecera para un determinado contexto.

- `clearHeaders($context, $header)` : eliminar todas las cabeceras para un determinado contexto.

- `setCallback($context, $trigger, $callback)` : establecer un callback en un determinado disparador para poner en marcha un determinado contexto. Los disparadores pueden ser 'init' o 'post' (indicando que se llamará a un callback para cada contexto de inicialización o postDispatch). `$callback` debe ser un callback válido de PHP .

- `setCallbacks($context, array $callbacks)` : establece varios callbacks para un determinado contexto. `$callbacks` deben ser pares de diparadores/callbacks. En realidad, la mayor cantidad de callbacks que pueden ser registrados son dos, uno para la inicialización y otro para el procesamiento posterior.

- `getCallback($context, $trigger)` : recuperar un callback para un determinado disparador en un contexto dado.

- `getCallbacks($context)` : recupera todos los callbacks para un determinado contexto. Devuelve un array de pares disparor/callback.

- `removeCallback($context, $trigger)` : elimina un callback para un determinado disparador y contexto.

- `clearCallbacks($context)` : elimina todos los callbacks para un determinado contexto.

- `setContextParam($name)` : establece el parámetro de petición para comprobar si un conmutador de contexto ha sido solicitado. El valor por defecto es 'format', pero este accededor puede ser utilizado para establecer un valor alternativo.

  `getContextParam()` puede ser utilizado para recuperar el valor actual.

- `setAutoDisableLayout($flag)` : Por defecto, los esquemas están deshabilitados cuando sucede una conmutación de contexto; esto es porque normalmente los esquemas sólo serán utilizados para devolver respuestas normales, y no tienen sentido en otros contextos. Sin embargo, si desea usar esquemas (tal vez puede tener un diseño para el nuevo contexto), puede cambiar este comportamiento pasando un valor falso a `setAutoDisableLayout()` . Usted debería hacer esto antes de llamar a `initContext()` .

  Para conseguir el valor de este flag, utilice el accededor `getAutoDisableLayout()` .

- `getCurrentContext()` Puede ser utilizado para determinar qué contexto fue detectado, si hay alguno. Este retorna null si no hubo conmutación de contexto, o si `initContext()` fue llamado antes de ser invocado.

## Funcionalidad de AjaxContext

El ayudante AjaxContext extiende ContextSwitch , así que toda de la funcionalidad listada para ContextSwitch está disponible. Hay algunas diferencias fundamentales, sin embargo.

En primer lugar, el controlador de acción utiliza una propiedad diferente para determinar contextos, `$ajaxable` . Esto es, que puede tener diferentes contextos utilizados para AJAX versus peticiones normales HTTP . Los diversos métodos `*ActionContext*()` de AjaxContext le escribirán a esta propiedad.

En segundo lugar, sólo se disparará si se produjo un XmlHttpRequest, según lo determinado por la solicitud del método del objeto `isXmlHttpRequest()` . Así, si se pasa el parámetro de contexto ('format') en la solicitud, pero la solicitud no fue hecha como un XmlHttpRequest, no se disparará ninguna conmutación de contexto.

En tercer lugar, AjaxContext agrega un contexto adicional, HTML . En este contexto, se establece el sufijo a ' `ajax.phtml` ' para diferenciar el contexto de una solicitud normal. No se devuelven cabeceras adicionales.

### Permitiendo a las Acciones Responder a Requerimientos Ajax

En el siguiente ejemplo, estamos permitiendo requerimientos a las acciones 'view', 'form', y 'process' para responder a peticiones AJAX . En los dos primeros casos, 'view' y 'form', devolveremos fragmentos (snippets) de HTML con los cuales actualizaremos la página; y en el último, devolveremos 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()
        {
            // Tirar para ver un único comentario.
            // Cuando se detecta AjaxContext, utiliza el script de vista
            // comment/view.ajax.phtml.
        }

        public function formAction()
        {
            // Mostrar el form "add new comment".
            // Cuando se detecta AjaxContext, utiliza el script de vista
            // comment/form.ajax.phtml.
        }

        public function processAction()
        {
            // Procesar un nuevo comentario
            // Devolver los resultados como JSON; simplemente asignar los
            // resultados como variables de la vista, y se devolverán como JSON.

        }
    }

En el lado del cliente, su biblioteca AJAX simplemente pedirá los parámetros finales ' `/comment/view` ', ' `/comment/form` ', y ' `/comment/process` ', y pasar el parámetro 'format': ' `/comment/view/format/html` ', ' `/comment/form/format/html` ', ' `/comment/process/format/json` '. (O puede pasar el parámetro via string de consulta: ejemplo "?format=json").

Asumiendo que su biblioteca pasa la cabecera 'X-Requested-With:XmlHttpRequest' entonces estas acciones devolverán la respuesta en el formato apropiado.