# Controladores de Acción

## Introducción

`Zend_Controller_Action` es una clase abstracta que puede utilizar para implementar controladores de acción (Action Controllers) para usar con el Front Controller al crear un un sitio basado en el patrón Modelo-Vista-Controlador ( MVC ).

Para usar `Zend_Controller_Action` , necesitará hacerla una subclase en sus clases actuales de controladores de acción (o hacerla una subclase para crear su propia clase base de acción de controladores). La operación más elemental es hacerla una subclase, y crear métodos de acción que corresponden a las diversas acciones que desee que el contralor maneje para su sitio. El manejo del ruteo y envío de `Zend_Controller` descubrirá por sí mismo cualquier método que termine en 'Action' en su clase, como posibles acciones del controlador.

Por ejemplo, digamos que su clase se define como sigue:

    class FooController extends Zend_Controller_Action
    {
        public function barAction()
        {
            // hacer algo
        }

        public function bazAction()
        {
            // hacer algo
        }
    }

La clase de arriba FooController (el controlador foo ) define dos acciones, bar y baz .

Se pueden lograr muchas cosas más, tales como personalizar la inicialización de acciones, las acciones a llamar por defecto no deberían especificar ninguna acción (o una acción inválida), ganchos de pre y post despacho, y una variedad de métodos ayudantes. Este capítulo sirve como panorama de la funcionalidad del controlador de acciones.

### Comportamiento por Defecto

Por defecto, el front controller habilita al ayudante de acción ViewRenderer . Este ayudante toma a su cargo la inyección del objeto "view" en el contralor, así como compatibilizar automáticamente las vistas. Usted podrá desactivarlo dentro de su contralor de acción por uno de los métodos siguientes:

    class FooController extends Zend_Controller_Action
    {
        public function init()
        {
            // Local a este controlador únicamente; afecta a todas las acciones
            // al cargarse en init:
            $this->_helper->viewRenderer->setNoRender(true);

            // Globalmente:
            $this->_helper->removeHelper('viewRenderer');

            // También globalmente, pero tendría que ser en conjunción con la
            // versión local con el fin de propagarlo para este controlador:
            Zend_Controller_Front::getInstance()
                ->setParam('noViewRenderer', true);
        }
    }

`initView()` , `getViewScript()` , `render()` , y `renderScript()` cada proxy al ViewRenderer a menos que el ayudante no esté como ayudante intermediario o no se haya establecido el flag de noViewRenderer .

También puede simplemente desactivarse para una prestación individual ajustando el flag noRender de ViewRenderer :

    class FooController extends Zend_Controller_Action
    {
        public function barAction()
        {
            // deshabilitar el autorendering para esta acción solamente:
            $this->_helper->viewRenderer->setNoRender();
        }
    }

Las principales razones para desactivar ViewRenderer son si usted simplemente no necesita una objeto "view" o si no está mostrándolos via view scripts (por ejemplo, cuando se utiliza un controlador de acción para alimentar a los protocolos de un servicio web como SOAP , XML-RPC , o REST ). En muchos casos, nunca necesitará desactivar a ViewRenderer globalmente, sólo selectivamente dentro de los distintos controladores o acciones.

## Inicialización de Objectos

Si bien siempre puede anular el contolador de acción del constructor, no lo recomendamos. `Zend_Controller_Action::__construct()` realiza algunas tareas importantes, tales como registrar los objetos de solicitud y respuesta, así como los argumentos de cualquier invocación personalizada pasados desde el front controller. Si debe anular el constructor, asegúrese de llamar a `parent::__construct($request, $response, $invokeArgs)` .

La manera más apropiada de personalizar la instanciación es utilizar el método `init()` , el cual es llamado como la última tarea de `__construct()` . Por ejemplo, si se quiere conectar a una base de datos en la instanciación:

    class FooController extends Zend_Controller_Action
    {
        public function init()
        {
            $this->db = Zend_Db::factory('Pdo_Mysql', array(
                'host'     => 'myhost',
                'username' => 'user',
                'password' => 'XXXXXXX',
                'dbname'   => 'website'
            ));
        }
    }

## Ganchos de Pre- and Post-Despacho

`Zend_Controller_Action` especifica dos métodos que pueden ser llamados para marcar una solicitud de acción, `preDispatch()` y `postDispatch()` . Estas pueden ser útiles de varias maneras: verificar la autenticación y ACL s antes de ejecutar una acción (llamando a `_forward()` en `preDispatch()` , se saltará la acción), por ejemplo, o colocando contenido generado en una plantilla general del sitio ( `postDispatch()` ).

### Usage of init() vs. preDispatch()

In the previous section , we introduced the `init()` method, and in this section, the `preDispatch()` method. What is the difference between them, and what actions would you take in each?

The `init()` method is primarily intended for extending the constructor. Typically, your constructor should simply set object state, and not perform much logic. This might include initializing resources used in the controller (such as models, configuration objects, etc.), or assigning values retrieved from the front controller, bootstrap, or a registry.

The `preDispatch()` method can also be used to set object or environmental (e.g., view, action helper, etc.) state, but its primary purpose is to make decisions about whether or not the requested action should be dispatched. If not, you should then `_forward()` to another action, or throw an exception.

