9: Enrutamiento Básico en Laravel 12 - Guía Práctica

 


Introducción

El enrutamiento es uno de los pilares fundamentales de cualquier aplicación web. En Laravel, el sistema de rutas es elegante, poderoso y flexible, permitiéndote definir cómo tu aplicación responde a las diferentes peticiones HTTP. En este tutorial, aprenderás desde los conceptos más básicos hasta técnicas avanzadas de enrutamiento en Laravel 12.

Índice

  1. ¿Qué es el Enrutamiento?

  2. Estructura de Rutas en Laravel

  3. Tipos de Rutas por Método HTTP

  4. Rutas con Parámetros

  5. Rutas con Expresiones Regulares

  6. Nombres de Rutas

  7. Grupos de Rutas

  8. Rutas Resource

  9. Redirecciones y Vistas

  10. Rutas para API

  11. Ejercicio Práctico: Sistema de Pasteles


1. ¿Qué es el Enrutamiento?

El enrutamiento es el proceso mediante el cual tu aplicación determina qué código ejecutar cuando un usuario accede a una URL específica. Laravel utiliza un sistema de rutas que mapea las URLs a controladores o funciones anónimas (closures).

Flujo básico de una petición HTTP en Laravel

text
Usuario (Navegador) 
    ↓
Petición HTTP (GET /pasteles)
    ↓
Archivo routes/web.php
    ↓
Coincidencia de ruta
    ↓
Controlador/Closure
    ↓
Vista/Respuesta
    ↓
Usuario ve la página

Arquitectura MVC en Laravel

text
┌─────────────────────────────────────────────────────────────┐
│                         USUARIO                            │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                       RUTAS (routes)                       │
│               app/Http/routes.php                         │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                    CONTROLADORES                           │
│               app/Http/Controllers/                        │
└──────────┬──────────────────────────────────┬──────────────┘
           │                                  │
           ▼                                  ▼
┌─────────────────────┐         ┌─────────────────────────────┐
│     MODELOS         │         │         VISTAS              │
│   app/Models/       │         │    resources/views/         │
│   (Base de Datos)   │         │    (HTML con Blade)         │
└─────────────────────┘         └─────────────────────────────┘

2. Estructura de Rutas en Laravel

Archivos de Rutas

Laravel 12 organiza las rutas en varios archivos dentro de la carpeta routes/:

text
routes/
├── web.php          # Rutas para la interfaz web (sesiones, CSRF, etc.)
├── api.php          # Rutas para API (sin sesiones, con tokens)
├── console.php      # Rutas para comandos de consola
└── channels.php     # Rutas para canales de broadcasting

Ruta básica

php
// routes/web.php

use Illuminate\Support\Facades\Route;

// Ruta más simple: devuelve texto
Route::get('/saludo', function () {
    return "Bienvenido a Laravel 12!";
});

Ruta con vista

php
// Ruta que devuelve una vista
Route::get('/inicio', function () {
    return view('inicio');
});

// Ruta que devuelve una vista con datos
Route::get('/pasteles', function () {
    $titulo = "Lista de Pasteles";
    return view('pasteles.index', ['titulo' => $titulo]);
});

Ruta que usa un controlador

php
// Usando el formato tradicional
Route::get('/pasteles', [PastelController::class, 'index']);

// Usando el formato de invocación (Laravel 12)
Route::get('/pasteles', 'App\Http\Controllers\PastelController@index');

// Con el namespace completo (Laravel 12)
Route::get('/pasteles', 'PastelController@index');

3. Tipos de Rutas por Método HTTP

Verbos HTTP disponibles

MétodoUsoEjemplo
GETObtener recursosMostrar página, listar datos
POSTCrear recursosEnviar formulario, crear registro
PUTActualizar recursosActualizar completamente
PATCHActualizar parcialmenteModificar algunos campos
DELETEEliminar recursosBorrar registro
OPTIONSObtener opcionesCORS preflight

Ejemplos de cada tipo

php
use Illuminate\Http\Request;

