TigerZF
🌐Español

24.8. Ayudantes de acción

24.8.1. Introducción

Los ayudantes de acción (Action Helpers) permiten a los desarrolladores inyectar funcionalidad en tiempo de ejecución y/o bajo demanda en cualquier controlador de acción que extienda de Zend_Controller_Action. Los ayudantes de acción tienen como objetivo minimizar la necesidad de extender el controlador de acción abstracto para inyectar funcionalidad común del controlador de acción.

Existen varias formas de utilizar los ayudantes de acción. Los ayudantes de acción emplean el uso de un sistema de corretaje (brokerage), similar al tipo de corretaje que se ve en Zend_View_Helper, y al de Zend_Controller_Plugin. Los ayudantes de acción (al igual que Zend_View_Helper) pueden cargarse y llamarse bajo demanda, o pueden instanciarse en el momento de la petición (bootstrap) o en el momento de creación del controlador de acción (init()). Para comprender esto con más detalle, consulte la sección de uso a continuación.

24.8.2. Inicialización de ayudantes

Un ayudante puede inicializarse de varias formas distintas, según sus necesidades así como la funcionalidad de dicho ayudante.

El corredor de ayudantes (helper broker) se almacena como el miembro $_helper de Zend_Controller_Action; use el corredor para recuperar o invocar ayudantes. Algunos métodos para hacerlo incluyen:

  • Usar explícitamente getHelper(). Simplemente pásele un nombre, y se devuelve un objeto ayudante:

    $flashMessenger = $this->_helper->getHelper('FlashMessenger');
    $flashMessenger->addMessage('We did something in the last request');
    
  • Usar la funcionalidad __get() del corredor de ayudantes y recuperar el ayudante como si fuera una propiedad miembro del corredor:

    $flashMessenger = $this->_helper->FlashMessenger;
    $flashMessenger->addMessage('We did something in the last request');
    
  • Finalmente, la mayoría de los ayudantes de acción implementan el método direct(), el cual llamará a un método específico y predeterminado en el ayudante. En el ejemplo del FlashMessenger, este invoca a addMessage():

    $this->_helper->FlashMessenger('We did something in the last request');
    
[Note] Nota

Todos los ejemplos anteriores son funcionalmente equivalentes.

También puede instanciar ayudantes explícitamente. Es posible que desee hacer esto si utiliza el ayudante fuera de un controlador de acción, o si desea pasar un ayudante al corredor de ayudantes para que lo use cualquier acción. La instanciación se realiza como con cualquier otra clase de PHP.

24.8.3. El corredor de ayudantes

Zend_Controller_Action_HelperBroker se encarga de los detalles del registro de objetos ayudantes y rutas de ayudantes, así como de recuperar ayudantes bajo demanda.

Para registrar un ayudante con el corredor, use addHelper():

Zend_Controller_Action_HelperBroker::addHelper($helper);

Por supuesto, instanciar y pasar ayudantes al corredor consume algo de tiempo y recursos, por lo que existen dos métodos para automatizar un poco las cosas: addPrefix() y addPath().

  • addPrefix() toma un prefijo de clase y lo usa para determinar una ruta donde se han definido las clases de ayudantes. Se asume que el prefijo sigue las convenciones de nomenclatura de clases de Zend Framework.

    // Add helpers prefixed with My_Action_Helpers in My/Action/Helpers/
    Zend_Controller_Action_HelperBroker::addPrefix('My_Action_Helpers');
    
  • addPath() toma un directorio como primer argumento y un prefijo de clase como segundo argumento (por defecto 'Zend_Controller_Action_Helper'). Esto le permite asignar sus propios prefijos de clase a directorios específicos.

    // Add helpers prefixed with Helper in Plugins/Helpers/
    Zend_Controller_Action_HelperBroker::addPath('./Plugins/Helpers',
                                                 'Helper');
    

Dado que estos métodos son estáticos, pueden invocarse en cualquier punto de la cadena del controlador con el fin de agregar ayudantes dinámicamente según sea necesario.

Internamente, el corredor de ayudantes utiliza una instancia de PluginLoader para mantener las rutas. Puede recuperar el PluginLoader usando el método estático getPluginLoader(), o, alternativamente, inyectar una instancia personalizada de PluginLoader usando setPluginLoader().

Para determinar si un ayudante existe en el corredor de ayudantes, use hasHelper($name), donde $name es el nombre corto del ayudante (sin el prefijo):

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

También existen dos métodos estáticos para recuperar ayudantes del corredor de ayudantes: getExistingHelper() y getStaticHelper(). getExistingHelper() recuperará un ayudante solo si previamente ha sido invocado o registrado explícitamente con el corredor de ayudantes; lanzará una excepción si no es así. getStaticHelper() hace lo mismo que getExistingHelper(), pero intentará instanciar el ayudante si aún no ha sido registrado en la pila de ayudantes. getStaticHelper() es una buena opción para recuperar ayudantes que se desea configurar.

Ambos métodos toman un único argumento, $name, que es el nombre corto del ayudante (sin el prefijo).

// Check if 'redirector' helper is registered with the broker, and fetch:
if (Zend_Controller_Action_HelperBroker::hasHelper('redirector')) {
    $redirector =
        Zend_Controller_Action_HelperBroker::getExistingHelper('redirector');
}

// Or, simply retrieve it, not worrying about whether or not it was
// previously registered:
$redirector =
    Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
}

Finalmente, para eliminar un ayudante registrado del corredor, use removeHelper($name), donde $name es el nombre corto del ayudante (sin el prefijo):

// Conditionally remove the 'redirector' helper from the broker:
if (Zend_Controller_Action_HelperBroker::hasHelper('redirector')) {
    Zend_Controller_Action_HelperBroker::removeHelper('redirector')
}

24.8.4. Ayudantes de acción incorporados

Zend Framework incluye varios ayudantes de acción por defecto: AutoComplete para automatizar respuestas de autocompletado AJAX; ContextSwitch y AjaxContext para servir formatos de respuesta alternativos para sus acciones; un FlashMessenger para manejar mensajes flash de sesión; Json para codificar y enviar respuestas JSON; un Redirector, que proporciona diferentes implementaciones para redirigir a páginas internas y externas desde su aplicación; y un ViewRenderer para automatizar el proceso de configuración del objeto de vista en sus controladores y la renderización de vistas.

24.8.4.1. ActionStack

El ayudante ActionStack le permite enviar peticiones al plugin del controlador frontal ActionStack, ayudándole efectivamente a crear una cola de acciones para ejecutar durante la petición. El ayudante le permite agregar acciones ya sea especificando nuevos objetos de petición o conjuntos de acción - controlador - módulo.

[Note] Invocar el ayudante ActionStack inicializa el plugin ActionStack

Invocar el ayudante ActionStack registra implícitamente el plugin ActionStack -- lo que significa que no necesita registrar explícitamente el plugin ActionStack para usar esta funcionalidad.

Ejemplo 24.2. Agregar una tarea usando nombres de acción, controlador y módulo

A menudo, es más sencillo simplemente especificar la acción, el controlador y el módulo (y parámetros de petición opcionales), tal como lo haría al llamar a Zend_Controller_Action::_forward():

class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // Add two actions to the stack
        // Add call to /foo/baz/bar/baz
        // (FooController::bazAction() with request var bar == baz)
        $this->_helper->actionStack('baz',
                                    'foo',
                                    'default',
                                    array('bar' => 'baz'));

        // Add call to /bar/bat
        // (BarController::batAction())
        $this->_helper->actionStack('bat', 'bar');
    }
}

Ejemplo 24.3. Agregar una tarea usando un objeto de petición

A veces la naturaleza OOP de un objeto de petición tiene más sentido; puede pasar dicho objeto al ayudante ActionStack también.