Note: `_forward()` actually will not work correctly when executed from `init()` , which is a formalization of the intentions of the two methods.

## Accessors (Accededores)

Con el objeto, se registran una serie de objetos y variables, y cada uno tiene métodos de acceso.

- Objecto Requerimiento : `getRequest()` puede ser utilizado para recuperar el objeto solicitud utilizado para llamar a la acción.

- Objecto Respuesta : `getResponse()` puede ser utilizado para recuperar el objeto respuesta agregando la respuesta final. Algunas llamadas típicas podrían ser:

      $this->getResponse()->setHeader('Content-Type', 'text/xml');
      $this->getResponse()->appendBody($content);

- Argumentos de Invocación : el front controller puede empujar parámetros al router, al despachador, y al controlador de acción. Para recuperarlos, use `getInvokeArg($key)` ; por otra parte, se puede traer toda la lista utilizando `getInvokeArgs()` .

- Parámetros de Requerimientos : La objeto solicitud agrega parámetros de solicitud, como cualquiera de los parámetros `_GET` o `_POST` , o parámetros del usuario especificados en la información del path de la URL . Para recuperarlos, use `_getParam($key)` o `_getAllParams()` . También se pueden establecer parámetros de solicitud usando `_setParam()` ; lo que es útil cuando se reenvían a acciones adicionales.

  Para probar si un parámetro existe o no (muy útil para bifurcaciones lógicas), use `_hasParam($key)` .

  `_getParam()` puede tomar opcionalmente un segundo argumento que contiene un valor por defecto a utilizar si el parámetro no está establecido o está vacío. Usándolo elimina la necesidad de llamar previamente a `_hasParam()` para recuperar un valor:

      // Usar por defecto el valor 1 si el id no está establecido
      $id = $this->_getParam('id', 1);

      // En lugar de:
      if ($this->_hasParam('id') {
          $id = $this->_getParam('id');
      } else {
          $id = 1;
      }

## Integración de Vistas

### Integración de la Vista por Defecto via ViewRenderer

El contenido de esta sección sólo es válida cuando usted tiene explícitamente deshabilitado a ViewRenderer . De lo contrario, puede saltarse esta sección.

`Zend_Controller_Action` proporciona un mecanismo rudimentario y flexible para ver la integración. Hay dos métodos para lograrlo, `initView()` y `render()` ; el anterior método `$view` carga la propiedad pública, y este último muestra una vista en base a la acción requerida actual, utilizando la jerarquía del directorio para determinar el path del script.

### Inicialización de la Vista

`initView()` inicializa el objeto vista. `render()` llama a `initView()` con el fin de recuperar el objeto vista, pero puede ser iniciada en cualquier momento; por defecto introduce información a la propiedad de `$view` con un objeto `Zend_View` , pero se puede usar cualquier clase que implemente `Zend_View_Interface` . Si `$view` ya ha sido inicializada, simplemente devuelve esa propiedad.

La implementación por defecto hace la siguiente hipótesis de la estructura del directorio:

    applicationOrModule/
        controllers/
            IndexController.php
        views/
            scripts/
                index/
                    index.phtml
            helpers/
            filters/

En otras palabras, los scripts de vista se supone están en el subdirectorio `/views/scripts/` , y en el subdirectorio `/views/` se supone que contiene funcionalidades hermanas (ayudantes, filtros). Al determinar el nombre y el path del script, el directorio `views/scripts/` será utilizado como el path base, con directorios nombrados después que los controladores individuales proporcionen una jerarquía a los scripts de vista.

### Suministrando las Vistas

`render()` tiene la siguiente firma:

    string render(string $action = null,
                  string $name = null,
                  bool $noController = false);

`render()` suministra un script de vista. Si no se pasan argumentos, se supone que el script requerido es `[controller]/[action].phtml` (donde `.phtml` es el valor de la propiedad `$viewSuffix` ). Pasándole un valor a `$action` suministrará esa plantilla en al subdirectorio `/[controller]/` . Para anular el subdirectorio `/[controller]/` ponga un valor `TRUE` en `$noController` . Por último, las plantillas son suministradas en el objeto respuesta; si desea suministrar a un determinado named segment en el objeto respuesta, pase un valor a `$name` .

Dado que el controlador y los nombres de acción pueden contener caracteres delimitadores como '_', '.', y '-', `render()` los normaliza a '-' para determinar el nombre del script. Internamente, utiliza los delimitadores de palabra y de path del despachador para hacer esta normalización. Así, una solicitud a `/foo.bar/baz-bat` suministrará el script `foo-bar/baz-bat.phtml` . Si su método de acción contiene camelCasing, recuerde que esto se traducirá en palabras separadas por '-' al determinar el nombre del archivo del script de vista.

Algunos ejemplos:

    class MyController extends Zend_Controller_Action
    {
        public function fooAction()
        {
            // Suministra my/foo.phtml
            $this->render();

            // Suministra my/bar.phtml
            $this->render('bar');

            // Suministra baz.phtml
            $this->render('baz', null, true);

            // Suministra my/login.phtml al segmento 'form' del
            // objeto respuesta
            $this->render('login', 'form');

            // Suministra site.phtml al segmento 'page' del objeto
            // respuesta; no usa el subdirectorio 'my/'
            $this->render('site', 'page', true);
        }

        public function bazBatAction()
        {
            // Suministra my/baz-bat.phtml
            $this->render();
        }
    }

## Métodos Utilitarios

Además de los accesadores y de los métodos de integración de vistas, `Zend_Controller_Action` tiene varios métodos utilitarios para realizar tareas comunes dentro de sus métodos de acción (o de pre- y post-dispatch).

- `_forward($action, $controller = null, $module = null, array $params = null)` : realiza otra acción. Si es llamado en `preDispatch()` , la acción actualmente requerida se saltará en favor de la nueva. De lo contrario, después de procesar la acción actual, se ejecutará la acción solicitada en `_forward()`.

- `_redirect($url, array $options = array())` : redireccionar a otro lugar. Este método toma una URL y un conjunto de opciones. Por defecto, realiza una redirección HTTP 302.

  Las opciones pueden incluir uno o más de los siguientes:

  - exit: ya sea para salir inmediatamente o no. Si así lo solicita, limpiamente cerrará cualquier sesión abierta y realizará la redirección.

    Puede configurar esta opción globalmente en el controlador utilizando el accesador `setRedirectExit()` .

  - prependBase: ya sea anteponiendo o no la base URL registrada con el objeto solicitud a la URL provista.

    Puede configurar esta opción globalmente en el controlador utilizando el accesador `setRedirectPrependBase()` .

  - code: qué código HTTP utilizar en la redirección. Por defecto, se utiliza un HTTP 302; se puede utilizar cualquier código entre 301 y 306.

    Puede configurar esta opción globalmente en el controlador utilizando el accesador `setRedirectCode()` .

## Controladores de Acción y haciendo Subclases

Por diseño, `Zend_Controller_Action` debe ser "subclaseada" a fin de crear un controlador de acción. Como mínimo, necesitará definir los métodos de acción que podrá llamar el controlador.

Además de crear una funcionalidad útil para su aplicaciones web, también puede encontrar que está repitiendo demasiado los mismos setups o métodos utilitarios en sus diferentes controladores; si así fuera, creando una clase base común del controlador que extienda `Zend_Controller_Action` puede resolver esta redundacia.

### Manejando Acciones No Existentes

Si hay una solicitud a un controlador que incluye un método de acción no definido, se invocará a `Zend_Controller_Action::__call()` . `__call()` es, por supuesto, el método mágico de PHP para la sobrecarga del método.

Por defecto, este método lanza un `Zend_Controller_Action_Exception` indicando que el método no se encuentró en el controlador. Si el método requerido termina en 'Action', la suposición es que una acción fue solicitada y no existe; tales errores resultan en una excepción con un código 404. Todos los demás métodos resultan en una excepción con un código 500. Esto le permite diferenciar fácilmente entre una página no encontrada y errores de aplicación con su manejador de errores.

Usted debe anular esta funcionalidad si desea realizar otras operaciones. Por ejemplo, si desea mostrar un mensaje de error, usted podría escribir algo como esto:

    class MyController extends Zend_Controller_Action
    {
        public function __call($method, $args)
        {
            if ('Action' == substr($method, -6)) {
                // Si no se encontró el método de la acción, suministrar la
                // plantilla de error
                return $this->render('error');
            }

            // todos los otros métodos lanzan una excepción
            throw new Exception('Se ha llamado al método "'
                                . $method
                                . '" que es inválido',
                                500);
        }
    }

Otra posibilidad es que puede querer avanzar a un controlador de página por defecto:

    class MyController extends Zend_Controller_Action
    {
        public function indexAction()
        {
            $this->render();
        }

        public function __call($method, $args)
        {
            if ('Action' == substr($method, -6)) {
                // Si no se encontró el método de acción, avance a la
                // acción index
                return $this->_forward('index');
            }

            // todos los otros métodos lanzan una excepción
            throw new Exception('Se ha llamado al método "'
                                . $method
                                . '" que es inválido',
                                500);
        }
    }

Además de sobrecargar `__call()` , cada uno de los métodos gancho de inicialización, utilidad, accesador, vista, y despacho mencionados anteriormente en este capítulo pueden ser anulados a fin de personalizar sus controladores. Como ejemplo, si está almacenando su objeto vista en un registro, quizás desee modificar su método `initView()` con código parecido al siguiente:

    abstract class My_Base_Controller extends Zend_Controller_Action
    {
        public function initView()
        {
            if (null === $this->view) {
                if (Zend_Registry::isRegistered('view')) {
                    $this->view = Zend_Registry::get('view');
                } else {
                    $this->view = new Zend_View();
                    $this->view->setBasePath(dirname(__FILE__) . '/../views');
                }
            }

            return $this->view;
        }
    }

Es de esperar, que de la información en este capítulo, usted puede ver la flexibilidad de este componente en particular y cómo puede darle forma a su aplicaciones o a las necesidades de su sitio web.