// GET - Obtener lista de pasteles
Route::get('/pasteles', function () {
    return "Lista de pasteles";
});

// POST - Crear un nuevo pastel
Route::post('/pasteles', function (Request $request) {
    return "Creando un pastel: " . $request->nombre;
});

// PUT - Actualizar un pastel completamente
Route::put('/pasteles/{id}', function ($id) {
    return "Actualizando pastel ID: " . $id;
});

// PATCH - Actualizar parcialmente un pastel
Route::patch('/pasteles/{id}', function ($id) {
    return "Actualizando parcialmente pastel ID: " . $id;
});

// DELETE - Eliminar un pastel
Route::delete('/pasteles/{id}', function ($id) {
    return "Eliminando pastel ID: " . $id;
});

// Ruta que acepta cualquier método HTTP
Route::any('/cualquiera', function () {
    return "Esta ruta acepta cualquier método HTTP";
});

// Ruta que coincide con múltiples verbos
Route::match(['get', 'post'], '/mixta', function () {
    return "Esta ruta acepta GET y POST";
});

Verificar las rutas registradas

bash
# Listar todas las rutas
php artisan route:list

# Filtrar por nombre
php artisan route:list --name=pasteles

# Filtrar por método
php artisan route:list --method=POST

# Ver rutas en formato JSON
php artisan route:list --json

4. Rutas con Parámetros

Parámetros obligatorios

php
// Parámetro simple
Route::get('/pastel/{id}', function ($id) {
    return "Mostrando pastel con ID: " . $id;
});

// Múltiples parámetros
Route::get('/pastel/{id}/ingrediente/{ingredienteId}', function ($id, $ingredienteId) {
    return "Pastel ID: {$id}, Ingrediente ID: {$ingredienteId}";
});

// Parámetros con nombres descriptivos
Route::get('/usuario/{nombre}/pastel/{pastelId}', function ($nombre, $pastelId) {
    return "Usuario: {$nombre} - Pastel: {$pastelId}";
});

Parámetros opcionales

php
// Parámetro opcional (con ? y valor por defecto)
Route::get('/pasteles/{sabor?}', function ($sabor = 'todos') {
    return "Mostrando pasteles de sabor: " . $sabor;
});

// Múltiples parámetros opcionales
Route::get('/buscar/{categoria?}/{subcategoria?}', function ($categoria = 'todos', $subcategoria = null) {
    return "Buscando: Categoría: {$categoria}, Subcategoría: {$subcategoria}";
});

// Parámetro opcional con validación adicional
Route::get('/pasteles/{sabor?}', function ($sabor = 'todos') {
    $saboresValidos = ['chocolate', 'vainilla', 'cheesecake', 'todos'];
    if (!in_array($sabor, $saboresValidos)) {
        return "Sabor no válido";
    }
    return "Mostrando pasteles de sabor: " . $sabor;
});

Parámetros con nombres y validación

php
// Parámetros con nombres claros
Route::get('/pasteles/{nombre}/{sabor}', function ($nombre, $sabor) {
    return "Pastel: {$nombre}, Sabor: {$sabor}";
});

// Parámetros con tipado
Route::get('/pasteles/{id}', function (int $id) {
    return "Pastel con ID numérico: " . $id;
});

// Parámetros con validación de tipo
Route::get('/pasteles/{id}', function (string $id) {
    return "ID como string: " . $id;
});

5. Rutas con Expresiones Regulares

Validación con where

php
// Validar que el ID sea numérico
Route::get('/pastel/{id}', function ($id) {
    return "Pastel ID: " . $id;
})->where('id', '[0-9]+');

// Validar que el nombre solo tenga letras
Route::get('/pastel/nombre/{nombre}', function ($nombre) {
    return "Pastel: " . $nombre;
})->where('nombre', '[A-Za-zÁ-ú]+');

// Validar formato específico
Route::get('/pastel/codigo/{codigo}', function ($codigo) {
    return "Código: " . $codigo;
})->where('codigo', '[A-Z]{3}-[0-9]{4}');