class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // Add two actions to the stack
        // Add call to /foo/baz/bar/baz
        // (FooController::bazAction() with request var bar == baz)
        $request = clone $this->getRequest();
        // Don't set controller or module; use current values
        $request->setActionName('baz')
                ->setParams(array('bar' => 'baz'));
        $this->_helper->actionStack($request);

        // Add call to /bar/bat
        // (BarController::batAction())
        $request = clone $this->getRequest();
        // don't set module; use current value
        $request->setActionName('bat')
                ->setControllerName('bar');
        $this->_helper->actionStack($request);
    }
}

24.8.4.2. AutoComplete

Muchas bibliotecas javascript de AJAX ofrecen funcionalidad para proporcionar autocompletado, mediante la cual se muestra una lista de posibles resultados coincidentes a medida que el usuario escribe. El ayudante AutoComplete tiene como objetivo simplificar la devolución de respuestas aceptables para dichos métodos.

Dado que no todas las bibliotecas JS implementan el autocompletado de la misma manera, el ayudante AutoComplete proporciona alguna funcionalidad base abstracta necesaria para muchas bibliotecas, e implementaciones concretas para bibliotecas individuales. Los tipos de retorno son generalmente arreglos JSON de cadenas, arreglos JSON de arreglos (siendo cada arreglo miembro un arreglo asociativo de metadatos usado para crear la lista de selección), o HTML.

El uso básico para cada implementación es el mismo:

class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // Perform some logic...

        // Encode and send response;
        $this->_helper->autoCompleteDojo($data);

        // Or explicitly:
        $response = $this->_helper->autoCompleteDojo
                                  ->sendAutoCompletion($data);

        // Or simply prepare autocompletion response:
        $response = $this->_helper->autoCompleteDojo
                                  ->prepareAutoCompletion($data);
    }
}

Por defecto, el autocompletado realiza lo siguiente:

  • Deshabilita los layouts y el ViewRenderer.

  • Establece las cabeceras de respuesta apropiadas.

  • Establece el cuerpo de la respuesta con los datos de autocompletado codificados o formateados.

  • Envía la respuesta.

Los métodos disponibles del ayudante incluyen:

  • disableLayouts() se puede usar para deshabilitar los layouts y el ViewRenderer. Típicamente, esto se llama dentro de prepareAutoCompletion().

  • encodeJson($data, $keepLayouts = false) codificará los datos a JSON, habilitando o deshabilitando opcionalmente los layouts. Típicamente, esto se llama dentro de prepareAutoCompletion().

  • prepareAutoCompletion($data, $keepLayouts = false) se usa para preparar los datos en el formato de respuesta necesario para la implementación concreta, habilitando o deshabilitando opcionalmente los layouts. El valor de retorno variará según la implementación.

  • sendAutoCompletion($data, $keepLayouts = false) se usa para enviar los datos en el formato de respuesta necesario para la implementación concreta. Llama a prepareAutoCompletion(), y luego envía la respuesta.

  • direct($data, $sendNow = true, $keepLayouts = false) se usa al llamar al ayudante como un método del corredor de ayudantes. La bandera $sendNow se usa para determinar si se debe llamar a sendAutoCompletion() o prepareAutoCompletion(), respectivamente.

Actualmente, AutoComplete soporta las bibliotecas AJAX Dojo y Scriptaculous.

24.8.4.2.1. Autocompletado con Dojo

Dojo no tiene un widget de AutoCompletion propiamente dicho, pero cuenta con dos widgets que pueden realizar AutoCompletion: ComboBox y FilteringSelect. En ambos casos, requieren un almacén de datos que implemente QueryReadStore; para más información sobre estos temas, consulte la documentación de dojo.data.

En Zend Framework, puede pasar un simple arreglo indexado al ayudante AutoCompleteDojo, y este devolverá una respuesta JSON adecuada para usar con dicho almacén:

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

Ejemplo 24.4. Autocompletado con Dojo usando el MVC de Zend

El autocompletado con Dojo a través del MVC de Zend requiere varias cosas: generar un objeto de formulario para el ComboBox en el que se desea el autocompletado, una acción de controlador para servir los resultados de autocompletado, crear un QueryReadStore personalizado para conectar con la acción de autocompletado, y generar el javascript necesario para inicializar el autocompletado en el lado del servidor.

Primero, veamos el javascript necesario. Dojo ofrece un framework completo para crear javascript OOP, tal como lo hace Zend Framework para PHP. Parte de esto es la capacidad de crear pseudo-espacios de nombres usando la jerarquía de directorios. Crearemos un directorio 'custom' al mismo nivel que el directorio Dojo que forma parte de la distribución de Dojo. Dentro de ese directorio, crearemos un archivo javascript, TestNameReadStore.js, con el siguiente contenido:

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

Esta clase es simplemente una extensión del propio QueryReadStore de Dojo, que en sí mismo es una clase abstracta. Simplemente definimos un método de petición, y lo asignamos al elemento 'test'.

A continuación, creemos el elemento de formulario para el cual queremos autocompletado:

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

Aquí, simplemente creamos un formulario con los métodos 'test' y 'go'. El método 'test' agrega varios atributos especiales, específicos de Dojo: dojoType, store, autoComplete y hasDownArrow. dojoType se usa para indicar que estamos creando un ComboBox, y lo enlazaremos a un almacén de datos (clave 'store') de 'testStore' -- más sobre esto más adelante. Especificar 'autoComplete' como FALSE le indica a Dojo que no seleccione automáticamente la primera coincidencia, sino que muestre una lista de coincidencias. Finalmente, 'hasDownArrow' crea una flecha hacia abajo similar a un cuadro de selección para que podamos mostrar y ocultar las coincidencias.

Agreguemos un método para mostrar el formulario, así como un punto final para procesar el autocompletado:

class TestController extends Zend_Controller_Action
{
    // ...

    /**
     * Landing page
     */
    public function indexAction()
    {
        $this->view->form = $this->getForm();
    }

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

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

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

En nuestra autocompleteAction() hacemos varias cosas. Primero, verificamos que se trate de una petición post, y que exista un parámetro 'format' establecido con el valor 'ajax'; esto simplemente ayuda a reducir consultas espurias a la acción. Luego, verificamos un parámetro 'test', y lo comparamos contra nuestros datos. (Omito deliberadamente la implementación de getData() aquí -- podría ser cualquier tipo de fuente de datos.) Finalmente, enviamos nuestras coincidencias a nuestro ayudante de AutoCompletion.

Ahora que tenemos todas las piezas en el backend, veamos qué necesitamos entregar en nuestro script de vista para la página de aterrizaje. Primero, necesitamos configurar nuestro almacén de datos, luego renderizar nuestro formulario, y finalmente asegurarnos de que se carguen las bibliotecas Dojo apropiadas -- incluyendo nuestro almacén de datos personalizado. Veamos el script de vista, que comenta los pasos:

<?php // setup our data store: ?>
<div dojoType="custom.TestNameReadStore" jsId="testStore"
    url="<?php echo $this->baseUrl() ?>/unit-test/autocomplete/format/ajax"
    requestMethod="get"></div>

<?php // render our form: ?>
<?php echo $this->form ?>

<?php // setup Dojo-related CSS to load in HTML head: ?>
<?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 // setup javascript to load in HTML head, including all required
   // Dojo libraries: ?>
<?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() ?>

Observe las llamadas a ayudantes de vista como headStyle y headScript; estos son marcadores de posición (placeholders), que luego podemos renderizar en la sección HTML head de nuestro script de vista de layout.

Ahora tenemos todas las piezas para hacer que el autocompletado de Dojo funcione.


24.8.4.2.2. Autocompletado con Scriptaculous

Scriptaculous espera una respuesta HTML en un formato específico.

El ayudante a usar con esta biblioteca es 'AutoCompleteScriptaculous'. Simplemente proporciónele un arreglo de datos, y el ayudante creará una respuesta HTML compatible con Ajax.Autocompleter.

24.8.4.3. ContextSwitch y AjaxContext

El ayudante de acción ContextSwitch está pensado para facilitar la devolución de diferentes formatos de respuesta según la petición. El ayudante AjaxContext es una versión especializada de ContextSwitch que facilita la devolución de respuestas a XmlHttpRequests.

Para habilitar cualquiera de los dos, debe proporcionar indicaciones en su controlador sobre qué acciones pueden responder a qué contextos. Si una petición entrante indica un contexto válido para la acción dada, el ayudante entonces:

  • Deshabilitará los layouts, si están habilitados.

  • Establecerá un sufijo de vista alternativo, lo que efectivamente requiere un script de vista independiente para el contexto.

  • Enviará las cabeceras de respuesta apropiadas para el contexto deseado.

  • Opcionalmente, invocará callbacks especificados para configurar el contexto y/o realizar el postprocesamiento.

Como ejemplo, consideremos el siguiente controlador:

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

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

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

Supongamos que queremos que listAction() también esté disponible en formato XML. En lugar de crear una acción diferente, podemos indicar 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 hará lo siguiente:

  • Establecerá la cabecera de respuesta 'Content-Type' a 'application/xml'.

  • Cambiará el sufijo de vista a 'xml.phtml' (o, si usa un sufijo de vista alternativo, 'xml.[su sufijo]').

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

Para determinar si una petición debe iniciar un cambio de contexto, el ayudante verifica un token en el objeto de petición. Por defecto, busca el parámetro 'format', aunque esto puede configurarse. Esto significa que, en la mayoría de los casos, para activar un cambio de contexto, puede agregar un parámetro 'format' a su petición:

  • Mediante parámetro de URL: /news/list/format/xml (recuerde, el esquema de enrutamiento por defecto permite pares clave-valor arbitrarios después de la acción)

  • Mediante parámetro GET: /news/list?format=xml

ContextSwitch le permite especificar contextos arbitrarios, incluyendo qué cambio de sufijo ocurrirá (si acaso), cualquier cabecera de respuesta que deba enviarse, y callbacks arbitrarios para la inicialización y el postprocesamiento.

24.8.4.3.1. Contextos disponibles por defecto

Por defecto, hay dos contextos disponibles para el 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 se requiere ningún script de vista. Simplemente serializará todas las variables de vista, y emitirá la respuesta JSON de inmediato.

    Este comportamiento se puede deshabilitar desactivando la serialización automática de JSON:

    $this->_helper->contextSwitch()->setAutoJsonSerialization(false);
    
  • XML. El contexto XML establece la cabecera de respuesta 'Content-Type' a 'application/xml', y el sufijo del script de vista a 'xml.phtml'. Necesitará crear un nuevo script de vista para el contexto.

24.8.4.3.2. Creación de contextos personalizados

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

La forma más fácil de agregar un nuevo contexto es mediante el método addContext(). Este método toma dos argumentos, el nombre del contexto, y una especificación en forma de arreglo. La especificación debe incluir uno o más de los siguientes:

  • suffix: el sufijo a anteponer al sufijo de vista por defecto registrado en el ViewRenderer.

  • headers: un arreglo de pares cabecera-valor que desea que se envíen como parte de la respuesta.

  • callbacks: un arreglo que contiene una o más de las claves 'init' o 'post', apuntando a callbacks PHP válidos que pueden usarse para la inicialización del contexto y/o el postprocesamiento.

    Los callbacks de inicialización ocurren cuando ContextSwitch detecta el contexto. Puede usarlo para realizar lógica arbitraria que deba ocurrir. Como ejemplo, el contexto JSON usa un callback para deshabilitar el ViewRenderer cuando la serialización automática de JSON está activada.

    El postprocesamiento ocurre durante la rutina postDispatch() de la acción, y se puede usar para realizar lógica arbitraria. Como ejemplo, el contexto JSON usa un callback para determinar si la serialización automática de JSON está activada; si es así, serializa las variables de vista a JSON y envía la respuesta, pero si no, vuelve a habilitar el ViewRenderer.

Existen diversos métodos para interactuar con los contextos:

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

  • setContext($context, array $spec): agrega un nuevo contexto o sobrescribe uno existente. Usa la misma especificación que addContext().

  • addContexts(array $contexts): agrega varios contextos a la vez. El arreglo $contexts debe ser un arreglo de pares contexto-especificación. Si alguno de los contextos ya existe, lanzará una excepción.

  • setContexts(array $contexts): agrega nuevos contextos y sobrescribe los existentes. Usa la misma especificación que addContexts().

  • hasContext($context): devuelve TRUE si el contexto existe, FALSE en caso contrario.

  • getContext($context): recupera un único contexto por nombre. Devuelve un arreglo siguiendo la especificación usada en addContext().

  • getContexts(): recupera todos los contextos. Devuelve un arreglo de pares contexto-especificación.

  • removeContext($context): elimina un único contexto por nombre. Devuelve TRUE si tiene éxito, FALSE si no se encontró el contexto.

  • clearContexts(): elimina todos los contextos.

24.8.4.3.3. Establecer contextos por acción

Existen dos mecanismos para establecer los contextos disponibles. Puede crear manualmente arreglos en su controlador, o usar varios métodos en ContextSwitch para ensamblarlos.

El método principal para agregar relaciones acción-contexto es addActionContext(). Espera dos argumentos, la acción a la que se agrega el contexto, y ya sea el nombre de un contexto o un arreglo de contextos. Como ejemplo, considere la siguiente clase de controlador:

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

    public function viewAction()
    {
    }

    public function commentsAction()
    {
    }

    public function updateAction()
    {
    }
}

Supongamos que queremos agregar un contexto XML a la acción 'list', y contextos XML y JSON a la acción 'comments'. Podemos 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, podría simplemente definir la propiedad de arreglo $contexts:

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

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

Lo anterior implica menos sobrecarga, pero también es más propenso a posibles errores.

Los siguientes métodos pueden usarse para construir los mapeos de contexto:

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

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

    Un valor vacío para $context deshabilitará todos los contextos para la acción dada.

  • setActionContext($action, $context): marca uno o más contextos como disponibles para una acción. Si ya existen mapeos, los reemplaza con los especificados. $context puede ser un único contexto, o un arreglo de contextos.

  • addActionContexts(array $contexts): agrega varios pares acción-contexto a la vez. $contexts debe ser un arreglo asociativo de pares acción-contexto. Delega en addActionContext(), lo que significa que si ya existen mapeos, se agrega a ellos.

  • setActionContexts(array $contexts): actúa como addActionContexts(), pero sobrescribe los pares acción-contexto existentes.

  • hasActionContext($action, $context): determina si una acción particular tiene un contexto dado.

  • getActionContexts($action = null): devuelve ya sea todos los contextos para una acción dada, o todos los pares acción-contexto.

  • removeActionContext($action, $context): elimina uno o más contextos de una acción dada. $context puede ser un único contexto o un arreglo de contextos.

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

24.8.4.3.4. Inicializar el cambio de contexto

Para inicializar el cambio 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, es posible que desee forzar el contexto usado; por ejemplo, puede que solo desee permitir el contexto XML si el cambio de contexto está activado. Puede hacerlo pasando el contexto a initContext():

$contextSwitch->initContext('xml');
24.8.4.3.5. Funcionalidad adicional

