Como vimos en el primer artículo de la serie, el controlador responde a acciones del usuario y se comunica con los modelos y la vista. En Zend_Framework, los controladores son clases que extienden de Zend_Controller_Action y pueden tener uno o más action methods.

Tareas de inicialización

Zend_Controller_Action tiene un método especial llamado init(), que es el último método que se ejecuta en el constructor, en el que podemos poner todo lo que deseemos para personalizar la instanciación del controlador.

Por ejemplo, si queremos setear cierta información sobre el usuario actual:

 class IndexController extends Zend_Controller_Action {

protected $_idUsuario;

    // este método se ejecuta siempre, por lo que en cualquier action podremos hacer uso de $this->_idUsuario
    // para acceder al id del usuario actualmente logueado
    public function init() {
        $this->_idUsuario = Zend_Auth::getInstance()->getIdentity()->id_usuario;
    }

}

PreDispatch y PostDispatch

Los métodos preDispatch() y postDispatch() se ejecutan antes y después de realizar el dispatch del action actual.

El preDispatch() se suele usar para hacer controles sobre los permisos del usuario logueado, y el postDispatch() para agregar contenido a la vista (por medio del response object), setear headers especiales y muchas otras acciones que se nos pueden ocurrir.

Accediendo al Request y al Response Object

El request (Zend_Controller_Request_Abstract) contiene información como por ejemplo el nombre del action / módulo / controlador actual, la url, parámetros recibidos por GET y POST, etc. Accedemos con el método getRequest():

Algunos métodos que nos pueden interesar del request object:

 $this->getRequest()->getActionName(); // devuelve el nombre del action actual
$this->getRequest()->getControllerName(); // el nombre del controller
$this->getRequest()->getModuleName(); // el nombre del módulo
$this->getRequest()->getRequestUri(); // la url actual
$this->getRequest()->isPost(); // si el request fue hecho por el método POST
$this->getRequest()->isXmlHttpRequest() // o fue hecho por ajax?

El response (Zend_Controller_Response_Abstract) setea y devuelve los headers del sitio y el contenido HTML del mismo, accedemos a el con el método getResponse():

 $this->getRequest()->setHttpResponseCode(); // para setear por ejemplo un 404, 301, etc.
$this->getRequest()->getHttpResponseCode(); // devuelve el HTTP response code actual
$this->getRequest()->appendBody($content); // para agregar contenido al body
$this->getRequest()->clearBody(); // para borrar todo el contenido generado hasta el momento

Por ahora esto puede parecer bastante avanzado (sobre todo el uso del response object), pero esta bueno que sepamos que existe. En el futuro veremos cosas realmente interesantes que podemos hacer con ellos y entenderemos mejor su uso y aplicación.

_forward() y _redirect()

Se podría decir que estos dos métodos, conocidos como “utility methods”, tienen cierto parecido: _forward ejecuta otro action. Si lo llamamos durante el preDispatch(), el action actual sera reemplazado por el nuevo. Sino, el nuevo sera ejecutado a continuación. Su sintaxis es:

_forward($action, $controller = null, $module = null, array $params = null)

Por su parte, _redirect() recibe una url y realiza un redirect http, terminando la ejecución del script actual:

_redirect($url, array $options = array())

Action Helpers

Al igual que los View Helpers, los Action Helpers permiten agregar funcionalidades extras al controlador en tiempo de ejecución, sin tener que modificar al controlador propiamente dicho ni hacer una clase propia que extienda Zend_Controller_Action. Esto es algo MUY bueno, la posibilidad de agregar funcionalidades y comportamientos sin tener que modificar la clase base significa que tenemos menos cosas que testear y nuestro código es mas pequeño y reutilizable.

Pero tiene algunas pequeñas diferencias con los View Helpers en cuanto a la forma en que fueron desarrollados. Los view helpers funcionan sobre el magic method __call, entonces al momento de llamar un método que no existe sabemos que es un view helper y Zend_View se encarga de buscarlo y de cargarlo. En cambio, el método __call de Zend_Controller_Action tiene otro uso, por lo que no se puede usar para cargar los action helpers. En su lugar, estos deben ser registrados manualmente y son almacenados en el atributo $_helper de Zend_Controller_Action.