// Múltiples restricciones
Route::get('/pastel/{id}/{nombre}', function ($id, $nombre) {
    return "ID: {$id}, Nombre: {$nombre}";
})->where([
    'id' => '[0-9]+',
    'nombre' => '[A-Za-zÁ-ú\s]+'
]);

// Restricción de longitud
Route::get('/pastel/{codigo}', function ($codigo) {
    return "Código: " . $codigo;
})->where('codigo', '^[A-Za-z0-9]{8}$');

Patrones comunes con expresiones regulares

php
// ID numérico
Route::get('/usuario/{id}', function ($id) {
    return "Usuario ID: " . $id;
})->where('id', '[0-9]+');

// UUID
Route::get('/pedido/{uuid}', function ($uuid) {
    return "Pedido UUID: " . $uuid;
})->where('uuid', '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}');

// Email
Route::get('/usuario/email/{email}', function ($email) {
    return "Email: " . $email;
})->where('email', '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}');

// Fecha (YYYY-MM-DD)
Route::get('/reporte/{fecha}', function ($fecha) {
    return "Fecha: " . $fecha;
})->where('fecha', '[0-9]{4}-[0-9]{2}-[0-9]{2}');

// Teléfono (10 dígitos)
Route::get('/telefono/{numero}', function ($numero) {
    return "Teléfono: " . $numero;
})->where('numero', '^[0-9]{10}$');

// Código postal (México)
Route::get('/codigo-postal/{cp}', function ($cp) {
    return "Código Postal: " . $cp;
})->where('cp', '^[0-9]{5}$');

// URL amigable (slug)
Route::get('/articulo/{slug}', function ($slug) {
    return "Artículo: " . $slug;
})->where('slug', '[a-z0-9-]+');

Parámetros con valores por defecto y restricciones

php
// Valor por defecto con restricción
Route::get('/pasteles/{sabor?}', function ($sabor = 'todos') {
    return "Sabor: " . $sabor;
})->where('sabor', '[a-zA-Z]+');

// Múltiples parámetros con valores por defecto
Route::get('/buscar/{categoria?}/{orden?}', function ($categoria = 'todos', $orden = 'asc') {
    return "Categoría: {$categoria}, Orden: {$orden}";
})->where([
    'categoria' => '[a-zA-Z]+',
    'orden' => 'asc|desc'
]);

6. Nombres de Rutas

Asignar nombres a las rutas

php
// Ruta con nombre básico
Route::get('/pasteles', function () {
    return "Lista de pasteles";
})->name('pasteles.index');

// Ruta con nombre y parámetros
Route::get('/pastel/{id}', function ($id) {
    return "Pastel ID: " . $id;
})->name('pasteles.show');

// Ruta con nombre y controlador
Route::get('/pasteles/crear', [PastelController::class, 'create'])
    ->name('pasteles.create');

// Ruta con nombre en el segundo parámetro
Route::get('/pasteles/editar', 'PastelController@edit')->name('pasteles.edit');

Generar URLs con nombres de rutas

php
// En cualquier parte de la aplicación

// Generar URL de la ruta nombrada
$url = route('pasteles.index');
// Resultado: /pasteles

// Generar URL con parámetros
$url = route('pasteles.show', ['id' => 1]);
// Resultado: /pastel/1

// Generar URL con múltiples parámetros
$url = route('pasteles.filtro', ['sabor' => 'chocolate', 'precio' => 'bajo']);
// Resultado: /pasteles/filtro/chocolate/bajo

// En Blade
<a href="{{ route('pasteles.index') }}">Lista de Pasteles</a>
<a href="{{ route('pasteles.show', ['id' => $pastel->id]) }}">Ver Pastel</a>

// Redirección con nombre
return redirect()->route('pasteles.index');

// Redirección con parámetros
return redirect()->route('pasteles.show', ['id' => $pastel->id]);

Nombres de rutas con parámetros opcionales

php
// Ruta con nombre y parámetro opcional
Route::get('/pasteles/{sabor?}', function ($sabor = 'todos') {
    return "Sabor: " . $sabor;
})->name('pasteles.por-sabor');