Se pueden usar varios métodos para modificar el comportamiento del ayudante ContextSwitch. Estos incluyen:

  • setAutoJsonSerialization($flag): Por defecto, los contextos JSON serializarán todas las variables de vista a notación JSON y las devolverán como respuesta. Si desea crear su propia respuesta, debe desactivar esto; esto debe hacerse antes de la llamada a initContext().

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

    Puede recuperar el valor de la bandera con getAutoJsonSerialization().

  • setSuffix($context, $suffix, $prependViewRendererSuffix): Con este método, puede especificar un sufijo diferente para usar en un contexto dado. El tercer argumento se usa para indicar si se debe anteponer el sufijo actual del ViewRenderer con el nuevo sufijo; esta bandera está habilitada por defecto.

    Pasar un valor vacío al sufijo hará que solo se use el sufijo del ViewRenderer.

  • addHeader($context, $header, $content): Agrega una cabecera de respuesta para un contexto dado. $header es el nombre de la cabecera, y $content es el valor a pasar para esa cabecera.

    Cada contexto puede tener múltiples cabeceras; addHeader() agrega cabeceras adicionales a la pila de cabeceras del contexto.

    Si la $header especificada ya existe para el contexto, se lanzará una excepción.

  • setHeader($context, $header, $content): setHeader() actúa igual que addHeader(), salvo que permite sobrescribir cabeceras de contexto existentes.

  • addHeaders($context, array $headers): Agrega varias cabeceras a la vez a un contexto dado. Delega en addHeader(), por lo que si la cabecera ya existe, se lanzará una excepción. $headers es un arreglo de pares cabecera-contexto.

  • setHeaders($context, array $headers.): como addHeaders(), salvo que delega en setHeader(), permitiéndole sobrescribir cabeceras existentes.

  • getHeader($context, $header): recupera el valor de una cabecera para un contexto dado. Devuelve NULL si no se encuentra.

  • removeHeader($context, $header): elimina una única cabecera para un contexto dado.

  • clearHeaders($context, $header): elimina todas las cabeceras para un contexto dado.

  • setCallback($context, $trigger, $callback): establece un callback en un disparador (trigger) dado para un contexto dado. Los disparadores pueden ser 'init' o 'post' (indicando que el callback se llamará en la inicialización del contexto o en postDispatch). $callback debe ser un callback PHP válido.

  • setCallbacks($context, array $callbacks): establece varios callbacks para un contexto dado. $callbacks debe ser pares disparador-callback. En realidad, la cantidad máxima de callbacks que se pueden registrar es dos, uno para la inicialización y otro para el postprocesamiento.

  • getCallback($context, $trigger): recupera un callback para un disparador dado en un contexto dado.

  • getCallbacks($context): recupera todos los callbacks para un contexto dado. Devuelve un arreglo de pares disparador-callback.

  • removeCallback($context, $trigger): elimina un callback para un disparador y contexto dados.

  • clearCallbacks($context): elimina todos los callbacks para un contexto dado.

  • setContextParam($name): establece el parámetro de petición a verificar al determinar si se ha solicitado un cambio de contexto. El valor por defecto es 'format', pero este accesor se puede usar para establecer un valor alternativo.

    getContextParam() se puede usar para recuperar el valor actual.

  • setAutoDisableLayout($flag): Por defecto, los layouts se deshabilitan cuando ocurre un cambio de contexto; esto se debe a que normalmente los layouts solo se usarán para devolver respuestas normales, y no tienen sentido en contextos alternativos. Sin embargo, si desea usar layouts (quizás tenga un layout para el nuevo contexto), puede cambiar este comportamiento pasando un valor FALSE a setAutoDisableLayout(). Debe hacer esto antes de llamar a initContext().

    Para obtener el valor de esta bandera, use el accesor getAutoDisableLayout().

  • getCurrentContext() se puede usar para determinar qué contexto fue detectado, si acaso. Esto devuelve NULL si no ocurrió ningún cambio de contexto, o si se llama antes de que se haya invocado initContext().

24.8.4.3.6. Funcionalidad de AjaxContext

El ayudante AjaxContext extiende ContextSwitch, por lo que toda la funcionalidad listada para ContextSwitch está disponible en él. Sin embargo, hay algunas diferencias clave.

Primero, usa una propiedad diferente del controlador de acción para determinar los contextos, $ajaxable. Esto es para que pueda tener diferentes contextos usados para peticiones AJAX frente a peticiones HTTP normales. Los diversos métodos *ActionContext()* de AjaxContext escribirán en esta propiedad.

Segundo, solo se activará si ha ocurrido un XmlHttpRequest, según lo determinado por el método isXmlHttpRequest() del objeto de petición. Por lo tanto, si el parámetro de contexto ('format') se pasa en la petición, pero la petición no se realizó como un XmlHttpRequest, no se activará ningún cambio de contexto.

Tercero, AjaxContext agrega un contexto adicional, HTML. En este contexto, establece el sufijo a 'ajax.phtml' para diferenciar el contexto de una petición normal. No se devuelven cabeceras adicionales.

Ejemplo 24.5. Permitir que las acciones respondan a peticiones Ajax

En el siguiente ejemplo, permitimos que las peticiones a las acciones 'view', 'form' y 'process' respondan a peticiones AJAX. En los dos primeros casos, 'view' y 'form', devolveremos fragmentos HTML con los que actualizar la página; 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()
    {
        // Pull a single comment to view.
        // When AjaxContext detected, uses the comment/view.ajax.phtml
        // view script.
    }

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

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

En el lado del cliente, su biblioteca AJAX simplemente solicitará los puntos 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 a través de la cadena de consulta: por ejemplo, "?format=json".)

Suponiendo que su biblioteca envía la cabecera 'X-Requested-With: XmlHttpRequest', estas acciones devolverán entonces el formato de respuesta apropiado.


24.8.4.4. FlashMessenger

24.8.4.4.1. Introducción

El ayudante FlashMessenger le permite pasar mensajes que el usuario pueda necesitar ver en la siguiente petición. Para lograr esto, FlashMessenger usa Zend_Session_Namespace para almacenar mensajes para su recuperación en una petición futura o en la siguiente. Generalmente es una buena idea, si planea usar Zend_Session o Zend_Session_Namespace, inicializar con Zend_Session::start() en su archivo de arranque (bootstrap). (Consulte la documentación de Zend_Session para más detalles sobre su uso.)

24.8.4.4.2. Métodos disponibles

Métodos generales:

  • setNamespace($namespace='default') se usa para establecer el espacio de nombres en el que se almacenan los mensajes por defecto.

  • getNamespace() se usa para recuperar el nombre del espacio de nombres por defecto. El espacio de nombres por defecto es 'default'.

  • resetNamespace() se usa para restablecer el nombre del espacio de nombres al valor por defecto, 'default'.

Métodos para manipular mensajes establecidos en la petición anterior:

  • hasMessages($namespace=NULL) se usa para determinar si se han transportado mensajes de una petición anterior mediante el flash messenger. El argumento opcional $namespace especifica en qué espacio de nombres buscar. Si se omite el argumento $namespace, se usará el valor devuelto por getNamespace().

  • getMessages($namespace=NULL) se usa para recuperar los mensajes que se han transportado de una petición anterior mediante el flash messenger. El argumento opcional $namespace especifica de qué espacio de nombres extraer. Si se omite el argumento $namespace, se usará el valor devuelto por getNamespace().

  • getIterator($namespace=NULL) envuelve el valor de retorno de getMessages() en una instancia de ArrayObject. Si se omite el argumento $namespace, se usará el valor devuelto por getNamespace().

  • count($namespace=NULL) devuelve el número de mensajes contenidos en el espacio de nombres especificado. Si se omite el argumento $namespace, se usará el valor devuelto por getNamespace().

  • clearMessages($namespace=NULL) se usa para borrar todos los mensajes que se han transportado de una petición anterior mediante el flash messenger. El argumento opcional $namespace especifica qué espacio de nombres borrar. Si se omite el argumento $namespace, se usará el valor devuelto por getNamespace().

Métodos para manipular mensajes establecidos en la petición actual:

  • addMessage($message, $namespace=NULL) se usa para agregar un nuevo mensaje a la petición actual. $message contiene el mensaje a agregar, y el argumento opcional $namespace especificará el espacio de nombres. Si se omite el argumento $namespace, se usará el valor devuelto por getNamespace().

  • hasCurrentMessages($namespace=NULL) se usa para determinar si se han agregado mensajes al flash messenger durante la petición actual. El argumento opcional $namespace especifica en qué espacio de nombres buscar. Si se omite el argumento $namespace, se usará el valor devuelto por getNamespace().

  • getCurrentMessages($namespace=NULL) se usa para recuperar los mensajes que se han agregado al flash messenger durante la petición actual. El argumento opcional $namespace especifica de qué espacio de nombres extraer. Si se omite el argumento $namespace, se usará el valor devuelto por getNamespace().

  • clearCurrentMessages($namespace=NULL) se usa para borrar todos los mensajes que se han agregado al flash messenger durante la petición actual. El argumento opcional $namespace especifica qué espacio de nombres borrar. Si se omite el argumento $namespace, se usará el valor devuelto por getNamespace().

