Curso de Laravel

Rutas en Laravel: definir las URLs de tu aplicación

Por Víctor Peña · Publicado el

Hola, ¿cómo están? Empezamos el módulo de la aplicación web. Hoy vemos las rutas: la pieza que decide qué código responde a cada dirección del navegador.

¡Empecemos!

Dónde se definen

En routes/web.php, que como vimos en la lección de estructura es uno de los dos archivos de rutas que trae un proyecto nuevo.

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

Esa ruta dice: cuando alguien visite la raíz del sitio con el método GET, devuelve la vista welcome.

Los métodos HTTP

Route::get('/pacientes', ...);          // mostrar
Route::post('/pacientes', ...);         // crear
Route::put('/pacientes/{id}', ...);     // actualizar completo
Route::patch('/pacientes/{id}', ...);   // actualizar parcial
Route::delete('/pacientes/{id}', ...);  // eliminar

Cada método tiene un significado convenido, el mismo que vimos al hablar de APIs REST. Respetarlo hace que tu aplicación sea predecible.

Si una ruta debe responder a varios métodos:

Route::match(['get', 'post'], '/buscar', ...);
Route::any('/webhook', ...);

Apuntar a un controlador

Definir la lógica dentro de la ruta sirve para ejemplos, pero en un proyecto real apunta a un controlador:

use App\Http\Controllers\PacienteController;

Route::get('/pacientes', [PacienteController::class, 'index']);
Route::get('/pacientes/{id}', [PacienteController::class, 'show']);

Lo vemos en detalle en la siguiente lección.

Parámetros

Route::get('/pacientes/{id}', function ($id) {
    return "Paciente número: $id";
});

El nombre entre llaves llega como argumento.

Parámetros opcionales, con signo de interrogación y valor por defecto:

Route::get('/citas/{estado?}', function ($estado = 'programada') {
    return Cita::where('estado', $estado)->get();
});

Restricciones, para que la ruta solo acepte cierto formato:

Route::get('/pacientes/{id}', ...)->whereNumber('id');
Route::get('/perfil/{nombre}', ...)->whereAlpha('nombre');
Route::get('/citas/{estado}', ...)
    ->whereIn('estado', ['programada', 'atendida', 'cancelada']);

Esto importa más de lo que parece: sin restricción, /pacientes/abc entra en la ruta y falla dentro del controlador. Con whereNumber(), Laravel devuelve un 404 limpio.

Vinculación de modelos

Aquí hay algo muy cómodo que conviene adoptar desde el principio.

// Sin vinculación
Route::get('/pacientes/{id}', function ($id) {
    $paciente = Paciente::findOrFail($id);
    return view('pacientes.show', compact('paciente'));
});

// Con vinculación
Route::get('/pacientes/{paciente}', function (Paciente $paciente) {
    return view('pacientes.show', compact('paciente'));
});

Laravel ve que el parámetro se llama {paciente} y que la función espera un Paciente, así que busca el registro por su id y lo inyecta ya resuelto. Si no existe, devuelve 404 automáticamente.

Y si prefieres buscar por otra columna:

Route::get('/pacientes/{paciente:cedula}', ...);

Rutas con nombre

Esta es de las prácticas que más problemas evita a largo plazo.

Route::get('/pacientes', [PacienteController::class, 'index'])
    ->name('pacientes.index');

Route::get('/pacientes/{paciente}', [PacienteController::class, 'show'])
    ->name('pacientes.show');

Y a partir de ahí, en vez de escribir la URL a mano:

route('pacientes.index');
route('pacientes.show', $paciente);
route('pacientes.show', ['paciente' => 5]);

En las vistas:

<a href="{{ route('pacientes.index') }}">Ver pacientes</a>

Por qué importa: el día que decidas cambiar /pacientes por /clientes, lo cambias en la ruta y todos los enlaces del proyecto siguen funcionando. Si escribiste las URLs a mano, te toca buscarlas una por una.

La convención de nombres es recurso.accion: pacientes.index, citas.create, doctores.edit.

Redirecciones y vistas directas

Para casos simples no hace falta controlador:

Route::redirect('/inicio', '/');
Route::redirect('/viejo', '/nuevo', 301);   // permanente

Route::view('/nosotros', 'paginas.nosotros');
Route::view('/contacto', 'paginas.contacto', ['telefono' => '76980507']);

