Introducción
Las rutas de Laravel son una gran herramienta que nos permite construir una api rápidamente, y de forma fácil y eficaz. Simplemente tenemos que definir una ruta así:
<?php
Route::get("/usuarios", "UsuariosController@todos");
?>
Para después poder consumirla con una petición get a sitio.com/api/usuarios (por ejemplo). De esta manera nos evitamos parsear la ruta y muchas cosas que son laboriosas.
También podemos definir una ruta para que coincida con variables. Por ejemplo, la siguiente ruta muestra las ventas de algún año:
<?php
Route::get("/ventas/{anio}", "VentasController@porAnio");
?>
Podemos consumirla llamando a sitio.com/api/ventas/2017 y obtendríamos las ventas del año 2017 (en un caso teórico).
Conforme nuestras rutas crezcan, puede que olvidemos las rutas que hemos definido anteriormente y que incluso repitamos algunas de ellas.
Descripción del problema o error
Como se vio anteriormente, podemos obtener las ventas de un año con la siguiente ruta:
<?php
Route::get("/ventas/{anio}", "VentasController@porAnio");
?>
Y puede que después definamos una ruta para obtener las ventas canceladas, así:
<?php
Route::get("/ventas/canceladas", "VentasController@canceladas");
?>
Si ahora llamamos a sitio.com/api/ventas/2017 obtendremos las ventas del 2017, claro está. Y si quiero las ventas canceladas podría llamar a sitio.com/api/ventas/canceladas.
Pues aquí está el problema, ya que al llamar a sitio.com/api/ventas/canceladas Laravel pensará que estamos consultando las ventas por año, y que queremos las ventas en donde el año sea “canceladas” cosa que no tiene sentido. Lo que en realidad queremos son las ventas canceladas.
Esto podría traernos muchos errores, ya que no sabríamos por qué al llamar a sitio.com/api/ventas/canceladas no nos está devolviendo nada.
Solución
Podemos hacer dos cosas: renombrar nuestras rutas o delimitarlas con una expresión regular.
Renombrando rutas
Una solución que no me gusta mucho pero que funciona es renombrar nuestras rutas. Por ejemplo, podemos cambiar a que las ventas por año sean así:
<?php
Route::get("/ventas/anio/{anio}", "VentasController@porAnio");
?>
Y ahora al llamar a ventas canceladas las rutas no se confundirán. Ya que para las ventas canceladas se llamará a sitio.com/api/ventas/canceladas y para las ventas por año se llamará a sitio.com/api/ventas/anio/2017 (agregamos anio antes de la variable).
De esta manera ya no chocarán. A mí se me hace una solución no tan práctica, pero funciona perfectamente y no tiene nada de malo.
Restringiendo rutas con expresión regular
Otra solución que me gusta más es hacer que la ruta coincida sólo si ésta coincide a su vez con una expresión regular. Es decir, podemos usar el método where de las rutas, para indicar que queremos que coincida sólo si es un número.
La ruta de las ventas por año quedaría así:
<?php
Route::get("/ventas/{anio}", "VentasController@porAnio")
->where(['anio' => '^\d{4}$']);
?>
Simplemente estamos indicando que coincida sólo si la variable año es un número de 4 dígitos.
Así que al llamar a sitio.com/api/ventas/2017 obtendremos las ventas del 2017, ya que 2017 es un año de 4 dígitos.
Al llamar a sitio.com/api/ventas/canceladas la ruta no coincidirá, ya que “canceladas” no es un año, por lo que Laravel buscará otra ruta para ver si coincide antes de lanzar un error. Y como encontrará la ruta de las ventas canceladas, caerá ahí y obtendremos correctamente lo que queremos.
Usar las expresiones regulares también nos ayuda a mejorar un poco nuestra aplicación, y a delimitar datos para evitar errores.
Referencias
Aquí dejo algunos enlaces que pueden ser de ayuda para entender mejor esto
Documentación oficial de las rutas de Laravel
Expresión regular para un año de 4 dígitos
Probar expresiones regulares online
Aprende más sobre Laravel aquí.
Pingback: Explicando las rutas web en Laravel 5.7 - Parzibyte's blog