Explicaciones detalladas sobre cómo trabajar con parámetros en las rutas del framework PHP Laravel.
En el Manual de Laravel 5 hemos comenzado a tratar el sistema de rutas en profundidad. En el artículo anterior os hablábamos sobre las rutas y sus verbos HTTP, en este momento vamos a dedicarnos a algunos temas relacionados con el paso de parámetros.
Las rutas en las aplicaciones web corresponden con patrones en los que en algunas ocasiones se encuentran textos fijos y en otras ocasiones textos que van a ser variables. A lo largo de este artículo nos vamos a referir a esos textos variables con el nombre de parámetros y se pueden producir con una sintaxis de llaves.
Veamos varias rutas de una supuesta aplicación para entender qué es esto de los parámetros.
example.com/colaboradores/miguel
example.com/colaboradores/carlos
example.com/tienda/productos/34
example.com/agenda/julio/2015
example.com/categoria/php
example.com/categoria/php/2
Podrás apreciar esas rutas y verás que hay zonas que son variables, por ejemplo, el nombre del colaborador que se pretende ver, el identificador del producto de una tienda, el mes y el año de una agenda, la categoría que se desea ver o la página de un hipotético listado de artículos de una categoría.
Crear rutas con parámetros
A continuación vamos a mostrar cómo se podrían crear algunas de esas rutas anteriores en el sistema de routing de Laravel.
Route::get('colaboradores/{nombre}', function($nombre){
return "Mostrando el colaborador $nombre";
});
Como puedes observar, en este ejemplo el nombre del colaborador es variable, por ello se expresa como un parámetro, encerrado entre llaves. En la función closure recibimos el parámetro y podemos trabajar con él, como con cualquier parámetro de una función.
El nombre del parámetro (variable con la que recibimos ese parámetro en la función anónima o closure) es independiente, como puedes ver en el siguiente ejemplo. Podríamos tener en el patrón de la ruta definido el {id} y luego recibir ese dato en una variable $id_producto. Lo que importa en este caso es el orden con el que se han definido los parámetros en el patrón del URI.
Route::get('tienda/productos/{id}', function($id_producto){
return "Mostrando el producto $id_producto de la tienda";
});
Se pueden enviar varios parámetros si se desea. Simplemente los recogeremos en la función, de una manera similar.
Route::get('agenda/{mes}/{ano}', function($mes, $ano){
return "Viendo la agenda de $mes de $ano";
});
Aquí lo importante no son el nombre de las variables con las que recoges los parámetros, sino el orden en el que fueron declarados en el patrón de la URI. Primero recibimos el parámetro $mes porque en la URI figura con anterioridad. Recuerda que el patrón era algo como "agenda/julio/2015".
Enviar los parámetros a los controladores
Aunque todavía no hemos hablado de los controladores, queremos poner un ejemplo aquí, aunque realmente no cambia mucho sobre lo que hemos visto.
Al definir una ruta a un controlador tenemos que indicarlo en el método que registra la ruta. En ese método, en lugar de definir una función anónima indicaremos el nombre y método del controlador a ejecutar.
Route::get('tienda/productos/{id}','TiendaController@producto');
Luego en el controlador recibimos el parámetro definido en el patrón de URI de la ruta registrada.
public function producto($id)
{
return "Esto muestra un producto. Recibiendo $id";
}
Parámetros opcionales
Hay ocasiones en las que se especifican parámetros que tienen valores opcionales. Mira las siguientes rutas:
example.com/categoria/php
example.com/categoria/php/2
Como puedes comprobar, a veces indicamos la página de categoría que se desea mostrar y a veces no se indica nada. Esto imitaría el funcionamiento de un paginador, en la que, si no se recibe nada, se mostraría la primera página y si se indica un número de página se mostraría esa en concreto.
Los parámetros opcionales en Laravel se indican con un símbolo de interrogación. En este código en el patrón de la URI observarás que la página es opcional.
Route::get('categoria/{categoria}/{pagina?}', function($categoria, $pagina = 1){
return "Viendo categoría $categoria y página $pagina";
});
Resultará de utilidad, al definir el closure, indicar un valor por defecto para aquellos parámetros que son opcionales. Si no lo hacemos, a la hora de usar ese parámetro dentro de la función anónima, nos mostrará un mensaje de error en caso que no lo enviemos "Missing argument 2 for...".
Precedencia de las rutas
Con dos rutas registradas que tienen patrones distintos, si por un casual una URI puede encajar en el formato definido por ambos patrones, el que se ejecutará será el que primero se haya escrito en el archivo routes.php.
Route::get('categoria/{categoria}', function($categoria){
return "Ruta 1- Viendo categoría $categoria y no recibo página";
});
Route::get('categoria/{categoria}/{pagina?}', function($categoria, $pagina=1){
return "Ruta 2 - Viendo categoría $categoria y página $pagina";
});
En esas dos rutas tenemos patrones diferentes de URI, pero si alguien escribe:
example.com/categoria/laravel/
Esa URL podría casar con ambos patrones de URI. En este caso, el mensaje que obtendremos será:
Ruta 1 - Viendo categoría laravel y no recibo página
En este caso no necesitaríamos haber indicado el valor por defecto en $pagina para la segunda ruta registrada, pues nunca se invocaría a esa ruta sin enviarle algún valor de página.
Aceptar solamente determinados valores de parámetros
Hay una posibilidad muy útil con los parámetros de las rutas que consiste en definir expresiones regulares para especificar qué tipo de valores aceptas en los parámetros. Por ejemplo, un identificador de producto debe ser un valor numérico o un nombre de un colaborador te debe aceptar solamente caracteres alfabéticos. Si has entendido esta situación observarás que hasta el momento, tal como hemos registrado las rutas, se aceptarían todo tipo de valores en los parámetros, generando rutas que muchas veces no deberían devolver un valor de página encontrada. Por ejemplo:
example.com/colaboradores/666
example.com/tienda/productos/kkk
Recuerda que hemos dicho que colaboradores debería ser un valor de tipo alfabético y que el identificador de producto solo puede ser numérico. Así que vamos a modificar las rutas para poder agregarle el código que nos permita no aceptar valores que no deseamos.
Route::get('colaboradores/{nombre}', function($nombre){
return "Mostrando el colaborador $nombre";
})->where(array('nombre' => '[a-zA-Z]+'));
Como puedes apreciar, se le coloca encadena, sobre la ruta generada, un método where() que nos permite especificar en un array todas las reglas que se le deben aplicar a cada uno de los parámetros que queramos restringir.
Ahora mira este código, donde tenemos dos rutas definidas:
Route::get('tienda/productos/{id}', function($id_producto){
return "Mostrando el producto $id_producto de la tienda";
})->where(['id' => '[0-9]+']);
Route::get('tienda/productos/{id}','PrimerController@test');
En la primera ruta estamos obligando a que el parámetro id sea un número. Sin embargo, en la segunda ruta tenemos el mismo patrón sin definir el tipo de valor al que queremos restringir. En este caso, si el id fuera un valor numérico se iría por la primera ruta y si es un valor diferente se iría por la segunda. En fin, que podemos configurar las rutas para hacer muchas cosas distintas y definir innumerables comportamientos atendiendo a las necesidades de la aplicación y a la manera en la que prefiramos organizar nuestro código.
De momento con lo que hemos visto sobre rutas podemos jugar bastante, así que para avanzar en el uso de Laravel 5 vamos a cambiar de tercio en los próximos artículos para tratar otros asuntos que a buen seguro estarás impaciente por conocer.
Carlos Ruiz Ruso
Consultor tecnológico para el desarrollo de proyectos online especializado en Wo...