// Generar URL sin parámetro
$url = route('pasteles.por-sabor');
// Resultado: /pasteles

// Generar URL con parámetro
$url = route('pasteles.por-sabor', ['sabor' => 'chocolate']);
// Resultado: /pasteles/chocolate

Organizar nombres con grupos

php
Route::name('admin.')->group(function () {
    Route::get('/admin/dashboard', function () {
        return "Dashboard";
    })->name('dashboard'); // admin.dashboard
    
    Route::get('/admin/usuarios', function () {
        return "Usuarios";
    })->name('usuarios'); // admin.usuarios
    
    Route::get('/admin/usuarios/{id}', function ($id) {
        return "Usuario: " . $id;
    })->name('usuarios.show'); // admin.usuarios.show
});

// Uso
$url = route('admin.dashboard');
$url = route('admin.usuarios');
$url = route('admin.usuarios.show', ['id' => 1]);

7. Grupos de Rutas

Grupo básico de rutas

php
// Agrupar rutas con prefijo
Route::prefix('admin')->group(function () {
    Route::get('/dashboard', function () {
        return "Dashboard de administración";
    });
    
    Route::get('/usuarios', function () {
        return "Lista de usuarios";
    });
    
    Route::get('/usuarios/{id}', function ($id) {
        return "Usuario ID: " . $id;
    });
});

// Acceso: /admin/dashboard, /admin/usuarios, /admin/usuarios/1

Grupo con prefijo y nombre

php
Route::prefix('admin')
    ->name('admin.')
    ->group(function () {
        Route::get('/dashboard', function () {
            return "Dashboard";
        })->name('dashboard'); // admin.dashboard
        
        Route::get('/usuarios', function () {
            return "Usuarios";
        })->name('usuarios'); // admin.usuarios
    });

Grupo con middleware

php
// Grupo con middleware de autenticación
Route::middleware(['auth'])->group(function () {
    Route::get('/perfil', function () {
        return "Perfil de usuario";
    });
    
    Route::get('/configuracion', function () {
        return "Configuración";
    });
});

// Grupo con múltiples middlewares
Route::middleware(['auth', 'admin'])->group(function () {
    Route::get('/admin/dashboard', function () {
        return "Dashboard de administrador";
    });
    
    Route::get('/admin/usuarios', function () {
        return "Gestión de usuarios";
    });
});

Grupo con prefijo, middleware y nombre

php
Route::prefix('admin')
    ->middleware(['auth', 'admin'])
    ->name('admin.')
    ->group(function () {
        Route::get('/dashboard', function () {
            return "Dashboard";
        })->name('dashboard');
        
        Route::get('/usuarios', function () {
            return "Usuarios";
        })->name('usuarios');
    });

Grupo para rutas de API

php
Route::prefix('api')
    ->middleware('api')
    ->group(function () {
        Route::get('/pasteles', function () {
            return response()->json(['pasteles' => []]);
        });
        
        Route::post('/pasteles', function () {
            return response()->json(['mensaje' => 'Creado'], 201);
        });
        
        Route::get('/pasteles/{id}', function ($id) {
            return response()->json(['pastel' => ['id' => $id]]);
        });
    });

Grupo con subdominio

php
Route::domain('admin.miapp.com')->group(function () {
    Route::get('/dashboard', function () {
        return "Dashboard en subdominio admin";
    });
    
    Route::get('/reportes', function () {
        return "Reportes en subdominio admin";
    });
});

// Grupo con subdominio dinámico
Route::domain('{empresa}.miapp.com')->group(function () {
    Route::get('/dashboard', function ($empresa) {
        return "Dashboard de: " . $empresa;
    });
});

8. Rutas Resource

Crear una ruta resource

php
// Ruta resource básica
Route::resource('pasteles', PastelController::class);

// Rutas generadas automáticamente:
// GET       /pasteles              index
// GET       /pasteles/create       create
// POST      /pasteles              store
// GET       /pasteles/{pastel}     show
// GET       /pasteles/{pastel}/edit   edit
// PUT/PATCH /pasteles/{pastel}     update
// DELETE    /pasteles/{pastel}     destroy

