El sistema de enrutamiento se encarga de 2 tareas:
La transformación se realiza en base a una serie de reglas de enrutamiento. Todas estas reglas se almacenan en un archivo de configuración llamado routing.yml y que se encuentra en el directorio config/. El listado 9-15 muestra las reglas que incluyen por defecto todos los proyectos de Symfony.
Listado 9-15 - Las reglas de enrutamiento por defecto, en frontend/config/routing.yml
# default rules homepage: url: / param: { module: default, action: index } default_symfony: url: /symfony/:action/* param: { module: default } default_index: url: /:module param: { action: index } default: url: /:module/:action/*
Las reglas de enrutamiento son asociaciones biyectivas entre las URL externas y las URI internas. Una regla típica está formada por:
url)param) Los patrones pueden contener comodines (que se representan con un asterisco, *) y comodines con nombre (que empiezan por 2 puntos, :). Si se produce una coincidencia con un comodín con nombre, ese valor que coincide se transforma en un parámetro de la petición. Por ejemplo, la regla anterior llamada default produce coincidencias con cualquier URL del tipo /valor1/valor2, en cuyo caso se ejecutará el módulo llamado valor1 y la acción llamada valor2. Y en la regla llamada default_symfony, el valor symfony es una palabra clave y action es un comodín con nombre que se transforma en parámetro de la petición.
A partir de la versión 1.1 de Symfony, los comodines con nombre pueden separarse con un guión medio o con un punto, por lo que es posible crear patrones avanzados como el siguiente:
mi_regla: url: /ruta/:parametro1.:formato param: { module: mimodulo, action: miaccion }
Si se define la regla anterior, una URL como /ruta/12.xml produce una coincidencia con esa regla y provoca que se ejecute la acción miaccion dentro del módulo mimodulo. Además, a la acción se le pasa un parámetro llamado parametro1 con valor igual a 12 y otro parámetro llamado formato con valor xml.
Si quieres utilizar otros separadores, puedes modificar la opción segment_separators en la configuración de la factoría sfPatternRouting (ver capítulo 19).
El sistema de enrutamiento procesa el archivo routing.yml desde la primera línea hasta la última y se detiene en la primera regla que produzca una coincidencia. Por este motivo se deben añadir las reglas personalizadas antes que las reglas por defecto. Si se consideran las reglas del listado 9-16, la URL /valor/123 produce coincidencias con las dos reglas, pero como Symfony prueba primero la regla mi_regla:, y esa regla produce una coincidencia, ni siquiera se llega a probar la regla default:. De esta forma, la petición se procesa en la acción mimodulo/miaccion con el parámetro id inicializado con el valor 123 (no se procesa por tanto en la acción valor/123).
Listado 9-16 - Las reglas se procesan de principio a fin
mi_regla: url: /valor/:id param: { module: mimodulo, action: miaccion } # default rules default: url: /:module/:action/*
No siempre que se crea una nueva acción es necesario añadir una nueva regla al sistema de enrutamiento. Si el patrón modulo/accion es útil para la nueva acción, no es necesario añadir más reglas al archivo routing.yml. Sin embargo, si se quieren personalizar las URL externas de la acción, es necesario añadir una nueva regla por encima de las reglas por defecto.
El listado 9-17 muestra el proceso de modificación del formato de la URL externa de la acción articulo/ver.
Listado 9-17 - Modificación del formato de las URL externas de la acción articulo/ver
<?php echo url_for('articulo/ver?id=123') ?> => /articulo/ver/id/123 // Formato por defecto // Para cambiarlo por /articulo/123, se añade una nueva regla al // principio del archivo routing.yml articulo_segun_id: url: /articulo/:id param: { module: articulo, action: ver }
El problema es que la regla articulo_segun_id del listado 9-17 rompe con el enrutamiento normal de todas las otras acciones del módulo articulo. De hecho, ahora una URL como articulo/borrar produce una coincidencia en esta regla, por lo que no se ejecuta la regla default, sino que se ejecuta la regla articulo_segun_id. Por tanto, esta URL no llama a la acción borrar, sino que llama a la acción ver con el atributo id inicializado con el valor borrar. Para evitar estos problemas, se deben definir restricciones en el patrón, de forma que la regla articulo_segun_id solo produzca coincidencias con las URL cuyo comodín id sea un número entero.
Cuando una URL puede producir coincidencias con varias reglas diferentes, se deben refinar las reglas añadiendo restricciones o requisitos a sus patrones. Un requisito es una serie de expresiones regulares que deben cumplir los comodines para que la regla produzca una coincidencia.
Para modificar por ejemplo la regla articulo_segun_id anterior de forma que solo se aplique a las URL cuyo atributo id sea un número entero, se debe añadir una nueva línea a la regla, como muestra el listado 9-18.
Listado 9-18 - Añadiendo requisitos a las reglas de enrutamiento
articulo_segun_id: url: /articulo/:id param: { module: articulo, action: ver } requirements: { id: \d+ }
Ahora, una URL como articulo/borrar nunca producirá una coincidencia con la regla articulo_segun_id, porque la cadena de texto borrar no cumple con los requisitos de la regla. Por consiguiente, el sistema de enrutamiento continua buscando posibles coincidencias con otras reglas hasta que al final la encuentra en la regla llamada default.
Para completar las reglas, se pueden asignar valores por defecto a los comodines con nombre, incluso aunque el parámetro no esté definido. Los valores por defecto se establecen en el array param:.
Por ejemplo, la regla articulo_segun_id no se ejecuta si no se pasa el parámetro id. El listado 9-19 muestra como forzar la presencia de ese parámetro.
Listado 9-19 - Asignar un valor por defecto a un comodín
articulo_segun_id: url: /articulo/:id param: { module: articulo, action: ver, id: 1 }
Los parámetros por defecto no necesariamente tienen que ser comodines que se encuentran en el patrón de la regla de enrutamiento. En el listado 9-20, al parámetro display se le asigna el valor true, aunque ni siquiera forma parte de la URL.
Listado 9-20 - Asignar un valor por defecto a un parámetro de la petición
articulo_segun_id: url: /articulo/:id param: { module: articulo, action: ver, id: 1, display: true }
Si se mira con un poco de detenimiento, se puede observar que articulo y ver son también valores por defecto asignados a las variables module y action que no se encuentran en el patrón de la URL.
Para incluir un parámetro por defecto en todas las reglas de enrutamiento, se utiliza el método sfRouting::setDefaultParameter(). Si por ejemplo se necesita que todas las reglas tengan un parámetro llamado tema con un valor por defecto igual a default, se añade la instrucción $this->context->getRouting()->setDefaultParameter('tema', 'default'); en al menos un filtro global de la aplicación.
Los helpers de enlaces aceptan como argumento el nombre o etiqueta de la regla en vez del par modulo/acción, siempre que la etiqueta vaya precedida del signo @, como muestra el listado 9-21.
Listado 9-21 - Uso de la etiqueta de las reglas en vez de Modulo/Acción
<?php echo link_to('Mi artículo', 'articulo/ver?id='.$articulo->getId()) ?> // también se puede escribir como... <?php echo link_to('Mi artículo', '@articulo_segun_id?id='.$articulo->getId()) ?>
Esta técnica tiene sus ventajas e inconvenientes. En cuanto a las ventajas:
routing.yml. Todas las llamadas al helper link_to() funcionarán sin tener que realizar ningún cambio.@ver_articulo_segun_slug que simplemente llamar a articulo/ver. Por otra parte, la desventaja principal es que es más complicado añadir los enlaces, ya que siempre se debe consultar el archivo routing.yml para saber el nombre de la regla que se utiliza en la acción.
La mejor técnica de las 2 depende del proyecto en el que se trate, por lo que es el programador el que tendrá que tomar la decisión.
Mientras se prueba la aplicación (en el entorno dev), se puede comprobar la regla que se está aplicando para cada petición del navegador. Para ello, se debe desplegar la sección "logs and msgs" de la barra de depuración y se debe buscar la línea que dice "matched route XXX". El Capítulo 16 contiene más información sobre el modo de depuración web.
Desde la versión 1.1 de Symfony las operaciones del sistema de enrutamiento son mucho más rápidas en el entorno de producción, ya que las conversiones de URI internas a URL externas se guardan en la caché.
Si se comparan estas dos URL:
http://frontend.ejemplo.com/articulo/Economia_en_Francia http://frontend.ejemplo.com/articulo/Economia_en_Francia.html
Aunque se trata de la misma página, los usuarios (y los robots que utilizan los buscadores) las consideran como si fueran diferentes debido a sus URL. La segunda URL parece que pertenece a un directorio web de páginas estáticas correctamente organizadas, que es exactamente el tipo de sitio web que mejor saben indexar los buscadores.
Para añadir un sufijo a todas las URL externas generadas en el sistema de enrutamiento, se debe modificar el valor de la opción suffix en el archivo de configuración settings.yml, como se muestra en el listado 9-22.
Listado 9-22 - Establecer un sufijo a todas las URL, en frontend/config/factories.yml
prod:
routing:
param:
suffix: .htmlEl sufijo por defecto es un punto (.), lo que significa que el sistema de enrutamiento no añade ningún sufijo a menos que se especifique uno.
En ocasiones es necesario indicar un sufijo específico para una única regla de enrutamiento. En ese caso, se indica el sufijo directamente como parte del patrón definido mediante url: en la regla del archivo routing.yml, como se muestra en el listado 9-23. El sufijo global se ignora en este caso.
Listado 9-23 - Estableciendo un sufijo en una única URL, en frontend/config/routing.yml
articulo_listado: url: /ultimos_articulos param: { module: articulo, action: listado } articulo_listado_rss: url: /ultimos_articulos.rss param: { module: articulo, action: listado, type: feed }
Como sucede con la mayoría de archivos de configuración, el archivo routing.yml es una buena solución para definir las reglas del sistema de enrutamiento, pero no es la única solución. Se pueden definir reglas en PHP, en el archivo config.php de la aplicación o en el script del controlador frontal, pero antes de llamar a la función dispatch(), ya que este método determina la acción que se ejecuta en función de las reglas de enrutamiento disponibles en ese momento. Definir reglas mediante PHP permite crear reglas dinámicas que dependan de la configuración o de otros parámetros.
El objeto que gestiona las reglas de enrutamiento es una factoría llamada sfPatternRouting. Se encuentra disponible en cualquier parte del código mediante la llamada sfContext::getInstance()->getRouting(). Su método prependRoute() añade una nueva regla por encima de las reglas definidas en el archivo routing.yml. El método espera 4 parámetros, que son los mismos que se utilizan para definir una regla: la etiqueta de la ruta, el patrón de la URL, el array asociativo con los valores por defecto y otro array asociativo con los requisitos. La regla definida en el archivo routing.yml del listado 9-18 es equivalente por ejemplo al código PHP mostrado en el listado 9-24.
A partir de la versión 1.1 de Symfony la clase que se encarga del enrutamiento se puede configurar en el archivo de configuración factories.yml. En este capítulo se explica el funcionamiento de la clase sfPatternRouting, que es la clase configurada por defecto para gestionar el sistema de enrutamiento, mientras que en el capítulo 17 se explica cómo cambiar esa clase por defecto.
Listado 9-24 - Definiendo una regla en PHP
sfContext::getInstance()->getRouting()->prependRoute( 'articulo_segun_id', // Nombre ruta '/articulo/:id', // Patrón de la ruta array('module' => 'articulo', 'action' => 'ver'), // Valores por defecto array('id' => '\d+'), // Requisitos );
La clase sfPatternRouting define otros métodos muy útiles para la gestión manual de las rutas: clearRoutes(), hasRoutes(), etc. La API de Symfony (http://www.symfony-project.org/api/1_1/) dispone de mucha más información.
A medida que se profundiza en los conceptos presentados en este libro, se pueden ampliar los conocimientos visitando la documentación de la API disponible online o incluso, investigando el código fuente de Symfony. En este libro no se describen todas las opciones y parámetros de Symfony, pero la documentación online contiene todos los detalles posibles.
Capítulo original en inglés Chapter 9 - Links And The Routing System Traducido por Javier Eguíluz