# El Router Standard

## Introducción

Zend_Controller_Router_Rewrite Es el router standard del Framework. Routing es el proceso de tomar la parte final de una URI (la parte de la URI que viene después de la URL base) y la descomposición en parámetros para determinar qué módulo, qué controlador y acción de ese controlador debe recibir la solicitud. Estos valores del módulo, controlador, acción y otros parámetros están enpaquetados en un objeto Zend_Controller_Request_Http el cual es procesado luego por Zend_Controller_Dispatcher_Standard . El routing ocurre sólo una vez: cuando se recibió inicialmente la solicitud y antes del dispatch del primer controlador.

Zend_Controller_Router_Rewrite está diseñado para permitir que una funcionalidad tipo mod_rewrite se pueda usar en estructuras PHP puras. Se basa muy vagamente en el routing de Ruby on Rails (RoR) y no requiere ningún conocimiento previo de reescritura de la URL del webserver. Está diseñado para trabajar con solo una regla mod_rewrite de Apache (one of):

    RewriteEngine on
    RewriteRule !\.(js|ico|gif|jpg|png|css|html)$ index.php

o (preferido):

    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} -s [OR]
    RewriteCond %{REQUEST_FILENAME} -l [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^.*$ - [NC,L]
    RewriteRule ^.*$ index.php [NC,L]

El router rewrite también puede utilizarse con el IIS webserver (versions <= 7.0) si Isapi_Rewrite se ha instalado como una extensión Isapi con la siguiente regla de reescribir:

    RewriteRule ^[\w/\%]*(?:\.(?!(?:js|ico|gif|jpg|png|css|html)$)[\w\%]*$)? /index.php [I]

IIS Isapi_Rewrite

Cuando se usa IIS , `$_SERVER['REQUEST_URI']` puede no existir, o establecerlo como un string vacío. En este caso, Zend_Controller_Request_Http intentará usar el valor de `$_SERVER['HTTP_X_REWRITE_URL']` establecido por la extensión Isapi_Rewrite.

IIS 7.0 introduce un módulo nativo de reescribir la URL, y puede ser configurado como sigue:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
         <system.webServer>
             <rewrite>
                 <rules>
                     <rule name="Imported Rule 1" stopProcessing="true">
                         <match url="^.*$" />
                         <conditions logicalGrouping="MatchAny">
                             <add input="{REQUEST_FILENAME}"
                                 matchType="IsFile" pattern=""
                                 ignoreCase="false" />
                             <add input="{REQUEST_FILENAME}"
                                 matchType="IsDirectory"
                                 pattern="" ignoreCase="false" />
                         </conditions>
                         <action type="None" />
                     </rule>
                     <rule name="Imported Rule 2" stopProcessing="true">
                         <match url="^.*$" />
                         <action type="Rewrite" url="index.php" />
                     </rule>
                 </rules>
             </rewrite>
         </system.webServer>
    </configuration>]></programlisting>

           <para>
                Si está usando Lighttpd, la siguiente regla de reescritura es válida:
           </para>

            <programlisting language="lighttpd"><![CDATA[
    url.rewrite-once = (
        ".*\?(.*)$" => "/index.php?$1",
        ".*\.(js|ico|gif|jpg|png|css|html)$" => "$0",
        "" => "/index.php"
    )

## Usando un Router

Para utilizar adecuadamente el router de reescritura debe instanciarlo, agregar algunas rutas definidas por el usuario y luego inyectarlo en el controlador. El siguiente código ilustra el procedimiento:

    // Crear un router

    $router = $ctrl->getRouter(); // returns a rewrite router by default
    $router->addRoute(
        'user',
        new Zend_Controller_Router_Route('user/:username',
                                         array('controller' => 'user',
                                               'action' => 'info'))
    );

## Operación Básica del Rewrite Router

El corazón del RewriteRouter es la definición de la rutas definidas por el usuario. Las rutas se agregan llamando al método addRoute de RewriteRouter y pasándole una nueva instancia de una clase que implementó a Zend_Controller_Router_Route_Interface . Eg.:

    $router->addRoute('user',
                      new Zend_Controller_Router_Route('user/:username'));

El Rewrite Router viene con seis tipos básicos de rutas (uno de los cuales es especial):

- Zend_Controller_Router_Route

- Zend_Controller_Router_Route_Static

- Zend_Controller_Router_Route_Regex

- Zend_Controller_Router_Route_Hostname

- Zend_Controller_Router_Route_Chain

- Zend_Controller_Router_Rewrite *

Las rutas pueden ser utilizadas numerosas veces para crear una cadena o un esquema de aplicación de ruteo definido por el usuario. Puede usar cualquier número de rutas en cualquier configuración, con la excepción de la ruta del Módulo, la cual debe ser utilizada una vez y probablemente como la ruta más genérica (es decir, por defecto). Cada ruta se describe en mayor detalle más adelante.

El primer parámetro a addRoute es el nombre de la ruta. Se utiliza como un manejador para sacar las rutas del router (por ejemplo, con fines de generación de URL ). El segundo parámetro es la ruta misma.

El uso más común del nombre de ruta es por medio del ayudante de URL Zend_View :

    <a href=
    "<?php echo $this->url(array('username' => 'martel'), 'user') ?>">Martel</a>

Que resultaría en la href: `user/martel` .

El routing es un simple proceso de iteración a través de todas las rutas provistas y la equiparación de sus definiciones con la petición actual de URI . Cuando se encuentra una concordancia, se devuelven valores de variables desde la instancia Route y se inyecta en el objeto Zend_Controller_Request para su posterior utilización en el dispatcher así también como en los controladores creados por el usuario. En caso de no encontrar ninguna concordancia, se comprobará la siguiente ruta en la cadena.

Si necesita determinar en qué ruta se encontró una concordancia, puede usar el método getCurrentRouteName() , que devolverá el identificador usado cuando registró la ruta con el router. Si quiere el objeto de la ruta actual, puede usar getCurrentRoute() .

Matching Inverso

Las rutas están equiparadas en orden inverso para asegurarse que las rutas más genéricas se definan primero.

Valores Retornados

Los valores retornados del routing provienen de parámetros URL o de rutas definidas por defecto por el usuario. Estas variables son accesibles posteriormente a través de los métodos Zend_Controller_Request::getParam() o Zend_Controller_Action::_getParam() .

Hay tres variables que pueden utilizarse en las rutas - 'module', 'controller' y 'action'. Estas variables especiales son utilizados por Zend_Controller_Dispatcher para encontrar un controlador y una acción para hacer el dispatch.

Variables Especiales

Los nombres de estas variables especiales pueden ser diferentes si elige alterar los valores por defecto en Zend_Controller_Request_Http mediante los métodos setControllerKey() y setActionKey() .

## Routes por Defecto

Zend_Controller_Router_Rewrite viene preconfigurado con una ruta por defecto, que se comparará con URI s en la forma de `controller/action` . Además, se puede especificar un nombre de módulo como primer elemento del path, permitiendo URI s de la forma `module/controller/action` . Por último, también coincidrá con cualquier parámetro adicional agregado a la URI por defecto - `controller/action/var1/value1/var2/value2` .

Algunos ejemplos de cómo están equiparadas las rutas:

    // Asumiendo lo siguiente:
    $ctrl->setControllerDirectory(
        array(
            'default' => '/path/to/default/controllers',
            'news'    => '/path/to/news/controllers',
            'blog'    => '/path/to/blog/controllers'
        )
    );

    Módulo únicamente:
    http://example/news
        module == news

    Modulo inválido mapea al nombre del controlador:
    http://example/foo
        controller == foo

    Módulo + controlador:
    http://example/blog/archive
        module     == blog
        controller == archive

    Módulo + controlador + accción:
    http://example/blog/archive/list
        module     == blog
        controller == archive
        action     == list

    Módulo + controlador + accción + parámetros:
    http://example/blog/archive/list/sort/alpha/date/desc
        module     == blog
        controller == archive
        action     == list
        sort       == alpha
        date       == desc

La ruta por defecto es simplemente un objeto Zend_Controller_Router_Route_Module almacenado bajo el nombre de (index) por 'default' en RewriteRouter. Está generado más o menos así:

    $compat = new Zend_Controller_Router_Route_Module(array(),
                                                      $dispatcher,
                                                      $request);
    $this->addRoute('default', $compat);

Si no quiere esta ruta en particular en su esquema por defecto de routing, podrá anularla creando su propia ruta por 'defecto' (es decir, almacenar bajo el nombre de 'default') o eliminarla por completo usando removeDefaultRoutes() :

    // Eliminar cualquier ruta por defecto
    $router->removeDefaultRoutes();

## URL Base y Subdirectorios

El router rewrite puede ser utilizado en subdirectorios (por ejemplo `http://domain.com/~user/application-root/` ) en cuyo caso la URL base de la aplicación ( `/user/application-root` ) debe ser detectada automáticamente por Zend_Controller_Request_Http y usada en consecuencia.

Si la URL base se detecta incorrectamente se la puede anular con su propio path de base usando Zend_Controller_Request_Http y llamando al método setBaseUrl() (ver ):

    $request->setBaseUrl('/~user/application-root/');

## Parámetros Globales

Puede establecer los parámetros globales en un router que se proporcionan automáticamente a una ruta cuando se ensamblasn mediante setGlobalParam() . Si se establece un parámetro global pero también se lo entrega directamente al método de ensamblaje, el parámetro del usuario sobreescribe al parámetro global. Puede establecer un parámetro global esta forma:

    $router->setGlobalParam('lang', 'en');

## Tipos de Route

## Usando Zend_Config con RewriteRouter

A veces es más conveniente para actualizar un archivo de configuración con nuevas rutas que modificar el código. Esto es posible a través del método addConfig() . Básicamente, se crea una configuración compatible con Zend_Config . Y en su código lo lee y lo pasa a RewriteRouter.

Como ejemplo, considere el siguiente archivo INI :

    [production]
    routes.archive.route = "archive/:year/*"
    routes.archive.defaults.controller = archive
    routes.archive.defaults.action = show
    routes.archive.defaults.year = 2000
    routes.archive.reqs.year = "\d+"

    routes.news.type = "Zend_Controller_Router_Route_Static"
    routes.news.route = "news"
    routes.news.defaults.controller = "news"
    routes.news.defaults.action = "list"

    routes.archive.type = "Zend_Controller_Router_Route_Regex"
    routes.archive.route = "archive/(\d+)"
    routes.archive.defaults.controller = "archive"
    routes.archive.defaults.action = "show"
    routes.archive.map.1 = "year"
    ; O: routes.archive.map.year = 1

Entonces el archivo INI puede ser leído por un objeto Zend_Config como sigue:

    $config = new Zend_Config_Ini('/path/to/config.ini', 'production');
    $router = new Zend_Controller_Router_Rewrite();
    $router->addConfig($config, 'routes');

En el ejemplo de arriba, le decimos el router que utilice la sección 'routes' del archivo INI para utilizarlo en sus rutas. Cada clave de primer nivel en esa sección será utilizada para definir un nombre de ruta; el ejemplo anterior define las rutas 'archive' y 'news'. Entonces cada ruta requiere, como mínimo, una entrada a la 'ruta' y una o más entradas por 'default'; opcionalmente puede proporcionarse una o más 'reqs' (abreviación de 'required'). Dicho todo esto, estos corresponden a los tres argumentos que se le suministran al objeto Zend_Controller_Router_Route_Interface . Puede utilizarse una clave opcional 'type' para especificar el tipo de clase de ruta a utilizar en esa ruta en particular; por defecto, usa Zend_Controller_Router_Route . En el ejemplo de arriba, la ruta 'news' está definida para usar Zend_Controller_Router_Route_Static .

## Subclassing del Router

El standard rewrite router debería proporcionarle más funcionalidad si la necesita; más a menudo, sólo necesitará crear un nuevo tipo de ruta a fin de ofrecer funcionalidades nuevas o modificadas sobre las tutas provistas.

Dicho esto, en algún momento puede encontrarse a si mismo deseando usar un paradigma diferente de routing. La intefaz Zend_Controller_Router_Interface proporciona la información mínima necesaria para crear un router, y consiste en un único método.

    interface Zend_Controller_Router_Interface
    {
      /**
       * @param  Zend_Controller_Request_Abstract $request
       * @throws Zend_Controller_Router_Exception
       * @return Zend_Controller_Request_Abstract
       */
      public function route(Zend_Controller_Request_Abstract $request);
    }

El routing sólo ocurre una vez: cuando la petición es recibida por primera vez en el sistema. El propósito del router es determinar el controlador, la acción, y los parámetros opcionales sobre la base del medio ambiente de la solicitud, y luego ajustarlos en la solicitud. El objeto solicitud se pasa entonces al dispatcher. Si no es posible trazar una ruta hacia un dispatch token, el router no debe hacer nada con el objeto solicitud.