Personalizar rutas resource

php
// Solo algunos métodos
Route::resource('pasteles', PastelController::class)
    ->only(['index', 'show']);

// Excepto algunos métodos
Route::resource('pasteles', PastelController::class)
    ->except(['create', 'store', 'edit', 'update', 'destroy']);

// Personalizar nombres
Route::resource('pasteles', PastelController::class)
    ->names([
        'index' => 'pasteles.lista',
        'create' => 'pasteles.crear',
        'store' => 'pasteles.guardar',
        'show' => 'pasteles.ver',
        'edit' => 'pasteles.editar',
        'update' => 'pasteles.actualizar',
        'destroy' => 'pasteles.eliminar'
    ]);

// Personalizar parámetros
Route::resource('pasteles', PastelController::class)
    ->parameters(['pasteles' => 'pastel_id']);

Recursos anidados

php
// Recursos anidados
Route::resource('categorias.pasteles', PastelController::class)
    ->shallow();

// Rutas generadas:
// GET       /categorias/{categoria}/pasteles              index
// GET       /categorias/{categoria}/pasteles/create       create
// POST      /categorias/{categoria}/pasteles              store
// GET       /pasteles/{pastel}                            show
// GET       /pasteles/{pastel}/edit                       edit
// PUT/PATCH /pasteles/{pastel}                            update
// DELETE    /pasteles/{pastel}                            destroy

Recursos API

php
// Rutas resource para API (sin create/edit)
Route::apiResource('pasteles', PastelController::class);

// Rutas API solo con algunos métodos
Route::apiResource('pasteles', PastelController::class)
    ->only(['index', 'show', 'store', 'update', 'destroy']);

// Rutas API con nombres personalizados
Route::apiResource('pasteles', PastelController::class)
    ->names([
        'index' => 'api.pasteles.lista',
        'store' => 'api.pasteles.guardar'
    ]);

9. Redirecciones y Vistas

Redirecciones en rutas

php
// Redirección simple
Route::redirect('/antigua', '/nueva');

// Redirección con código de estado
Route::redirect('/antigua', '/nueva', 301);

// Redirección con nombre de ruta
Route::permanentRedirect('/antigua', '/nueva');

Vistas directas desde rutas

php
// Vista directa
Route::view('/inicio', 'inicio');

// Vista con datos
Route::view('/bienvenida', 'bienvenida', ['nombre' => 'Juan']);

// Vista con múltiples datos
Route::view('/dashboard', 'dashboard', [
    'titulo' => 'Panel de Control',
    'usuario' => 'Administrador'
]);

// Vista con datos dinámicos
Route::view('/perfil', 'perfil', [
    'nombre' => auth()->user()->name ?? 'Invitado',
    'email' => auth()->user()->email ?? 'Sin email'
]);

10. Rutas para API

Configuración de rutas API

php
// routes/api.php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

// Ruta básica de API
Route::get('/pasteles', function () {
    return response()->json([
        'success' => true,
        'data' => [
            ['id' => 1, 'nombre' => 'Pastel de Chocolate'],
            ['id' => 2, 'nombre' => 'Pastel de Vainilla']
        ]
    ]);
});

// Ruta con parámetros
Route::get('/pasteles/{id}', function ($id) {
    return response()->json([
        'success' => true,
        'data' => ['id' => $id, 'nombre' => 'Pastel de Chocolate']
    ]);
});

// Ruta POST
Route::post('/pasteles', function (Request $request) {
    return response()->json([
        'success' => true,
        'message' => 'Pastel creado',
        'data' => $request->all()
    ], 201);
});

// Ruta PUT
Route::put('/pasteles/{id}', function ($id, Request $request) {
    return response()->json([
        'success' => true,
        'message' => 'Pastel actualizado',
        'data' => ['id' => $id] + $request->all()
    ]);
});