Grupos de rutas

Cuando varias rutas comparten algo, se agrupan.

Prefijo común:

Route::prefix('admin')->group(function () {
    Route::get('/pacientes', ...);   // /admin/pacientes
    Route::get('/citas', ...);       // /admin/citas
});

Prefijo de nombre:

Route::name('admin.')->group(function () {
    Route::get('/pacientes', ...)->name('pacientes');   // admin.pacientes
});

Middleware:

Route::middleware('auth')->group(function () {
    Route::get('/panel', ...);
    Route::get('/perfil', ...);
});

Y todo combinado, que es como se ve en un proyecto real:

Route::middleware(['auth', 'verified'])
    ->prefix('admin')
    ->name('admin.')
    ->group(function () {
        Route::get('/pacientes', [PacienteController::class, 'index'])
            ->name('pacientes.index');

        Route::get('/citas', [CitaController::class, 'index'])
            ->name('citas.index');
    });

Esas rutas quedan en /admin/pacientes, se llaman admin.pacientes.index y exigen usuario autenticado y verificado.

Rutas de recurso

Un CRUD necesita siempre las mismas siete rutas. Laravel las genera de una vez:

Route::resource('pacientes', PacienteController::class);

Eso crea:

Método URI Acción Nombre
GET /pacientes index pacientes.index
GET /pacientes/create create pacientes.create
POST /pacientes store pacientes.store
GET /pacientes/{paciente} show pacientes.show
GET /pacientes/{paciente}/edit edit pacientes.edit
PUT/PATCH /pacientes/{paciente} update pacientes.update
DELETE /pacientes/{paciente} destroy pacientes.destroy

Es el estándar del framework, y adoptarlo hace que cualquier desarrollador entienda tu proyecto de inmediato.

Y se puede acotar:

Route::resource('pacientes', PacienteController::class)
    ->only(['index', 'show']);

Route::resource('pacientes', PacienteController::class)
    ->except(['destroy']);

Para recursos anidados:

Route::resource('pacientes.citas', CitaController::class);
// /pacientes/{paciente}/citas

Construiremos un CRUD completo sobre estas rutas en el módulo siguiente.

Ver las rutas registradas

php artisan route:list
php artisan route:list --path=pacientes
php artisan route:list --name=admin

Cuando una dirección devuelva 404, este es el primer comando que hay que ejecutar. Te dice si la ruta existe, con qué método y qué controlador la atiende.

El orden importa

Laravel evalúa las rutas de arriba abajo y usa la primera que coincide:

// Mal: /pacientes/create entra en la primera
Route::get('/pacientes/{id}', ...);
Route::get('/pacientes/create', ...);

// Bien: las rutas fijas van antes que las de parámetro
Route::get('/pacientes/create', ...);
Route::get('/pacientes/{id}', ...);

Es un error que da mensajes confusos, porque la ruta existe pero nunca se alcanza.

Rutas de API

Como vimos, routes/api.php no viene por defecto:

php artisan install:api

Las rutas de ahí llevan automáticamente el prefijo /api, no tienen sesión y se autentican con token. Lo veremos en la lección de API REST.

Errores comunes

  • Escribir URLs a mano en lugar de usar route().
  • Rutas con parámetro antes que las fijas.
  • No poner restricciones y dejar que el fallo ocurra dentro del controlador.
  • Definir lógica en la ruta en lugar de en un controlador.
  • Olvidar install:api y buscar un archivo que no existe.
  • No usar route:list al depurar un 404.

Para cerrar

Las rutas son el mapa de tu aplicación. Dos hábitos que conviene adoptar desde el primer día: ponerles nombre siempre y usar Route::resource para los CRUD.

Con eso, tu proyecto queda predecible y los cambios de URL dejan de ser un problema.

En la siguiente lección vemos los controladores, que es donde va la lógica que estas rutas invocan.

Saludos y éxitos.

Norvic Software

Desarrollamos el software que tu empresa necesita

Somos una empresa de software en Bolivia. Construimos sistemas a medida y aplicaciones móviles, y llevamos Inteligencia Artificial a las empresas que ya tienen un sistema funcionando.

Solicitar cotizaciónVer todos los servicios

Cotización sin costo · Respuesta directa por WhatsApp