24.8.4.4.3. Ejemplo de uso básico

El ejemplo de uso a continuación muestra el uso del flash messenger en su forma más básica. Cuando se llama a la acción /some/my, esta agrega el mensaje flash "Record Saved!" Una petición posterior a la acción /some/my-next-request lo recuperará (y por lo tanto también lo eliminará).

class SomeController extends Zend_Controller_Action
{
    /**
     * FlashMessenger
     *
     * @var Zend_Controller_Action_Helper_FlashMessenger
     */
    protected $_flashMessenger = null;

    public function init()
    {
        $this->_flashMessenger =
            $this->_helper->getHelper('FlashMessenger');
        $this->initView();
    }

    public function myAction()
    {
        /**
         * default method of getting
         * Zend_Controller_Action_Helper_FlashMessenger instance
         * on-demand
         */
        $this->_flashMessenger->addMessage('Record Saved!');
    }

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

24.8.4.5. JSON

Las respuestas JSON se están convirtiendo rápidamente en la respuesta preferida al tratar con peticiones AJAX que esperan respuestas de conjuntos de datos; JSON se puede analizar (parsear) de inmediato en el lado del cliente, lo que conduce a una ejecución rápida.

24.8.4.5.1. Uso

El uso es simple: ya sea llamándolo como un método del corredor de ayudantes, o llamando a uno de los métodos encodeJson() o sendJson():

direct($data, $sendNow = true, $keepLayouts = false, $encodeData = true)

sendJson($data, $keepLayouts = false, $encodeData = true)

encodeJson($data, $keepLayouts = false, $encodeData = true)

  • $data: datos a codificar como JSON

  • $sendNow: bandera para definir si se deben enviar los datos JSON de inmediato. Cuando es true, el ayudante establecerá inmediatamente el cuerpo de la respuesta y saldrá.

  • $keepLayouts: bandera para definir si se deben habilitar o deshabilitar los layouts. Cuando es false, se deshabilitan todos los layouts. Opcionalmente, esto puede ser un arreglo de opciones para pasar como segundo argumento a Zend_Json::encode(). Este arreglo de opciones permite habilitar los layouts y codificar usando Zend_Json_Expr.

  • $encodeData: bandera para definir si $data ya está codificado en JSON. Cuando es true, este ayudante no codificará $data a JSON antes de enviarlo.

[Note] Manteniendo los layouts

Si tiene un layout separado para las respuestas JSON -- quizás para envolver la respuesta JSON en algún tipo de contexto -- cada método en el ayudante JSON acepta un argumento opcional $keepLayouts: una bandera para habilitar o deshabilitar los layouts. Pasar un valor booleano TRUE mantendrá los layouts habilitados:

$this->_helper->json($data, true);

Opcionalmente, puede pasar un arreglo como tercer parámetro. Este arreglo puede contener una variedad de opciones, incluyendo la opción keepLayouts:

// Direct helper call
$this->_helper->json($data, true, array('keepLayouts' => true);

// ...or, call a method of the helper
$this->_helper->sendJson($data, array('keepLayouts' => true));
[Note] Habilitar la codificación usando Zend_Json_Expr

Zend_Json::encode() permite la codificación de expresiones nativas JSON usando objetos Zend_Json_Expr. Esta opción está deshabilitada por defecto. Para habilitar esta opción, pase un valor booleano TRUE a la opción enableJsonExprFinder:

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

Si desea hacer esto, debe pasar un arreglo como tercer argumento. Esto también le permite combinar otras opciones, como la opción keepLayouts. Todas esas opciones se pasan entonces a Zend_Json::encode().

$this->_helper->json($data, true, array(
'enableJsonExprFinder' => true,
'keepLayouts'          => true,
));
24.8.4.5.2. Ejemplo
class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // do some processing...
        // Send the JSON response:
        $this->_helper->json($data);

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

        // or retrieve the json:
        $json = $this->_helper->json->encodeJson($data);
    }
}

24.8.4.6. Redirector

24.8.4.6.1. Introducción

El ayudante Redirector le permite usar un objeto redirector para satisfacer las necesidades de su aplicación de redirigir a una nueva URL. Proporciona numerosas ventajas sobre el método _redirect(), como poder preconfigurar el comportamiento a nivel de sitio en el objeto redirector o usar la interfaz incorporada gotoSimple($action, $controller, $module, $params) similar a la de Zend_Controller_Action::_forward().

El Redirector tiene varios métodos que se pueden usar para afectar el comportamiento en la redirección:

  • setCode() se puede usar para establecer el código de respuesta HTTP a usar durante la redirección.

  • setExit() se puede usar para forzar un exit() tras una redirección. Por defecto esto es TRUE.

  • setGotoSimple() se puede usar para establecer una URL por defecto a usar si no se pasa ninguna a gotoSimple(). Usa la API de Zend_Controller_Action::_forward(): setGotoSimple($action, $controller = null, $module = null, array $params = array())

  • setGotoRoute() se puede usar para establecer una URL basada en una ruta registrada. Pase un arreglo de pares clave/valor y un nombre de ruta, y ensamblará la URL de acuerdo con el tipo y la definición de la ruta.

  • setGotoUrl() se puede usar para establecer una URL por defecto a usar si no se pasa ninguna a gotoUrl(). Acepta una única cadena URL.

  • setPrependBase() se puede usar para anteponer la URL base del objeto de petición a una URL especificada con setGotoUrl(), gotoUrl(), o gotoUrlAndExit().

  • setUseAbsoluteUri() se puede usar para forzar al Redirector a usar URIs absolutos al redirigir. Cuando se establece esta opción, usa el valor de $_SERVER['HTTP_HOST'], $_SERVER['SERVER_PORT'], y $_SERVER['HTTPS'] para formar una URI completa a la URL especificada por uno de los métodos de redirección. Esta opción está desactivada por defecto, pero puede habilitarse por defecto en versiones posteriores.

Además, hay una variedad de métodos en el redirector para realizar las redirecciones propiamente dichas:

  • gotoSimple() usa setGotoSimple() (API similar a _forward()) para construir una URL y realizar una redirección.

  • gotoRoute() usa setGotoRoute() (ensamblado de ruta) para construir una URL y realizar una redirección.

  • gotoUrl() usa setGotoUrl() (cadena URL) para construir una URL y realizar una redirección.

Finalmente, puede determinar la URL de redirección actual en cualquier momento usando getRedirectUrl().

24.8.4.6.2. Ejemplos de uso básico

Ejemplo 24.6. Establecer opciones

Este ejemplo sobrescribe varias opciones, incluyendo el establecimiento del código de estado HTTP a usar en la redirección ('303'), sin salir por defecto tras la redirección, y definiendo una URL por defecto a usar al redirigir.

class SomeController extends Zend_Controller_Action
{
    /**
     * Redirector - defined for code completion
     *
     * @var Zend_Controller_Action_Helper_Redirector
     */
    protected $_redirector = null;

    public function init()
    {
        $this->_redirector = $this->_helper->getHelper('Redirector');

        // Set the default options for the redirector
        // Since the object is registered in the helper broker, these
        // become relevant for all actions from this point forward
        $this->_redirector->setCode(303)
                          ->setExit(false)
                          ->setGotoSimple("this-action",
                                          "some-controller");
    }

    public function myAction()
    {
        /* do some stuff */

        // Redirect to a previously registered URL, and force an exit
        // to occur when done:
        $this->_redirector->redirectAndExit();
        return; // never reached
    }
}