// Ruta DELETE
Route::delete('/pasteles/{id}', function ($id) {
    return response()->json([
        'success' => true,
        'message' => 'Pastel eliminado',
        'data' => ['id' => $id]
    ]);
});

Grupo de rutas API con versión

php
// routes/api.php

Route::prefix('v1')->group(function () {
    Route::get('/pasteles', [Api\PastelController::class, 'index']);
    Route::post('/pasteles', [Api\PastelController::class, 'store']);
    Route::get('/pasteles/{id}', [Api\PastelController::class, 'show']);
    Route::put('/pasteles/{id}', [Api\PastelController::class, 'update']);
    Route::delete('/pasteles/{id}', [Api\PastelController::class, 'destroy']);
});

Route::prefix('v2')->group(function () {
    Route::get('/pasteles', [Api\V2\PastelController::class, 'index']);
    // Versionamiento de API
});

Middleware para API

php
Route::middleware('auth:api')->group(function () {
    Route::get('/pasteles', [Api\PastelController::class, 'index']);
    Route::post('/pasteles', [Api\PastelController::class, 'store']);
    Route::get('/pasteles/{id}', [Api\PastelController::class, 'show']);
});

Route::middleware('throttle:60,1')->group(function () {
    Route::get('/publicos', function () {
        return response()->json(['publico' => true]);
    });
});

11. Ejercicio Práctico: Sistema de Pasteles

Paso 1: Crear el archivo de rutas

php
// routes/web.php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PastelController;

/*
|--------------------------------------------------------------------------
| Rutas del Sistema de Pasteles
|--------------------------------------------------------------------------
*/

// Ruta principal del sistema
Route::get('/', function () {
    return view('welcome');
})->name('home');

// Grupo de rutas para pasteles
Route::prefix('pasteles')
    ->name('pasteles.')
    ->group(function () {
        
        // Ruta para listar todos los pasteles
        Route::get('/', [PastelController::class, 'index'])
            ->name('index');
        
        // Ruta para mostrar formulario de creación
        Route::get('/crear', [PastelController::class, 'create'])
            ->name('create');
        
        // Ruta para guardar un nuevo pastel
        Route::post('/', [PastelController::class, 'store'])
            ->name('store');
        
        // Ruta para mostrar un pastel específico
        Route::get('/{pastel}', [PastelController::class, 'show'])
            ->where('pastel', '[0-9]+')
            ->name('show');
        
        // Ruta para mostrar formulario de edición
        Route::get('/{pastel}/editar', [PastelController::class, 'edit'])
            ->where('pastel', '[0-9]+')
            ->name('edit');
        
        // Ruta para actualizar un pastel
        Route::put('/{pastel}', [PastelController::class, 'update'])
            ->where('pastel', '[0-9]+')
            ->name('update');
        
        // Ruta para eliminar un pastel
        Route::delete('/{pastel}', [PastelController::class, 'destroy'])
            ->where('pastel', '[0-9]+')
            ->name('destroy');
        
        // Ruta adicional: filtrar por sabor
        Route::get('/sabor/{sabor}', [PastelController::class, 'porSabor'])
            ->where('sabor', 'chocolate|vainilla|cheesecake|fresa|coco')
            ->name('por-sabor');
        
        // Ruta adicional: pasteles disponibles
        Route::get('/disponibles', [PastelController::class, 'disponibles'])
            ->name('disponibles');
    });

// Grupo de rutas para API
Route::prefix('api')
    ->name('api.')
    ->group(function () {
        Route::get('/pasteles', [PastelController::class, 'apiIndex'])
            ->name('pasteles.index');
        
        Route::get('/pasteles/{id}', [PastelController::class, 'apiShow'])
            ->where('id', '[0-9]+')
            ->name('pasteles.show');
        
        Route::post('/pasteles', [PastelController::class, 'apiStore'])
            ->name('pasteles.store');
    });

Paso 2: Crear el controlador

bash
php artisan make:controller PastelController
php
<?php

namespace App\Http\Controllers;

use App\Models\Pastel;
use Illuminate\Http\Request;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\JsonResponse;