ZF trae por defecto unos cuantos action helpers, los cuales veremos de que se tratan:

  • ActionStack: como su nombre lo indica, permite agregar requests al ActionStack Front Controller Plugin, permitiendo crear una pila de actions a ejecutar durante el request. Sin embargo su uso no es recomendado ya que tiene una importante penalización de rendimiento, entre otras contras, como ya explicamos en un articulo anterior cuando hablamos sobre el Action View Helper.
  • ContextSwitch y AjaxContext: estos helpers permiten retornar diferentes tipos de formatos en el request, ya sea json, xml, html, etc. El AjaxContext es una implementación especifica del ContextSwitch que reconoce automáticamente request que fueron hechas por AJAX.
  • Autocomplete, AutoCompleteDojo y AutoCompleteScriptaculous: la mayoría de los frameworks de JavaScript cuentan con un autocomplete, pero no todos esperan el mismo tipo de datos (algunos trabajan con JSON, otros con texto plano, etc). El Autocomplete Action Helper se encarga de simplificarnos esta tarea, brindándonos una funcionalidad base que luego podremos extender para acomodarla a los requerimientos del framework que usemos. ZF trae por defecto soporte para los autocompletes de dojo y scriptaculous.
  • FlashMessenger: este es uno de los helpers mas usados, permite enviarle al usuario un mensaje que será mostrado en el próximo request, es decir la próxima vez que cargue la página. Por ejemplo, si vamos a hacer un redirect a otra página de nuestro sitio con el flashMessenger podremos generar un mensaje que será mostrado una vez que esta otra página se cargue.
  • JSON: al trabajar con AJAX es muy común enviar datos en JSON, este helper recibe como parámetros los datos a encodear y se encarga de otras tareas extras como deshabilitar el layout, setear el header content-type a ‘text/json’, etc.
  • URL: entre otras cosas, crea URLs para usar, por ejemplo, al hacer un redirect, en base al módulo / controlador / action actual, la url base y otros parámetros recibidos.
  • Redirector: este helper es implícitamente llamado al hacer un _redirect(), pero tiene muchas mas opciones como poder especificar el código de respuesta HTTP, forzar o no un exit() luego del redirect, etc.
  • ViewRenderer: este es quizás el helper mas usado aunque no nos demos cuenta, ya que registra automáticamente la vista en el controlador, setea los paths relacionados a la vista, crea un objeto del tipo Zend_View global para toda la aplicación, etc.

Estos helpers que vienen por defecto pueden ser accesibles en cualquier momento de las siguientes maneras, por ejemplo para llamar al FlashMessenger:

$flashMessenger = $this->_helper->getHelper('FlashMessenger');

o la forma mas corta:

$flashMessenger = $this->_helper->flashMessenger

El método direct()

Este método permite ejecutar un método del helper como si fuera un método del helper broker. ¿Un poco confuso? veamos un ejemplo.

Lo “común” sería hacer:

$this->_helper->flashMessenger->addMessage("123");

Pero el flashMessenger implementa el método direct, que al ser llamado trabaja como un alias de addMessage:

// Zend/Controller/Action/Helper/FlashMessenger.php
    public function direct($message)
    {
        return $this->addMessage($message);
    }

Entonces en vez de llamar directamente al addMessage() podemos hacer esto:

$this->_helper->flashMessenger("123");

$this->_helper es el Helper Broker, que por sobrecarga de métodos verifica si lo que se esta llamando es un helper existente, y de ser así ejecuta el método direct con los parámetros que recibe.

El Helper Broker

Zend_Controller_Action_HelperBroker es el encargado de manejar los action helpers: registrarlos, retornarlos, controlar si existe uno determinado, etc.

Para registrar un helper llamamos el método estático addHelper():

Zend_Controller_Action_HelperBroker::addHelper(new Mdw_Action_Helper_Ejemplo);

Sin embargo hay otras dos opciones que son mas eficientes ya que no requieren que el helper sea instanciado en el momento:

addPrefix(): en base a los prefijos que recibe como parámetro, al momento de cargar un helper resolverá el path en el que se encuentra, lo instanciará y lo devolverá:

 Zend_Controller_Action_HelperBroker::addPrefix('My_Action_Helpers');

addPath(): recibe como parámetro un directorio y un prefijo en el que buscar los helpers, permitiendonos buscar clases con un determinado prefijo en un directorio establecido por nosotros:

 Zend_Controller_Action_HelperBroker::addPath('/Plugins/Helpers', 'Helper');

Creando un Helper

Crear un action helper propio es sumamente fácil. Nuestra clase debe extender de Zend_Controller_Action_Helper_Abstract, luego tenemos que registrarla por alguno de los métodos anteriormente vistos y listo.

Front Controller Plugins

El Front Controller atraviesa distintos estados y por cada uno de estos se activan uno o varios eventos (también llamados hooks). Estos eventos son:

  • routeStartup(): antes de inicializar el routing del request
  • routeShutdown(): al finalizar el routing del request
  • dispatchLoopStartup(): antes de entrar al dispatch loop
  • preDispatch(): antes de realizar el dispatch de un action
  • postDispatch(): después de realizar el dispatch de un action
  • dispatchLoopShutdown(): al terminar el dispatch loop

Dicho esto, un plugin es simplemente una clase que extiende de Zend_Controller_Plugin_Abstract, clase base que define todos estos hooks. Nuestro plugin sobreescribirá los métodos deseados para agregar la funcionalidad que querramos. A excepción del dispatchLoopShutdown(), todos los demás métodos reciben al $request como primer parámetro.

Registrando Plugins

Los plugins necesitan ser registrados manualmente en el FrontController:

$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Mdw_Plugin(), $index);

El segundo parámetro especifica un índice dentro del stack de plugins. Este parámetro es opcional y alterara el orden en que son ejecutados los plugins, si no se completa se van ejecutando en el orden en que fueron agregados. Según las tareas que estemos haciendo, puede llegar a ser importante que un plugin se ejecute antes que otro, así que no hay que perder esto de vista.

¿Qué tareas suelen hacer? Las tareas más comunes son controlar si el usuario esta logueado o tiene acceso a ciertas páginas, cachear, modificar el HTML generado, etc.

Ir al siguiente capítulo: Vistas, View Helpers y Layout