Zend_Controller_Router_Rewrite es el enrutador
estándar del framework. El enrutamiento es el proceso de tomar un
endpoint de URI
(la parte de la URI que viene después de la
URL base) y descomponerlo en parámetros para determinar
qué módulo, controlador y acción de ese controlador deben recibir la
petición. Los valores del módulo, controlador, acción y otros
parámetros se empaquetan en un objeto
Zend_Controller_Request_Http que luego es
procesado por Zend_Controller_Dispatcher_Standard.
El enrutamiento ocurre solo una vez: cuando la petición es recibida
inicialmente y antes de que se despache el primer controlador.
Zend_Controller_Router_Rewrite está diseñado
para permitir una funcionalidad similar a mod_rewrite usando
estructuras PHP puras. Está muy vagamente basado en el
enrutamiento de Ruby on Rails y no requiere ningún conocimiento previo
de la reescritura de URL del servidor web. Está diseñado para
funcionar con una única regla de Apache mod_rewrite (una de):
RewriteEngine on RewriteRule !\.(js|ico|gif|jpg|png|css|html)$ index.php
o (preferida):
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 enrutador de reescritura también puede usarse con el servidor web IIS (versiones <= 7.0) si se ha instalado Isapi_Rewrite como una extensión Isapi con la siguiente regla de reescritura:
RewriteRule ^[\w/\%]*(?:\.(?!(?:js|ico|gif|jpg|png|css|html)$)[\w\%]*$)? /index.php [I]
![]() |
IIS Isapi_Rewrite |
|---|---|
Al usar IIS, |
IIS 7.0 introduce un módulo nativo de reescritura de URL, y puede configurarse de la siguiente manera:
<?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>
Si se usa Lighttpd, la siguiente regla de reescritura es válida:
url.rewrite-once = (
".*\?(.*)$" => "/index.php?$1",
".*\.(js|ico|gif|jpg|png|css|html)$" => "$0",
"" => "/index.php"
)
Para usar correctamente el enrutador de reescritura hay que instanciarlo, añadir algunas rutas definidas por el usuario e inyectarlo en el controlador. El siguiente código ilustra el procedimiento:
// Create a 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'))
);
El corazón del RewriteRouter es la definición de rutas definidas
por el usuario. Las rutas se añaden llamando al método addRoute de
RewriteRouter y pasando una nueva instancia de una clase que
implementa Zend_Controller_Router_Route_Interface. P. ej.:
$router->addRoute('user',
new Zend_Controller_Router_Route('user/:username'));
Rewrite Router viene con seis tipos básicos de rutas (uno de los cuales es especial):
Las rutas pueden usarse numerosas veces para crear una cadena o un esquema de enrutamiento de aplicación definido por el usuario. Puede usar cualquier número de rutas en cualquier configuración, con la excepción de la ruta Module, que debería usarse una única vez y probablemente como la ruta más genérica (es decir, como predeterminada). Cada ruta se describirá con mayor detalle más adelante.
El primer parámetro de addRoute es el nombre de la ruta. Se usa como identificador para obtener las rutas del enrutador (por ejemplo, con fines de generación de URL). El segundo parámetro es la propia ruta.
![]() |
Nota |
|---|---|
|
El uso más común del nombre de la ruta es a través del helper
url de
<a href=
"<?php echo $this->url(array('username' => 'martel'), 'user') ?>">Martel</a>
Lo que resultaría en el href: |
El enrutamiento es un proceso simple de iterar a través de todas
las rutas proporcionadas y comparar sus definiciones con la
URI de la petición actual. Cuando se encuentra una
coincidencia positiva, se devuelven los valores de las variables
desde la instancia de Route y se inyectan en el objeto
Zend_Controller_Request para su uso posterior en
el dispatcher, así como en los controladores creados por el
usuario. En caso de un resultado de coincidencia negativa, se
comprueba la siguiente ruta en la cadena.
Si necesita determinar qué ruta coincidió, puede usar el método
getCurrentRouteName(), que devolverá el
identificador usado al registrar la ruta con el enrutador. Si
desea el objeto de ruta real, puede usar
getCurrentRoute().
![]() |
Coincidencia inversa |
|---|---|
Las rutas se comparan en orden inverso, así que asegúrese de que sus rutas más genéricas se definan primero. |
![]() |
Valores devueltos |
|---|---|
Los valores devueltos por el enrutamiento provienen de
parámetros de URL o de valores predeterminados de
rutas definidos por el usuario. Estas variables son luego
accesibles a través de los métodos
|
Hay tres variables especiales que pueden usarse en sus rutas:
'module', 'controller' y 'action'. Estas variables especiales son
usadas por Zend_Controller_Dispatcher para
encontrar un controlador y una acción a la que despachar.
![]() |
Variables especiales |
|---|---|
Los nombres de estas variables especiales pueden ser
diferentes si opta por alterar los valores predeterminados en
|
Zend_Controller_Router_Rewrite viene
preconfigurado con una ruta predeterminada, que coincidirá con
URIs con la forma de
controller/action. Además, se puede especificar
un nombre de módulo como el primer elemento de la ruta,
permitiendo URIs de la forma
module/controller/action. Finalmente, también
coincidirá con cualquier parámetro adicional añadido a la
URI por defecto -
controller/action/var1/value1/var2/value2.
Algunos ejemplos de cómo coinciden dichas rutas:
// Assuming the following:
$ctrl->setControllerDirectory(
array(
'default' => '/path/to/default/controllers',
'news' => '/path/to/news/controllers',
'blog' => '/path/to/blog/controllers'
)
);
Module only:
http://example/news
module == news
Invalid module maps to controller name:
http://example/foo
controller == foo
Module + controller:
http://example/blog/archive
module == blog
controller == archive
Module + controller + action:
http://example/blog/archive/list
module == blog
controller == archive
action == list
Module + controller + action + params:
http://example/blog/archive/list/sort/alpha/date/desc
module == blog
controller == archive
action == list
sort == alpha
date == desc
La ruta predeterminada es simplemente un objeto
Zend_Controller_Router_Route_Module almacenado
bajo el nombre (índice) 'default' en RewriteRouter. Se crea
más o menos como sigue:
$compat = new Zend_Controller_Router_Route_Module(array(),
$dispatcher,
$request);
$this->addRoute('default', $compat);
Si no desea esta ruta predeterminada particular en su esquema de
enrutamiento, puede sobrescribirla creando su propia ruta
'default' (es decir, almacenándola bajo el nombre 'default') o
eliminándola por completo usando
removeDefaultRoutes():
// Remove any default routes $router->removeDefaultRoutes();
El enrutador de reescritura puede usarse en subdirectorios (por
ejemplo,
http://domain.com/user/application-root/), en
cuyo caso la URL base de la aplicación
(/user/application-root) debería ser detectada
automáticamente por
Zend_Controller_Request_Http y usada en
consecuencia.
Si la URL base se detecta incorrectamente, puede
sobrescribirla con su propia ruta base usando
Zend_Controller_Request_Http y llamando al
método setBaseUrl() (vea URL base y subdirectorios):
$request->setBaseUrl('/~user/application-root/');
Puede establecer parámetros globales en un enrutador que se
suministran automáticamente a una ruta al ensamblar mediante
setGlobalParam(). Si se establece un
parámetro global pero también se proporciona directamente al
método assemble, el parámetro del usuario sobrescribe al
parámetro global. Puede establecer un parámetro global de esta
manera:
$router->setGlobalParam('lang', 'en');
Zend_Controller_Router_Route es la ruta
estándar del framework. Combina la facilidad de uso con una
definición de ruta flexible. Cada ruta consiste principalmente en un
mapeo de URL (de partes estáticas y dinámicas
(variables)) y puede inicializarse con valores predeterminados así
como con requisitos de variables.
Imaginemos que nuestra aplicación ficticia necesitará una página
informativa sobre los autores del contenido. Queremos poder apuntar
nuestros navegadores web a
http://domain.com/author/martel para ver la
información sobre este tipo llamado "martel". Y la ruta para tal
funcionalidad podría verse así:
$route = new Zend_Controller_Router_Route(
'author/:username',
array(
'controller' => 'profile',
'action' => 'userinfo'
)
);
$router->addRoute('user', $route);
El primer parámetro en el constructor de
Zend_Controller_Router_Route es una definición de
ruta que se comparará con una URL. Las definiciones de
ruta constan de partes estáticas y dinámicas separadas por el
carácter de barra ('/'). Las partes estáticas son solo texto simple:
author. Las partes dinámicas, llamadas
variables, se marcan anteponiendo dos puntos al nombre de la
variable: :username.
![]() |
Uso de caracteres |
|---|---|
La implementación actual permite usar cualquier carácter (excepto una barra) como identificador de variable, pero se recomienda encarecidamente usar solo caracteres que sean válidos para identificadores de variables PHP. Implementaciones futuras pueden alterar este comportamiento, lo que podría resultar en errores ocultos en su código. |
Este ejemplo de ruta debería coincidir cuando apunte su navegador a
http://domain.com/author/martel, en cuyo caso
todas sus variables se inyectarán en el objeto
Zend_Controller_Request y serán accesibles en su
ProfileController. Las variables devueltas por
este ejemplo pueden representarse como un array de los siguientes
pares clave y valor:
$values = array(
'username' => 'martel',
'controller' => 'profile',
'action' => 'userinfo'
);
Más adelante,
Zend_Controller_Dispatcher_Standard debería
invocar el método userinfoAction() de su
clase ProfileController (en el módulo
predeterminado) basándose en estos valores. Allí podrá acceder a
todas las variables mediante los métodos
Zend_Controller_Action::_getParam() o
Zend_Controller_Request::getParam():
public function userinfoAction()
{
$request = $this->getRequest();
$username = $request->getParam('username');
$username = $this->_getParam('username');
}
La definición de ruta puede contener un carácter especial más: un comodín, representado por el símbolo '*'. Se usa para recopilar parámetros de manera similar a la ruta Module predeterminada (pares var => value definidos en la URI). La siguiente ruta imita más o menos el comportamiento de la ruta Module:
$route = new Zend_Controller_Router_Route(
':module/:controller/:action/*',
array('module' => 'default')
);
$router->addRoute('default', $route);
Cada variable en la ruta puede tener un valor predeterminado y
para eso se usa el segundo parámetro del constructor de
Zend_Controller_Router_Route. Este parámetro
es un array con claves que representan nombres de variables y con
valores como los predeterminados deseados:
$route = new Zend_Controller_Router_Route(
'archive/:year',
array('year' => 2006)
);
$router->addRoute('archive', $route);
La ruta anterior coincidirá con URLs como
http://domain.com/archive/2005 y
http://example.com/archive. En el último caso
la variable year tendrá un valor predeterminado inicial de 2006.
Este ejemplo resultará en la inyección de una variable year en el
objeto request. Dado que no hay información de enrutamiento
presente (no se definen parámetros de controller y action), la
aplicación se despachará al controlador y método de acción
predeterminados (ambos definidos en
Zend_Controller_Dispatcher_Abstract). Para
hacerlo más útil, debe proporcionar un controller y una action
válidos como valores predeterminados de la ruta:
$route = new Zend_Controller_Router_Route(
'archive/:year',
array(
'year' => 2006,
'controller' => 'archive',
'action' => 'show'
)
);
$router->addRoute('archive', $route);
Esta ruta resultará entonces en el despacho al método
showAction() de la clase
ArchiveController.
Se puede añadir un tercer parámetro al constructor de
Zend_Controller_Router_Route donde pueden
establecerse los requisitos de las variables. Estos se definen
como partes de una expresión regular:
$route = new Zend_Controller_Router_Route(
'archive/:year',
array(
'year' => 2006,
'controller' => 'archive',
'action' => 'show'
),
array('year' => '\d+')
);
$router->addRoute('archive', $route);
Con una ruta definida como la anterior, el enrutador la hará
coincidir solo cuando la variable year contenga datos numéricos,
por ejemplo http://domain.com/archive/2345.
Una URL como
http://example.com/archive/test no
coincidirá y el control pasará a la siguiente ruta de la cadena
en su lugar.
La ruta estándar admite segmentos traducidos. Para usar esta
característica, debe definir al menos un traductor (una
instancia de Zend_Translate) mediante una de
las siguientes formas:
Colocarlo en el registro con la clave
Zend_Translate.Establecerlo mediante el método estático
Zend_Controller_Router_Route::setDefaultTranslator().Pasarlo como cuarto parámetro al constructor.
Por defecto, se usará el locale especificado en la instancia de
Zend_Translate. Para sobrescribirlo, se
establece (una instancia de Zend_Locale o
una cadena de locale) de una de las siguientes maneras:
Colocarlo en el registro con la clave
Zend_Locale.Establecerlo mediante el método estático
Zend_Controller_Router_Route::setDefaultLocale().Pasarlo como quinto parámetro al constructor.
Pasarlo como parámetro @locale al método assemble.
Los segmentos traducidos se dividen en dos partes. Los segmentos fijos se prefijan con un único signo @, y se traducirán al locale actual al ensamblar y se revertirán al ID del mensaje al volver a coincidir. Los segmentos dinámicos se prefijan con :@. Al ensamblar, el parámetro dado se traducirá y se insertará en la posición del parámetro. Al coincidir, el parámetro traducido de la URL se revertirá al ID del mensaje nuevamente.
![]() |
IDs de mensaje y archivo de idioma separado |
|---|---|
Ocasionalmente un ID de mensaje que desea usar en una de sus rutas ya se usa en un script de vista o en algún otro lugar. Para tener control total sobre URLs seguras, debería usar un archivo de idioma separado para los mensajes usados en la ruta. |
La siguiente es la forma más simple de preparar la ruta estándar para el uso de segmentos traducidos:
// Prepare the translator
$translator = new Zend_Translate(
array(
'adapter' => 'array',
'content' => array(),
'locale' => 'en'
)
);
$translator->addTranslation(
array(
'content' =>
array(
'archive' => 'archiv',
'year' => 'jahr',
'month' => 'monat',
'index' => 'uebersicht'
),
'locale' => 'de'
)
);
// Set the current locale for the translator
$translator->setLocale('en');
// Set it as default translator for routes
Zend_Controller_Router_Route::setDefaultTranslator($translator);
Este ejemplo demuestra el uso de segmentos estáticos:
// Create the route
$route = new Zend_Controller_Router_Route(
'@archive',
array(
'controller' => 'archive',
'action' => 'index'
)
);
$router->addRoute('archive', $route);
// Assemble the URL in default locale: archive
$route->assemble(array());
// Assemble the URL in german: archiv
$route->assemble(array());
Puede usar los segmentos dinámicos para crear una versión traducida similar a una ruta module:
// Create the route
$route = new Zend_Controller_Router_Route(
':@controller/:@action/*',
array(
'controller' => 'index',
'action' => 'index'
)
);
$router->addRoute('archive', $route);
// Assemble the URL in default locale: archive/index/foo/bar
$route->assemble(array('controller' => 'archive', 'foo' => 'bar'));
// Assemble the URL in german: archiv/uebersicht/foo/bar
$route->assemble(array('controller' => 'archive', 'foo' => 'bar'));
También puede mezclar segmentos estáticos y dinámicos:
// Create the route
$route = new Zend_Controller_Router_Route(
'@archive/:@mode/:value',
array(
'mode' => 'year'
'value' => 2005,
'controller' => 'archive',
'action' => 'show'
),
array('mode' => '(month|year)'
'value' => '\d+')
);
$router->addRoute('archive', $route);
// Assemble the URL in default locale: archive/month/5
$route->assemble(array('mode' => 'month', 'value' => '5'));
// Assemble the URL in german: archiv/monat/5
$route->assemble(array('mode' => 'month', 'value' => '5', '@locale' => 'de'));
Los ejemplos anteriores usan todos rutas dinámicas: rutas que contienen patrones con los que comparar. Sin embargo, a veces una ruta en particular está fijada de antemano, y poner en marcha el motor de expresiones regulares sería excesivo. La respuesta a esta situación es usar rutas estáticas:
$route = new Zend_Controller_Router_Route_Static(
'login',
array('controller' => 'auth', 'action' => 'login')
);
$router->addRoute('login', $route);
La ruta anterior coincidirá con una URL de
http://domain.com/login, y despachará a
AuthController::loginAction().
Además de los tipos de ruta default y static, hay disponible un tipo de ruta de Expresión Regular. Esta ruta ofrece más potencia y flexibilidad que las otras, pero a un ligero costo de complejidad. Al mismo tiempo, debería ser más rápida que la ruta Route estándar.
Al igual que la ruta estándar, esta ruta tiene que inicializarse con una definición de ruta y algunos valores predeterminados. Creemos una ruta archive como ejemplo, similar a la definida anteriormente, solo que usando esta vez la ruta Regex:
$route = new Zend_Controller_Router_Route_Regex(
'archive/(\d+)',
array(
'controller' => 'archive',
'action' => 'show'
)
);
$router->addRoute('archive', $route);
Cada subpatrón de regex definido se inyectará en el objeto request.
Con nuestro ejemplo anterior, después de una coincidencia exitosa con
http://domain.com/archive/2006, el array de
valores resultante podría verse así:
$values = array(
1 => '2006',
'controller' => 'archive',
'action' => 'show'
);
![]() |
Nota |
|---|---|
Las barras iniciales y finales se recortan de la URL
en el Router antes de una coincidencia. Como resultado, hacer
coincidir la URL
|
![]() |
Nota |
|---|---|
Los anclajes de inicio y fin de línea ('^' y '$', respectivamente) se anteponen y añaden automáticamente a todas las expresiones. Por lo tanto, no debería usarlos en sus expresiones regulares, y debería hacer coincidir la cadena completa. |
![]() |
Nota |
|---|---|
Esta clase de ruta usa el carácter '#' como delimitador. Esto significa que necesitará escapar los caracteres de almohadilla ('#') pero no las barras diagonales ('/') en sus definiciones de ruta. Dado que el carácter '#' (ancla nombrada) rara vez se pasa al servidor web, rara vez necesitará usar ese carácter en su regex. |
Puede obtener el contenido de los subpatrones definidos de la manera habitual:
public function showAction()
{
$request = $this->getRequest();
$year = $request->getParam(1); // $year = '2006';
}
![]() |
Nota |
|---|---|
Note que la clave es un entero (1) en lugar de una cadena ('1'). |
Esta ruta aún no funcionará exactamente igual que su contraparte de ruta estándar, ya que el valor predeterminado para 'year' aún no está establecido. Y lo que puede no ser aún evidente es que tendremos un problema con una barra final incluso si declaramos un valor predeterminado para el año y hacemos que el subpatrón sea opcional. La solución es hacer que toda la parte del año sea opcional junto con la barra, pero capturar solo la parte numérica:
$route = new Zend_Controller_Router_Route_Regex(
'archive(?:/(\d+))?',
array(
1 => '2006',
'controller' => 'archive',
'action' => 'show'
)
);
$router->addRoute('archive', $route);
Ahora lleguemos al problema que probablemente ya haya notado por su cuenta. Usar claves basadas en enteros para los parámetros no es una solución fácilmente manejable y puede ser potencialmente problemática a largo plazo. Y ahí es donde entra el tercer parámetro. Este parámetro es un array asociativo que representa un mapa de subpatrones de regex a claves de parámetros con nombre. Trabajemos en nuestro ejemplo más fácil:
$route = new Zend_Controller_Router_Route_Regex(
'archive/(\d+)',
array(
'controller' => 'archive',
'action' => 'show'
),
array(
1 => 'year'
)
);
$router->addRoute('archive', $route);
Esto resultará en los siguientes valores inyectados en Request:
$values = array(
'year' => '2006',
'controller' => 'archive',
'action' => 'show'
);
El mapa puede definirse en cualquier dirección para que funcione en cualquier entorno. Las claves pueden contener nombres de variables o índices de subpatrones:
$route = new Zend_Controller_Router_Route_Regex(
'archive/(\d+)',
array( ... ),
array(1 => 'year')
);
// OR
$route = new Zend_Controller_Router_Route_Regex(
'archive/(\d+)',
array( ... ),
array('year' => 1)
);
![]() |
Nota |
|---|---|
Las claves de subpatrón deben representarse mediante enteros. |
Note que el índice numérico en los valores de Request ahora ha desaparecido y una variable con nombre se muestra en su lugar. Por supuesto, puede mezclar variables numéricas y con nombre si lo desea:
$route = new Zend_Controller_Router_Route_Regex(
'archive/(\d+)/page/(\d+)',
array( ... ),
array('year' => 1)
);
Lo que resultará en valores mixtos disponibles en Request. Como
ejemplo, la URL
http://domain.com/archive/2006/page/10
resultará en los siguientes valores:
$values = array(
'year' => '2006',
2 => 10,
'controller' => 'archive',
'action' => 'show'
);
Dado que los patrones regex no son fácilmente reversibles, necesitará
preparar una URL inversa si desea usar un helper de
URL o incluso un método assemble de esta clase. Esta
ruta inversa está representada por una cadena analizable por
sprintf() y se define como un cuarto parámetro
del constructor:
$route = new Zend_Controller_Router_Route_Regex(
'archive/(\d+)',
array( ... ),
array('year' => 1),
'archive/%s'
);
Todo esto era algo ya posible mediante un objeto de ruta estándar,
entonces ¿dónde está el beneficio de usar la ruta Regex, se
preguntará? Principalmente, permite describir cualquier tipo de
URL sin restricciones. Imagine que tiene un blog y
desea crear URLs como:
http://domain.com/blog/archive/01-Using_the_Regex_Router.html,
y que descomponga el último elemento de la ruta,
01-Using_the_Regex_Router.html, en un ID de
artículo y un título o descripción del artículo; esto no es posible
con la ruta estándar. Con la ruta Regex, puede hacer algo como la
siguiente solución:
$route = new Zend_Controller_Router_Route_Regex(
'blog/archive/(\d+)-(.+)\.html',
array(
'controller' => 'blog',
'action' => 'view'
),
array(
1 => 'id',
2 => 'description'
),
'blog/archive/%d-%s.html'
);
$router->addRoute('blogArchive', $route);
Como puede ver, esto añade una tremenda cantidad de flexibilidad sobre la ruta estándar.
Zend_Controller_Router_Route_Hostname es la
ruta de hostname del framework. Funciona de manera similar a la ruta
estándar, pero opera con el hostname de la URL
llamada en lugar de con la ruta.
Usemos el ejemplo de la ruta estándar y veamos cómo se vería de una
manera basada en el hostname. En lugar de llamar al usuario a través
de una ruta, queremos que un usuario pueda llamar a
http://martel.users.example.com para ver la
información sobre el usuario "martel":
$hostnameRoute = new Zend_Controller_Router_Route_Hostname(
':username.users.example.com',
array(
'controller' => 'profile',
'action' => 'userinfo'
)
);
$plainPathRoute = new Zend_Controller_Router_Route_Static('');
$router->addRoute('user', $hostnameRoute->chain($plainPathRoute));
El primer parámetro en el constructor de
Zend_Controller_Router_Route_Hostname es una
definición de ruta que se comparará con un hostname. Las
definiciones de ruta constan de partes estáticas y dinámicas
separadas por el carácter de punto ('.'). Las partes dinámicas,
llamadas variables, se marcan anteponiendo dos puntos al nombre de la
variable: :username. Las partes estáticas
son solo texto simple: user.
Las rutas de hostname pueden, pero nunca deberían, usarse tal cual.
La razón detrás de esto es que una ruta de hostname sola
coincidiría con cualquier ruta. Así que lo que debe hacer es
encadenar una ruta de path a la ruta de hostname. Esto se hace como
en el ejemplo llamando a
$hostnameRoute->chain($pathRoute);. Al hacer
esto, $hostnameRoute no se modifica, sino que se
devuelve una nueva ruta
(Zend_Controller_Router_Route_Chain), que
luego puede darse al enrutador.
Zend_Controller_Router_Route_Chain es una ruta
que permite encadenar múltiples rutas juntas. Esto le permite
encadenar rutas de hostname y rutas de ruta, o múltiples rutas de
ruta, por ejemplo. El encadenamiento puede hacerse ya sea
programáticamente o dentro de un archivo de configuración.
![]() |
Prioridad de parámetros |
|---|---|
Al encadenar rutas juntas, los parámetros de la ruta externa tienen mayor prioridad que los parámetros de la ruta interna. Así, si define un controller en la ruta externa y en la interna, se seleccionará el controller de la ruta externa. |
Al encadenar programáticamente, hay dos formas de lograrlo. La
primera es crear una nueva instancia de
Zend_Controller_Router_Route_Chain y luego
llamar al método chain() varias veces con
todas las rutas que deben encadenarse juntas. La otra forma es tomar
la primera ruta, por ejemplo, una ruta de hostname, y llamar al
método chain() en ella con la ruta que debe
añadirse. Esto no modificará la ruta de hostname, sino que devolverá
una nueva instancia de
Zend_Controller_Router_Route_Chain, que
entonces tiene ambas rutas encadenadas juntas:
// Create two routes
$hostnameRoute = new Zend_Controller_Router_Route_Hostname(...);
$pathRoute = new Zend_Controller_Router_Route(...);
// First way, chain them via the chain route
$chainedRoute = new Zend_Controller_Router_Route_Chain();
$chainedRoute->chain($hostnameRoute)
->chain($pathRoute);
// Second way, chain them directly
$chainedRoute = $hostnameRoute->chain($pathRoute);
Al encadenar rutas juntas, su separador es una barra por defecto. Puede haber casos en los que desee tener un separador diferente:
// Create two routes
$firstRoute = new Zend_Controller_Router_Route('foo');
$secondRoute = new Zend_Controller_Router_Route('bar');
// Chain them together with a different separator
$chainedRoute = $firstRoute->chain($secondRoute, '-');
// Assemble the route: "foo-bar"
echo $chainedRoute->assemble();
Para encadenar rutas juntas en un archivo de configuración, hay parámetros adicionales para la configuración de estas. El enfoque más simple es usar el parámetro chains. Este es simplemente una lista de rutas, que se encadenarán con la ruta padre. Ni la ruta padre ni la ruta hija se añadirán directamente al enrutador, solo la ruta encadenada resultante. El nombre de la ruta encadenada en el enrutador será el nombre de la ruta padre y el nombre de la ruta hija concatenados con un guion (-) por defecto. Una configuración simple en XML se vería así:
<routes>
<www type="Zend_Controller_Router_Route_Hostname">
<route>www.example.com</route>
<chains>
<language type="Zend_Controller_Router_Route">
<route>:language</route>
<reqs language="[a-z]{2}">
<chains>
<index type="Zend_Controller_Router_Route_Static">
<route></route>
<defaults module="default" controller="index"
action="index" />
</index>
<imprint type="Zend_Controller_Router_Route_Static">
<route>imprint</route>
<defaults module="default" controller="index"
action="index" />
</imprint>
</chains>
</language>
</chains>
</www>
<users type="Zend_Controller_Router_Route_Hostname">
<route>users.example.com</route>
<chains>
<profile type="Zend_Controller_Router_Route">
<route>:username</route>
<defaults module="users" controller="profile" action="index" />
</profile>
</chains>
</users>
<misc type="Zend_Controller_Router_Route_Static">
<route>misc</route>
</misc>
</routes>
Esto resultará en las tres rutas www-language-index, www-language-imprint y users-language-profile que solo coincidirán en función del hostname y la ruta misc, que coincidirá con cualquier hostname.
La forma alternativa de crear una ruta encadenada es mediante el parámetro chain, que solo puede usarse directamente con el tipo de ruta chain, y que también solo funciona en el nivel raíz:
<routes>
<www type="Zend_Controller_Router_Route_Chain">
<route>www.example.com</route>
</www>
<language type="Zend_Controller_Router_Route">
<route>:language</route>
<reqs language="[a-z]{2}">
</language>
<index type="Zend_Controller_Router_Route_Static">
<route></route>
<defaults module="default" controller="index" action="index" />
</index>
<imprint type="Zend_Controller_Router_Route_Static">
<route>imprint</route>
<defaults module="default" controller="index" action="index" />
</imprint>
<www-index type="Zend_Controller_Router_Route_Chain">
<chain>www, language, index</chain>
</www-index>
<www-imprint type="Zend_Controller_Router_Route_Chain">
<chain>www, language, imprint</chain>
</www-imprint>
</routes>
También puede dar el parámetro chain como array en lugar de separar las rutas con una coma:
<routes>
<www-index type="Zend_Controller_Router_Route_Chain">
<chain>www</chain>
<chain>language</chain>
<chain>index</chain>
</www-index>
<www-imprint type="Zend_Controller_Router_Route_Chain">
<chain>www</chain>
<chain>language</chain>
<chain>imprint</chain>
</www-imprint>
</routes>
Cuando configure rutas chain con Zend_Config
y desee que el separador de nombre de la cadena sea diferente de
un guion, necesita especificar este separador por separado:
$config = new Zend_Config(array(
'chainName' => array(
'type' => 'Zend_Controller_Router_Route_Static',
'route' => 'foo',
'chains' => array(
'subRouteName' => array(
'type' => 'Zend_Controller_Router_Route_Static',
'route' => 'bar',
'defaults' => array(
'module' => 'module',
'controller' => 'controller',
'action' => 'action'
)
)
)
)
));
// Set separator before adding config
$router->setChainNameSeparator('_separator_')
// Add config
$router->addConfig($config);
// The name of our route now is: chainName_separator_subRouteName
echo $this->_router->assemble(array(), 'chainName_separator_subRouteName');
// The proof: it echoes /foo/bar
El componente Zend_Rest contiene una ruta
RESTful para Zend_Controller_Router_Rewrite.
Esta ruta ofrece un esquema de enrutamiento estandarizado que
enruta las peticiones traduciendo el método HTTP y la
URI a un módulo, controlador y acción. La tabla a
continuación ofrece una visión general de cómo se enrutan los
métodos de petición y las URIs.
Tabla 24.1. Comportamiento de Zend_Rest_Route
| Método | URI | Module_Controller::action |
|---|---|---|
GET |
/product/ratings/ |
Product_RatingsController::indexAction() |
GET |
/product/ratings/:id |
Product_RatingsController::getAction() |
POST |
/product/ratings |
Product_RatingsController::postAction() |
PUT |
/product/ratings/:id |
Product_RatingsController::putAction() |
DELETE |
/product/ratings/:id |
Product_RatingsController::deleteAction()
|
POST |
/product/ratings/:id?_method=PUT |
Product_RatingsController::putAction() |
POST |
/product/ratings/:id?_method=DELETE |
Product_RatingsController::deleteAction()
|
Para habilitar Zend_Rest_Route para toda
una aplicación, constrúyala sin parámetros de configuración y
añádala como la ruta predeterminada en el controlador frontal:
$front = Zend_Controller_Front::getInstance();
$restRoute = new Zend_Rest_Route($front);
$front->getRouter()->addRoute('default', $restRoute);
![]() |
Nota |
|---|---|
Si |
Para habilitar Zend_Rest_Route para módulos
específicos, constrúyala con un array de nombres de módulos como
el tercer argumento del constructor:
$front = Zend_Controller_Front::getInstance();
$restRoute = new Zend_Rest_Route($front, array(), array('product'));
$front->getRouter()->addRoute('rest', $restRoute);
Para habilitar Zend_Rest_Route para
controladores específicos, añada un array de nombres de
controladores como el valor de cada elemento del array de
módulo.
$front = Zend_Controller_Front::getInstance();
$restRoute = new Zend_Rest_Route($front, array(), array(
'product' => array('ratings')
));
$front->getRouter()->addRoute('rest', $restRoute);
Para usar Zend_Rest_Route desde un archivo
de configuración INI, use un parámetro de tipo de
ruta y establezca las opciones de configuración:
routes.rest.type = Zend_Rest_Route routes.rest.defaults.controller = object routes.rest.mod = project,user
La opción 'type' designa el tipo de configuración de enrutamiento
RESTful. La opción 'defaults' se usa para especificar módulo,
controlador y/o acciones predeterminados personalizados para la
ruta. Todas las demás opciones en el grupo de configuración se
tratan como nombres de módulos RESTful, y sus valores son
nombres de controladores RESTful. El ejemplo de configuración
define Mod_ProjectController y
Mod_UserController como controladores
RESTful.
Luego use el método addConfig() del
objeto router Rewrite:
$config = new Zend_Config_Ini('path/to/routes.ini');
$router = new Zend_Controller_Router_Rewrite();
$router->addConfig($config, 'routes');
Para ayudar o guiar el desarrollo de controladores para su uso
con Zend_Rest_Route, haga que sus
controladores extiendan de
Zend_Rest_Controller.
Zend_Rest_Controller define las 5
operaciones más comúnmente necesarias para recursos RESTful en
forma de métodos de acción abstractos.
indexAction()- Debería recuperar un índice de recursos y asignarlo a la vista.getAction()- Debería recuperar un único recurso identificado por URI y asignarlo a la vista.postAction()- Debería aceptar un nuevo recurso único y persistir su estado.putAction()- Debería aceptar un único recurso identificado por URI y persistir su estado.deleteAction()- Debería eliminar un único recurso identificado por URI.
A veces es más conveniente actualizar un archivo de configuración
con nuevas rutas que cambiar el código. Esto es posible mediante
el método addConfig(). Básicamente, se
crea una configuración compatible con
Zend_Config, y en su código se lee y se
pasa al 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" ; OR: routes.archive.map.year = 1
El archivo INI anterior puede leerse entonces
en un objeto Zend_Config de la siguiente
manera:
$config = new Zend_Config_Ini('/path/to/config.ini', 'production');
$router = new Zend_Controller_Router_Rewrite();
$router->addConfig($config, 'routes');
En el ejemplo anterior, le decimos al enrutador que use la
sección 'routes' del archivo INI para sus rutas.
Cada clave de primer nivel bajo esa sección se usará para
definir un nombre de ruta; el ejemplo anterior define las rutas
'archive' y 'news'. Cada ruta requiere entonces, como mínimo, una
entrada 'route' y una o más entradas 'defaults'; opcionalmente se
pueden proporcionar una o más 'reqs' (abreviatura de
'required'). En conjunto, estas corresponden a los tres
argumentos proporcionados a un objeto
Zend_Controller_Router_Route_Interface. Una
clave de opción, 'type', puede usarse para especificar el tipo de
clase de ruta a usar para esa ruta en particular; por defecto,
usa Zend_Controller_Router_Route. En el
ejemplo anterior, la ruta 'news' se define para usar
Zend_Controller_Router_Route_Static.
El enrutador de reescritura estándar debería proporcionar la mayor parte de la funcionalidad que pueda necesitar; la mayoría de las veces, solo necesitará crear un nuevo tipo de ruta para proporcionar una funcionalidad nueva o modificada sobre las rutas provistas.
Dicho esto, puede que en algún momento se encuentre queriendo
usar un paradigma de enrutamiento diferente. La interfaz
Zend_Controller_Router_Interface proporciona
la información mínima requerida para crear un enrutador, 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 enrutamiento ocurre solo una vez: cuando la petición se recibe por primera vez en el sistema. El propósito del enrutador es determinar el controlador, la acción y los parámetros opcionales en función del entorno de la petición, y luego establecerlos en la petición. El objeto de petición se pasa entonces al dispatcher. Si no es posible mapear una ruta a un token de despacho, el enrutador no debería hacer nada con el objeto de petición.
![[Note]](images/note.png)