class PastelController extends Controller
{
    /**
     * Listar todos los pasteles
     */
    public function index(): View
    {
        $pasteles = Pastel::all();
        return view('pasteles.index', compact('pasteles'));
    }

    /**
     * Mostrar formulario de creación
     */
    public function create(): View
    {
        return view('pasteles.create');
    }

    /**
     * Guardar un nuevo pastel
     */
    public function store(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'nombre' => 'required|string|max:100',
            'sabor' => 'required|string|in:chocolate,vainilla,cheesecake,fresa,coco',
            'precio' => 'nullable|numeric|min:0',
            'disponible' => 'boolean',
            'descripcion' => 'nullable|string'
        ]);

        Pastel::create($validated);
        
        return redirect()
            ->route('pasteles.index')
            ->with('success', 'Pastel creado exitosamente');
    }

    /**
     * Mostrar un pastel específico
     */
    public function show(int $id): View
    {
        $pastel = Pastel::findOrFail($id);
        return view('pasteles.show', compact('pastel'));
    }

    /**
     * Mostrar formulario de edición
     */
    public function edit(int $id): View
    {
        $pastel = Pastel::findOrFail($id);
        return view('pasteles.edit', compact('pastel'));
    }

    /**
     * Actualizar un pastel
     */
    public function update(Request $request, int $id): RedirectResponse
    {
        $validated = $request->validate([
            'nombre' => 'required|string|max:100',
            'sabor' => 'required|string|in:chocolate,vainilla,cheesecake,fresa,coco',
            'precio' => 'nullable|numeric|min:0',
            'disponible' => 'boolean',
            'descripcion' => 'nullable|string'
        ]);

        $pastel = Pastel::findOrFail($id);
        $pastel->update($validated);
        
        return redirect()
            ->route('pasteles.index')
            ->with('success', 'Pastel actualizado exitosamente');
    }

    /**
     * Eliminar un pastel
     */
    public function destroy(int $id): RedirectResponse
    {
        $pastel = Pastel::findOrFail($id);
        $pastel->delete();
        
        return redirect()
            ->route('pasteles.index')
            ->with('success', 'Pastel eliminado exitosamente');
    }

    /**
     * Filtrar pasteles por sabor
     */
    public function porSabor(string $sabor): View
    {
        $pasteles = Pastel::where('sabor', $sabor)->get();
        return view('pasteles.index', compact('pasteles'));
    }

    /**
     * Mostrar solo pasteles disponibles
     */
    public function disponibles(): View
    {
        $pasteles = Pastel::where('disponible', true)->get();
        return view('pasteles.index', compact('pasteles'));
    }

    /**
     * API: Listar pasteles
     */
    public function apiIndex(): JsonResponse
    {
        $pasteles = Pastel::all();
        return response()->json([
            'success' => true,
            'data' => $pasteles
        ]);
    }

    /**
     * API: Mostrar pastel específico
     */
    public function apiShow(int $id): JsonResponse
    {
        $pastel = Pastel::find($id);
        
        if (!$pastel) {
            return response()->json([
                'success' => false,
                'message' => 'Pastel no encontrado'
            ], 404);
        }
        
        return response()->json([
            'success' => true,
            'data' => $pastel
        ]);
    }

    /**
     * API: Crear pastel
     */
    public function apiStore(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'nombre' => 'required|string|max:100',
            'sabor' => 'required|string|in:chocolate,vainilla,cheesecake,fresa,coco',
            'precio' => 'nullable|numeric|min:0',
            'disponible' => 'boolean',
            'descripcion' => 'nullable|string'
        ]);

        $pastel = Pastel::create($validated);
        
        return response()->json([
            'success' => true,
            'message' => 'Pastel creado exitosamente',
            'data' => $pastel
        ], 201);
    }
}

Paso 3: Verificar las rutas

bash
# Ver todas las rutas
php artisan route:list

# Ver rutas del sistema de pasteles
php artisan route:list --path=pasteles

# Ver rutas API
php artisan route:list --path=api

Paso 4: Usar las rutas en Blade