Ejemplo 24.7. Uso de los valores por defecto

Este ejemplo asume que se usan los valores por defecto, lo que significa que cualquier redirección resultará en un exit() inmediato.

// ALTERNATIVE EXAMPLE
class AlternativeController extends Zend_Controller_Action
{
    /**
     * Redirector - defined for code completion
     *
     * @var Zend_Controller_Action_Helper_Redirector
     */
    protected $_redirector = null;

    public function init()
    {
        $this->_redirector = $this->_helper->getHelper('Redirector');
    }

    public function myAction()
    {
        /* do some stuff */

        $this->_redirector
            ->gotoUrl('/my-controller/my-action/param1/test/param2/test2');
        return; // never reached since default is to goto and exit
    }
}

Ejemplo 24.8. Uso de la API _forward() de goto()

La API de gotoSimple() imita la de Zend_Controller_Action::_forward(). La diferencia principal es que construye una URL a partir de los parámetros pasados, y usando el formato :module/:controller/:action/* por defecto del enrutador por defecto. Luego redirige en lugar de encadenar la acción.

class ForwardController extends Zend_Controller_Action
{
    /**
     * Redirector - defined for code completion
     *
     * @var Zend_Controller_Action_Helper_Redirector
     */
    protected $_redirector = null;

    public function init()
    {
        $this->_redirector = $this->_helper->getHelper('Redirector');
    }

    public function myAction()
    {
        /* do some stuff */

        // Redirect to 'my-action' of 'my-controller' in the current
        // module, using the params param1 => test and param2 => test2
        $this->_redirector->gotoSimple('my-action',
                                       'my-controller',
                                       null,
                                       array('param1' => 'test',
                                             'param2' => 'test2'
                                             )
                                       );
    }
}

Ejemplo 24.9. Uso del ensamblado de rutas con gotoRoute()

El siguiente ejemplo usa el método assemble() del enrutador para crear una URL basada en un arreglo asociativo de parámetros pasado. Se asume que se ha registrado la siguiente ruta:

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

Dado un arreglo con year establecido en 2006, month en 4, day en 24, e id en 42, entonces construiría la URL /blog/2006/4/24/42.

class BlogAdminController extends Zend_Controller_Action
{
    /**
     * Redirector - defined for code completion
     *
     * @var Zend_Controller_Action_Helper_Redirector
     */
    protected $_redirector = null;

    public function init()
    {
        $this->_redirector = $this->_helper->getHelper('Redirector');
    }

