<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 15103 -->
<!-- Reviewed: no -->
<sect1 id="zend.controller.exceptions">
    <title>Excepciones MVC</title>

    <sect2 id="zend.controller.exceptions.introduction">
        <title>Introducción</title>

        <para>
            Los componentes MVC en Zend Framework utilizan un Front Controller,
            lo que significa que todas las solicitudes de un determinado
            sitio pasarán por un solo punto de entrada. Como resultado, todas
            las excepciones burbujearán eventualmente hacia arriba hasta el
            Front Controller, permitiendo al desarrollador manejarlos en un
            solo lugar.
        </para>

        <para>
            Sin embargo, los mensajes de excepción y la información de backtrace
            contienen a menudo información sensible del sistema, como
            declaraciones SQL, ubicaciones de archivos y otras cosas más.
            Para ayudar a proteger su sitio, por defecto
            <classname>Zend_Controller_Front</classname> captura todas las
            excepciones y las registra con el objeto respuesta; a su vez, y
            por defecto, el objeto respuesta no muestra mensajes de excepción.
        </para>
    </sect2>

    <sect2 id="zend.controller.exceptions.handling">
        <title>Manejando las Excepciones</title>

        <para>
            Ya hay varios mecanismos construidos en los componentes de MVC,
            que le permiten manejar excepciones.
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    Por defecto, el <link
                        linkend="zend.controller.plugins.standard.errorhandler">error
                    handler plugin</link> está registrado y activo.
                    Este plugin fue diseñado para manejar:
                </para>

                <itemizedlist>
                    <listitem><para>Errores debido a controladores o acciones perdidas</para></listitem>

                    <listitem><para>Errores ocurriendo dentro de controladores de acción</para></listitem>
                </itemizedlist>

                <para>
                    Operan como un plugin de <methodname>postDispatch()</methodname>,
                    y comprueban para ver si un despachador, controlador de
                    acción, o de otra excepción ha ocurrido.
                    Si así fuera, lo remite a un controlador de manejo de
                    errores.
                </para>

                <para>
                    Este manejador abarcará la mayoría de las situaciones
                    excepcionales, y maneja airosamente controladores y acciones
                    perdidos.
                </para>
            </listitem>

            <listitem>
                <para><classname>Zend_Controller_Front::throwExceptions()</classname></para>

                <para>
                    Pasando a este método un valor booleano verdadero,
                    puede decirle al front controller que, en lugar
                    de sumar excepciones en el objeto respuesta o utilizando
                    el plugin de manejo de errores, prefiere manejarlos usted mismo.
                    Como ejemplo:
                </para>

                 <programlisting language="php"><![CDATA[
$front->throwExceptions(true);
try {
    $front->dispatch();
} catch (Exception $e) {
    // usted mismo maneja las excepciones
}
]]></programlisting>

                <para>
                    Este método es probablemente la forma más fácil de añadir
                    un manejo de excepciones personalizado que abarque toda la
                    gama de posibles excepciones a su aplicación de
                    front controller.
                </para>
            </listitem>

            <listitem>
                <para><classname>Zend_Controller_Response_Abstract::renderExceptions()</classname></para>

                <para>
                    Al pasar a este método un valor booleano verdadero,
                    le esta diciendo al objeto respuesta que debe emitir un
                    mensaje de excepción y backtrace cuando se renderiza a sí
                    mismo.
                    En este escenario, se mostrará cualquier excepción planteada
                    por su aplicación. Esto no es recomendable para entornos de
                    producción, pero sí en desarrollo.
                </para>
            </listitem>

            <listitem>
                <para>
                    <classname>Zend_Controller_Front::returnResponse()</classname> y
                    <classname>Zend_Controller_Response_Abstract::isException()</classname>.
                </para>

                <para>
                    Pasando un valor booleano verdadero a
                    <classname>Zend_Controller_Front::returnResponse()</classname>,
                    <classname>Zend_Controller_Front::dispatch()</classname>
                    no renderizará la respuesta, sino que la devolverá.
                    Una vez que tiene la respuesta, entonces puede probar ver
                    si todas las excepciones fueron atrapadas usando su método
                    <methodname>isException()</methodname>, y recuperando las excepciones
                    a través del método <methodname>getException()</methodname>.
                    Como ejemplo:
                </para>

                 <programlisting language="php"><![CDATA[
$front->returnResponse(true);
$response = $front->dispatch();
if ($response->isException()) {
    $exceptions = $response->getException();
    // maneje las excepciones ...
} else {
    $response->sendHeaders();
    $response->outputBody();
}
]]></programlisting>

                <para>
                    La principal ventaja que este método ofrece por sobre
                    <classname>Zend_Controller_Front::throwExceptions()</classname>
                    es que le permite renderizar condicionalmente la respuesta
                    después de manejar la excepción. Esta capturará cualquier
                    excepción en la cadena de controladores, a diferencia del
                    plugin de manejo de errores.
                </para>
            </listitem>
        </itemizedlist>
    </sect2>

    <sect2 id="zend.controller.exceptions.internal">
        <title>Excepciones MVC que Usted Pueda Encontrar</title>

        <para>
            Los diversos componentes de MVC -- solicitud, router, despachador,
            controlador de acción, y los objetos respuesta -- pueden arrojar
            excepciones en ocasiones.
            Algunas excepciones puede ser condicionalmente anuladas,
            y otras se usan para indicar al desarrollador que puede necesitar
            re-considerar la estructura de su aplicación.
        </para>

        <para>Como algunos ejemplos:</para>

        <itemizedlist>
            <listitem>
                <para>
                    <classname>Zend_Controller_Dispatcher::dispatch()</classname>
                    hará, por defecto, arrojar una excepción si se hace un
                    requerimiento a un controlador no válido.
                    Hay dos maneras recomendadas para lidiar con esto.
                </para>

                <itemizedlist>
                    <listitem>
                        <para>Establecer el parámetro
                             <methodname>useDefaultControllerAlways</methodname>.
                        </para>

                        <para>
                            En su front controller, o en su despachador,
                            añada la siguiente directiva:
                        </para>

                         <programlisting language="php"><![CDATA[
$front->setParam('useDefaultControllerAlways', true);

// o

$dispatcher->setParam('useDefaultControllerAlways', true);
]]></programlisting>

                        <para>
                            Cuando este flag está establecido, el despachador
                            utilizará el controlador y la acción por defecto
                            en lugar de lanzar una excepción.
                            La desventaja de este método es que cualquier
                            error ortográfico que un usuario haga cuando
                            acceda a su sitio lo resolverá y mostrará su
                            página de inicio, y que puede causar estragos
                            con la optimización para los motores de búsqueda.
                        </para>
                    </listitem>

                    <listitem>
                        <para>
                            La excepción arrojada por <methodname>dispatch()</methodname> es
                            una <classname>Zend_Controller_Dispatcher_Exception</classname>
                            conteniendo el texto 'Invalid controller specified'.
                            Use uno de los métodos descriptos de <link
                                linkend="zend.controller.exceptions.handling">la
                                sección anterior</link> para atrapar la
                                excepción, y luego redireccionar a una página
                                genérica de error o a la página de inicio.
                        </para>
                    </listitem>
                </itemizedlist>
            </listitem>

            <listitem>
                <para>
                    <classname>Zend_Controller_Action::__call()</classname>
                    arrojará una
                    <classname>Zend_Controller_Action_Exception</classname>
                    si no puede despachar una acción inexistente a un método.
                    Es probable que desee utilizar alguna acción por defecto
                    en el controlador en casos como este.
                    Formas de lograr esto incluyen:
                </para>

                <itemizedlist>
                    <listitem>
                        <para>
                            Subclasear <classname>Zend_Controller_Action</classname> y
                            anular el método <methodname>__call()</methodname>.
                            Como ejemplo:
                        </para>

                         <programlisting language="php"><![CDATA[
class My_Controller_Action extends Zend_Controller_Action
{
    public function __call($method, $args)
    {
        if ('Action' == substr($method, -6)) {
            $controller = $this->getRequest()->getControllerName();
            $url = '/' . $controller . '/index';
            return $this->_redirect($url);
        }

        throw new Exception('Invalid method');
    }
}
]]></programlisting>
                        <para>
                            El ejemplo anterior intercepta cualquier llamada a
                            un método de acción indefinido y redirecciona a la
                            acción predeterminada en el controlador.
                        </para>
                    </listitem>

                    <listitem>
                        <para>
                            Subclasea a <classname>Zend_Controller_Dispatcher</classname>
                            y anula el método <methodname>getAction()</methodname>
                            para verificar si la acción existe.
                            Como ejemplo:
                        </para>

                         <programlisting language="php"><![CDATA[
class My_Controller_Dispatcher extends Zend_Controller_Dispatcher
{
    public function getAction($request)
    {
        $action = $request->getActionName();
        if (empty($action)) {
            $action = $this->getDefaultAction();
            $request->setActionName($action);
            $action = $this->formatActionName($action);
        } else {
            $controller = $this->getController();
            $action     = $this->formatActionName($action);
            if (!method_exists($controller, $action)) {
                $action = $this->getDefaultAction();
                $request->setActionName($action);
                $action = $this->formatActionName($action);
            }
        }

        return $action;
    }
}
]]></programlisting>

                        <para>
                            El código anterior comprueba para ver que las
                            acciones solicitadas existan en la clase del
                            controlador; si no, se restablece la acción a la
                            acción por defecto.
                        </para>

                        <para>
                            Este método es agradable porque puede alterar
                            transparentemente la acción antes del último
                            despacho. Sin embargo, también significa que errores
                            ortográficos en la URL todavía pueden despacharse
                            correctamente, lo que no es muy bueno para la
                            optimización en un motor de búsqueda.
                        </para>
                    </listitem>

                    <listitem>
                        <para>
                            Use
                            <classname>Zend_Controller_Action::preDispatch()</classname>
                            o
                            <classname>Zend_Controller_Plugin_Abstract::preDispatch()</classname>
                            para identificar acciones inválidas.
                        </para>

                        <para>
                            Subclaseando <classname>Zend_Controller_Action</classname>
                            y modificando <methodname>preDispatch()</methodname>,
                            puede modificar todos sus controladores que
                            transmitan a otra acción o redireccionar antes de
                            despachar la acción. El código para hacer esto se
                            verá parecido al código de sustitución de arriba
                            <methodname>__call()</methodname>.
                        </para>

                        <para>
                            Alternativamente, puede verificar esta información
                            en un plugin global. Esto tiene la ventaja de ser
                            independiente del controlador de acción; si su
                            aplicación consiste en una variedad de controladores
                            de acción, y no todos ellos heredan de la misma clase,
                            este método puede añadir coherencia a su manejo de
                            clases diferentes.
                        </para>

                        <para>
                            Como ejemplo:
                        </para>

                         <programlisting language="php"><![CDATA[
class My_Controller_PreDispatchPlugin extends Zend_Controller_Plugin_Abstract
{
    public function preDispatch(Zend_Controller_Request_Abstract $request)
    {
        $front      = Zend_Controller_Front::getInstance();
        $dispatcher = $front->getDispatcher();
        $class      = $dispatcher->getControllerClass($request);
        if (!$controller) {
            $class = $dispatcher->getDefaultControllerClass($request);
        }

        $r      = new ReflectionClass($class);
        $action = $dispatcher->getActionMethod($request);

        if (!$r->hasMethod($action)) {
            $defaultAction  = $dispatcher->getDefaultAction();
            $controllerName = $request->getControllerName();
            $response       = $front->getResponse();
            $response->setRedirect('/' . $controllerName
                                  . '/' . $defaultAction);
            $response->sendHeaders();
            exit;
        }
    }
}
]]></programlisting>

                        <para>
                            En este ejemplo, vamos a consultar para ver si la
                            acción solicitada está disponible en el controlador.
                            Si no, redireccionamos a la acción predeterminada
                            en el controlador, y salimos inmediatamente de la
                            ejecución del script.
                        </para>
                    </listitem>
                </itemizedlist>
            </listitem>
        </itemizedlist>
    </sect2>
</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