blade
<!-- En una vista Blade -->

<!-- Enlaces usando nombres de rutas -->
<a href="{{ route('pasteles.index') }}">Lista de Pasteles</a>
<a href="{{ route('pasteles.create') }}">Crear Pastel</a>
<a href="{{ route('pasteles.show', ['pastel' => $pastel->id]) }}">Ver Pastel</a>
<a href="{{ route('pasteles.edit', ['pastel' => $pastel->id]) }}">Editar Pastel</a>

<!-- Enlaces con parámetros -->
<a href="{{ route('pasteles.por-sabor', ['sabor' => 'chocolate']) }}">Pasteles de Chocolate</a>
<a href="{{ route('pasteles.disponibles') }}">Pasteles Disponibles</a>

<!-- Formularios con rutas -->
<form action="{{ route('pasteles.store') }}" method="POST">
    @csrf
    <!-- Campos del formulario -->
</form>

<form action="{{ route('pasteles.update', ['pastel' => $pastel->id]) }}" method="POST">
    @csrf
    @method('PUT')
    <!-- Campos del formulario -->
</form>

<form action="{{ route('pasteles.destroy', ['pastel' => $pastel->id]) }}" method="POST">
    @csrf
    @method('DELETE')
    <button type="submit">Eliminar</button>
</form>

Paso 5: Probar las rutas

php
// Desde el navegador:
// GET    /pasteles              - Lista de pasteles
// GET    /pasteles/crear        - Formulario de creación
// POST   /pasteles              - Guardar pastel
// GET    /pasteles/1            - Ver pastel ID 1
// GET    /pasteles/1/editar     - Editar pastel ID 1
// PUT    /pasteles/1            - Actualizar pastel ID 1
// DELETE /pasteles/1            - Eliminar pastel ID 1
// GET    /pasteles/sabor/chocolate - Pasteles de chocolate
// GET    /pasteles/disponibles  - Pasteles disponibles

// Desde API:
// GET    /api/pasteles          - Listar pasteles (JSON)
// GET    /api/pasteles/1        - Ver pastel ID 1 (JSON)
// POST   /api/pasteles          - Crear pastel (JSON)

Comandos Útiles

bash
# Ver todas las rutas registradas
php artisan route:list

# Ver rutas con detalles
php artisan route:list -v

# Ver rutas en formato JSON
php artisan route:list --json

# Filtrar rutas por nombre
php artisan route:list --name=users

# Filtrar rutas por método
php artisan route:list --method=POST

# Filtrar rutas por path
php artisan route:list --path=admin

# Filtrar rutas por dominio
php artisan route:list --domain=admin

# Limpiar caché de rutas
php artisan route:clear

# Generar caché de rutas (producción)
php artisan route:cache

Resumen

ConceptoDescripciónEjemplo
Ruta básicaDefine una URL y su respuestaRoute::get('/ruta', fn() => 'Hola')
ParámetrosCaptura valores de la URLRoute::get('/{id}', fn($id) => $id)
Parámetros opcionalesParámetros con valor por defectoRoute::get('/{id?}', fn($id=1) => $id)
Expresiones regularesValida el formato de parámetros->where('id', '[0-9]+')
Nombres de rutasAsigna nombre para referencia->name('ruta.nombre')
Grupos de rutasAgrupa rutas con prefijo/middlewareRoute::prefix('admin')->group(...)
ResourceGenera rutas CRUD automáticamenteRoute::resource('pasteles', Controller::class)
RedireccionesRedirige de una URL a otraRoute::redirect('/vieja', '/nueva')
Vistas directasDevuelve una vista sin controladorRoute::view('/ruta', 'vista')

¡Con este conocimiento, puedes crear un sistema de enrutamiento robusto y bien organizado en Laravel 12

Comentarios

Entradas más populares de este blog

Tutorial CRUD con Laravel 12-Pastel::factory(50)->create();

CRUD Completo con Laravel 12 - Guía Práctica Paso a Paso-productos

CRUD en Laravel 12 con Git - Guía para Principiantes