    public function returnAction()
    {
        /* do some stuff */

        // Redirect to blog archive. Builds the following 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. Introducción

El ayudante ViewRenderer está diseñado para satisfacer los siguientes objetivos:

  • Eliminar la necesidad de instanciar objetos de vista dentro de los controladores; los objetos de vista se registrarán automáticamente con el controlador.

  • Establecer automáticamente las rutas de scripts de vista, ayudantes y filtros basadas en el módulo actual, y asociar automáticamente el nombre del módulo actual como prefijo de clase para las clases de ayudantes y filtros.

  • Crear un objeto de vista globalmente disponible para todos los controladores y acciones despachados.

  • Permitir al desarrollador establecer opciones de renderización de vista por defecto para todos los controladores.

  • Agregar la capacidad de renderizar automáticamente un script de vista sin intervención.

  • Permitir al desarrollador crear sus propias especificaciones para la ruta base de la vista y para las rutas de scripts de vista.

[Note] Nota

Si realiza un _forward(), redirect(), o render() manualmente, la renderización automática no ocurrirá, ya que al realizar cualquiera de estas acciones le está indicando al ViewRenderer que usted está determinando su propia salida.

[Note] Nota

El ViewRenderer está habilitado por defecto. Puede deshabilitarlo mediante el parámetro noViewRenderer del controlador frontal ($front->setParam('noViewRenderer', true);) o eliminando el ayudante de la pila del corredor de ayudantes (Zend_Controller_Action_HelperBroker::removeHelper('viewRenderer')).

Si desea modificar la configuración del ViewRenderer antes de despachar el controlador frontal, puede hacerlo de una de dos maneras:

  • Instanciar y registrar su propio objeto ViewRenderer y pasarlo al corredor de ayudantes:

    $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
    $viewRenderer->setView($view)
                 ->setViewSuffix('php');
    Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
    
  • Inicializar y/o recuperar un objeto ViewRenderer bajo demanda a través del corredor de ayudantes:

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

En su uso más básico, simplemente instancia el ViewRenderer y lo pasa al corredor de ayudantes de acción. La forma más fácil de instanciarlo y registrarlo de una sola vez es usar el método getStaticHelper() del corredor de ayudantes:

Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');

La primera vez que se instancia un controlador de acción, activará al ViewRenderer para que instancie un objeto de vista. Cada vez que se instancia un controlador, se llama al método init() del ViewRenderer, lo cual hará que establezca la propiedad view del controlador de acción, y llame a addScriptPath() con una ruta relativa al módulo actual; esto se llamará con un prefijo de clase con el nombre del módulo actual, delimitando efectivamente todas las clases de ayudantes y filtros que defina para el módulo.

Cada vez que se llama a postDispatch(), este llamará a render() para la acción actual.

Como ejemplo, considere la siguiente clase:

// A controller class, foo module:
class Foo_BarController extends Zend_Controller_Action
{
    // Render bar/index.phtml by default; no action required
    public function indexAction()
    {
    }

    // Render bar/populate.phtml with variable 'foo' set to 'bar'.
    // Since view object defined at preDispatch(), it's already available.
    public function populateAction()
    {
        $this->view->foo = 'bar';
    }
}

...

// in one of your view scripts:
$this->foo(); // call Foo_View_Helper_Foo::foo()

El ViewRenderer también define una serie de accesores para permitir establecer y recuperar opciones de vista:

  • setView($view) le permite establecer el objeto de vista para el ViewRenderer. Se establece como la propiedad pública de clase $view.

  • setNeverRender($flag = true) se puede usar para deshabilitar o habilitar la renderización automática globalmente, es decir, para todos los controladores. Si se establece en TRUE, postDispatch() no llamará automáticamente a render() en el controlador actual. getNeverRender() recupera el valor actual.

  • setNoRender($flag = true) se puede usar para deshabilitar o habilitar la renderización automática. Si se establece en TRUE, postDispatch() no llamará automáticamente a render() en el controlador actual. Esta configuración se restablece cada vez que se llama a preDispatch() (es decir, necesita establecer esta bandera para cada controlador para el que no desee que ocurra la renderización automática). getNoRender() recupera el valor actual.

  • setNoController($flag = true) se puede usar para indicarle a render() que no busque el script de acción en un subdirectorio con el nombre del controlador (que es el comportamiento por defecto). getNoController() recupera el valor actual.

  • setNeverController($flag = true) es análogo a setNoController(), pero funciona a nivel global -- es decir, no se restablecerá para cada acción despachada. getNeverController() recupera el valor actual.

  • setScriptAction($name) se puede usar para especificar el script de acción a renderizar. $name debe ser el nombre del script sin el sufijo de archivo (y sin el subdirectorio del controlador, a menos que noController esté activado). Si no se especifica, busca un script de vista con el nombre de la acción en el objeto de petición. getScriptAction() recupera el valor actual.

  • setResponseSegment($name) se puede usar para especificar en qué segmento con nombre del objeto de respuesta renderizar. Si no se especifica, renderiza en el segmento por defecto. getResponseSegment() recupera el valor actual.

  • initView($path, $prefix, $options) se puede llamar para especificar la ruta base de la vista, el prefijo de clase para los scripts de ayudantes y filtros, y las opciones del ViewRenderer. Puede pasar cualquiera de las siguientes banderas: neverRender, noRender, noController, scriptAction, y responseSegment.

  • setRender($action = null, $name = null, $noController = false) le permite establecer cualquiera de scriptAction, responseSegment, y noController en una sola pasada. direct() es un alias de este método, lo que le permite llamarlo fácilmente desde su controlador:

    // Render 'foo' instead of current action script
    $this->_helper->viewRenderer('foo');
    
    // render form.phtml to the 'html' response segment, without using a
    // controller view script subdirectory:
    $this->_helper->viewRenderer('form', 'html', true);
    
    [Note] Nota

    setRender() y direct() en realidad no renderizan el script de vista, sino que establecen indicaciones que postDispatch() y render() usarán para renderizar la vista.

El constructor le permite pasar opcionalmente el objeto de vista y las opciones del ViewRenderer; acepta las mismas banderas que initView():

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

Hay varios métodos adicionales para personalizar las especificaciones de ruta usadas para determinar la ruta base de vista a agregar al objeto de vista, y la ruta del script de vista a usar al autodeterminar el script de vista a renderizar. Estos métodos toman cada uno uno o más de los siguientes marcadores de posición:

  • :moduleDir se refiere al directorio base del módulo actual (por convención, el directorio padre del directorio de controladores del módulo).

  • :module se refiere al nombre del módulo actual.

  • :controller se refiere al nombre del controlador actual.

  • :action se refiere al nombre de la acción actual.

  • :suffix se refiere al sufijo del script de vista (que se puede establecer mediante setViewSuffix()).

Los métodos para controlar las especificaciones de ruta son:

  • setViewBasePathSpec($spec) le permite cambiar la especificación de ruta usada para determinar la ruta base a agregar al objeto de vista. La especificación por defecto es :moduleDir/views. Puede recuperar la especificación actual en cualquier momento usando getViewBasePathSpec().

  • setViewScriptPathSpec($spec) le permite cambiar la especificación de ruta usada para determinar la ruta a un script de vista individual (sin la ruta base del script de vista). La especificación por defecto es :controller/:action.:suffix. Puede recuperar la especificación actual en cualquier momento usando getViewScriptPathSpec().

  • setViewScriptPathNoControllerSpec($spec) le permite cambiar la especificación de ruta usada para determinar la ruta a un script de vista individual cuando noController está activo (sin la ruta base del script de vista). La especificación por defecto es :action.:suffix. Puede recuperar la especificación actual en cualquier momento usando getViewScriptPathNoControllerSpec().

Para un control más detallado sobre las especificaciones de ruta, puede usar Zend_Filter_Inflector. Internamente, el ViewRenderer ya utiliza un inflector para realizar los mapeos de ruta. Para interactuar con el inflector -- ya sea para establecer el suyo propio, o para modificar el inflector por defecto -- se pueden usar los siguientes métodos:

  • getInflector() recuperará el inflector. Si aún no existe ninguno en el ViewRenderer, crea uno usando las reglas por defecto.

    Por defecto, usa referencias de reglas estáticas para el sufijo y el directorio del módulo, así como un objetivo estático; esto permite que varias propiedades del ViewRenderer puedan modificar dinámicamente el inflector.

  • setInflector($inflector, $reference) le permite establecer un inflector personalizado para usar con el ViewRenderer. Si $reference es TRUE, establecerá el sufijo y el directorio del módulo como referencias estáticas a propiedades del ViewRenderer, así como el objetivo.

[Note] Convenciones de búsqueda por defecto

El ViewRenderer realiza cierta normalización de rutas para facilitar las búsquedas de scripts de vista. Las reglas por defecto son las siguientes:

  • :module: MixedCase y camelCasedWords se separan con guiones, y toda la cadena se convierte a minúsculas. Por ejemplo: "FooBarBaz" se convierte en "foo-bar-baz".

    Internamente, el inflector usa los filtros Zend_Filter_Word_CamelCaseToDash y Zend_Filter_StringToLower.

  • :controller: MixedCase y camelCasedWords se separan con guiones; los guiones bajos se convierten en separadores de directorio, y toda la cadena se convierte a minúsculas. Ejemplos: "FooBar" se convierte en "foo-bar"; "FooBar_Admin" se convierte en "foo-bar/admin".

    Internamente, el inflector usa los filtros Zend_Filter_Word_CamelCaseToDash, Zend_Filter_Word_UnderscoreToSeparator, y Zend_Filter_StringToLower.

  • :action: MixedCase y camelCasedWords se separan con guiones; los caracteres no alfanuméricos se traducen a guiones, y toda la cadena se convierte a minúsculas. Ejemplos: "fooBar" se convierte en "foo-bar"; "foo-barBaz" se convierte en "foo-bar-baz".

    Internamente, el inflector usa los filtros Zend_Filter_Word_CamelCaseToDash, Zend_Filter_PregReplace, y Zend_Filter_StringToLower.

Los elementos finales en la API del ViewRenderer son los métodos para determinar realmente las rutas de scripts de vista y renderizar vistas. Estos incluyen:

  • renderScript($script, $name) le permite renderizar un script con una ruta que usted especifique, opcionalmente a una ruta con nombre. Al usar este método, el ViewRenderer no realiza autodeterminación del nombre del script, sino que pasa directamente el argumento $script al método render() del objeto de vista.

    [Note] Nota

    Una vez que la vista se ha renderizado al objeto de respuesta, establece noRender para evitar renderizar accidentalmente el mismo script de vista varias veces.

    [Note] Nota

    Por defecto, Zend_Controller_Action::renderScript() delega en el método renderScript() del ViewRenderer.

  • getViewScript($action, $vars) crea la ruta a un script de vista basada en la acción pasada y/o cualquier variable pasada en $vars. Las claves de este arreglo pueden incluir cualquiera de las claves de especificación de ruta ('moduleDir', 'module', 'controller', 'action', y 'suffix'). Se usarán las variables que se pasen; de lo contrario, se usarán valores basados en la petición actual.

    getViewScript() usará ya sea viewScriptPathSpec o viewScriptPathNoControllerSpec según la configuración de la bandera noController.

    Los delimitadores de palabras que aparezcan en los nombres de módulo, controlador o acción se reemplazarán con guiones ('-'). Así, si tiene el nombre de controlador 'foo.bar' y la acción 'baz:bat', usando la especificación de ruta por defecto se obtendrá una ruta de script de vista de 'foo-bar/baz-bat.phtml'.

    [Note] Nota

    Por defecto, Zend_Controller_Action::getViewScript() delega en el método getViewScript() del ViewRenderer.

  • render($action, $name, $noController) verifica primero si se han pasado $name o $noController, y si es así, establece las banderas apropiadas (responseSegment y noController, respectivamente) en el ViewRenderer. Luego pasa el argumento $action, si lo hay, a getViewScript(). Finalmente, pasa la ruta del script de vista calculada a renderScript().

    [Note] Nota

    Tenga en cuenta los efectos secundarios de usar render(): los valores que pase para el nombre del segmento de respuesta y para la bandera noController persistirán en el objeto. Además, noRender se establecerá una vez completada la renderización.

    [Note] Nota

    Por defecto, Zend_Controller_Action::render() delega en el método render() del ViewRenderer.

  • renderBySpec($action, $vars, $name) le permite pasar variables de especificación de ruta para determinar la ruta del script de vista a crear. Pasa $action y $vars a getScriptPath(), y luego pasa la ruta de script resultante y $name a renderScript().

24.8.4.7.3. Ejemplos de uso básico

Ejemplo 24.10. Uso básico

En su forma más básica, simplemente inicializa y registra el ayudante ViewRenderer con el corredor de ayudantes en su bootstrap, y luego establece variables en sus métodos de acción.

// In your bootstrap:
Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');

...

// 'foo' module, 'bar' controller:
class Foo_BarController extends Zend_Controller_Action
{
    // Render bar/index.phtml by default; no action required
    public function indexAction()
    {
    }

    // Render bar/populate.phtml with variable 'foo' set to 'bar'.
    // Since view object defined at preDispatch(), it's already available.
    public function populateAction()
    {
        $this->view->foo = 'bar';
    }

    // Renders nothing as it forwards to another action; the new action
    // will perform any rendering
    public function bazAction()
    {
        $this->_forward('index');
    }

    // Renders nothing as it redirects to another location
    public function batAction()
    {
        $this->_redirect('/index');
    }
}

[Note] Convenciones de nomenclatura: delimitadores de palabras en nombres de controlador y acción

Si el nombre de su controlador o acción está compuesto por varias palabras, el despachador requiere que estas se separen en la URL mediante caracteres delimitadores de ruta y de palabra específicos. El ViewRenderer reemplaza cualquier delimitador de ruta encontrado en el nombre del controlador con un delimitador de ruta real ('/'), y cualquier delimitador de palabra encontrado con un guion ('-') al crear rutas. Así, una llamada a la acción /foo.bar/baz.bat despacharía a FooBarController::bazBatAction() en FooBarController.php, la cual renderizaría foo-bar/baz-bat.phtml; una llamada a la acción /bar_baz/baz-bat despacharía a Bar_BazController::bazBatAction() en Bar/BazController.php (observe la separación de ruta) y renderizaría bar/baz/baz-bat.phtml.

Observe que en el segundo ejemplo, el módulo sigue siendo el módulo por defecto, pero que, debido a la existencia de un separador de ruta, el controlador recibe el nombre Bar_BazController, en Bar/BazController.php. El ViewRenderer imita la jerarquía de directorios del controlador.

Ejemplo 24.11. Deshabilitar la renderización automática

Para algunas acciones o controladores, es posible que desee desactivar la renderización automática -- por ejemplo, si desea emitir un tipo de salida diferente (XML, JSON, etc), o si simplemente desea no emitir nada. Tiene dos opciones: desactivar todos los casos de renderización automática (setNeverRender()), o simplemente desactivarla para la acción actual (setNoRender()).

// Baz controller class, bar module:
class Bar_BazController extends Zend_Controller_Action
{
    public function fooAction()
    {
        // Don't auto render this action
        $this->_helper->viewRenderer->setNoRender();
    }
}

// Bat controller class, bar module:
class Bar_BatController extends Zend_Controller_Action
{
    public function preDispatch()
    {
        // Never auto render this controller's actions
        $this->_helper->viewRenderer->setNoRender();
    }
}

[Note] Nota

En la mayoría de los casos, no tiene sentido desactivar la renderización automática globalmente (mediante setNeverRender()), ya que lo único que se obtiene entonces del ViewRenderer es la configuración automática del objeto de vista.

Ejemplo 24.12. Elegir un script de vista diferente

Algunas situaciones requieren que se renderice un script diferente al que lleva el nombre de la acción. Por ejemplo, si tiene un controlador con acciones tanto de agregar como de editar, ambas pueden mostrar la misma vista 'form', aunque con valores diferentes establecidos. Puede cambiar fácilmente el nombre del script usado con setScriptAction(), setRender(), o llamando al ayudante como un método, lo cual invocará a setRender().

// Bar controller class, foo module:
class Foo_BarController extends Zend_Controller_Action
{
    public function addAction()
    {
        // Render 'bar/form.phtml' instead of 'bar/add.phtml'
        $this->_helper->viewRenderer('form');
    }

    public function editAction()
    {
        // Render 'bar/form.phtml' instead of 'bar/edit.phtml'
        $this->_helper->viewRenderer->setScriptAction('form');
    }

    public function processAction()
    {
        // do some validation...
        if (!$valid) {
            // Render 'bar/form.phtml' instead of 'bar/process.phtml'
            $this->_helper->viewRenderer->setRender('form');
            return;
        }

        // otherwise continue processing...
    }

}

Ejemplo 24.13. Modificar la vista registrada

¿Qué sucede si necesita modificar el objeto de vista -- por ejemplo, cambiar las rutas de ayudantes, o la codificación? Puede hacerlo modificando el objeto de vista establecido en su controlador, o tomando el objeto de vista del ViewRenderer; ambos son referencias al mismo objeto.

// Bar controller class, foo module:
class Foo_BarController extends Zend_Controller_Action
{
    public function preDispatch()
    {
        // change view encoding
        $this->view->setEncoding('UTF-8');
    }

    public function bazAction()
    {
        // Get view object and set escape callback to 'htmlspecialchars'
        $view = $this->_helper->viewRenderer->view;
        $view->setEscape('htmlspecialchars');
    }
}

24.8.4.7.4. Ejemplos de uso avanzado

Ejemplo 24.14. Cambiar las especificaciones de ruta

En algunas circunstancias, puede decidir que las especificaciones de ruta por defecto no se ajustan a las necesidades de su sitio. Por ejemplo, es posible que desee tener un único árbol de plantillas al cual dar acceso a sus diseñadores (esto es muy típico al usar Smarty, por ejemplo). En tal caso, es posible que desee codificar de forma fija la especificación de la ruta base de vista, y crear una especificación alternativa para las rutas de los scripts de vista de las acciones en sí.

Para efectos de este ejemplo, supongamos que la ruta base a las vistas debe ser '/opt/vendor/templates', y que desea que los scripts de vista se referencien mediante ':moduleDir/:controller/:action.:suffix'; si se ha establecido la bandera noController, desea renderizar desde el nivel superior en lugar de en un subdirectorio (':action.:suffix'). Finalmente, desea usar 'tpl' como sufijo del nombre de archivo del script de vista.

/**
 * In your bootstrap:
 */

// Different view implementation
$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);

Ejemplo 24.15. Renderizar múltiples scripts de vista desde una única acción

A veces, puede necesitar renderizar múltiples scripts de vista desde una única acción. Esto es muy sencillo -- simplemente realice múltiples llamadas a render():

class SearchController extends Zend_Controller_Action
{
    public function resultsAction()
    {
        // Assume $this->model is the current model
        $this->view->results =
            $this->model->find($this->_getParam('query', '');

        // render() by default proxies to the ViewRenderer
        // Render first the search form and then the results
        $this->render('form');
        $this->render('results');
    }

    public function formAction()
    {
        // do nothing; ViewRenderer autorenders the view script
    }
}

24.8.5. Escribir sus propios ayudantes

Los ayudantes de acción extienden Zend_Controller_Action_Helper_Abstract, una clase abstracta que proporciona la interfaz y funcionalidad básicas requeridas por el corredor de ayudantes. Estas incluyen los siguientes métodos:

  • setActionController() se usa para establecer el controlador de acción actual.

  • init(), activado por el corredor de ayudantes en la instanciación, se puede usar para activar la inicialización en el ayudante; esto puede ser útil para restablecer el estado cuando varios controladores usan el mismo ayudante en acciones encadenadas.

  • preDispatch(), se activa antes de una acción despachada.

  • postDispatch() se activa cuando una acción despachada ha terminado -- incluso si un plugin preDispatch() ha omitido la acción. Principalmente útil para la limpieza.

  • getRequest() recupera el objeto de petición actual.

  • getResponse() recupera el objeto de respuesta actual.

  • getName() recupera el nombre del ayudante. Recupera la porción del nombre de clase que sigue al último carácter de guion bajo, o el nombre completo de la clase en caso contrario. Como ejemplo, si la clase se llama Zend_Controller_Action_Helper_Redirector, devolverá Redirector; una clase llamada FooMessage simplemente se devolverá a sí misma.

Opcionalmente, puede incluir un método direct() en su clase de ayudante. Si se define, le permite tratar al ayudante como un método del corredor de ayudantes, con el fin de permitir un uso fácil y puntual del ayudante. Como ejemplo, el redirector define direct() como un alias de goto(), permitiendo usar el ayudante así:

// Redirect to /blog/view/item/id/42
$this->_helper->redirector('item', 'view', 'blog', array('id' => 42));

Internamente, el método __call() del corredor de ayudantes busca un ayudante llamado redirector, luego verifica si ese ayudante tiene un método direct() definido, y lo invoca con los argumentos proporcionados.

Una vez que haya creado su propia clase de ayudante, puede proporcionar acceso a ella como se describe en las secciones anteriores.