Módulo 4 · Lección 2 20 min

Resource Controllers

Domina los Resource Controllers de Laravel para crear CRUD completos con un solo comando y rutas automáticas para tus modelos.

¿Qué es un Resource Controller?

En la lección anterior creamos controladores y métodos manualmente. Pero Laravel tiene un patrón muy común para aplicaciones web: el CRUD (Create, Read, Update, Delete). Casi todas las secciones de una app web siguen este patrón: crear artículos, editarlos, verlos y eliminarlos.

Un Resource Controller es un controlador que ya viene con los 7 métodos estándar para gestionar un recurso, siguiendo las convenciones REST de Laravel. Y con una sola línea en las rutas, registras las 7 URLs necesarias de forma automática.

Crear un Resource Controller

php artisan make:controller ProductoController --resource

Esto genera app/Http/Controllers/ProductoController.php con esta estructura:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ProductoController extends Controller
{
    public function index()
    {
        //
    }

    public function create()
    {
        //
    }

    public function store(Request $request)
    {
        //
    }

    public function show(string $id)
    {
        //
    }

    public function edit(string $id)
    {
        //
    }

    public function update(Request $request, string $id)
    {
        //
    }

    public function destroy(string $id)
    {
        //
    }
}

Los 7 Métodos y su Significado

Cada método tiene un rol bien definido:

MétodoVerbo HTTPURLDescripción
indexGET/productosLista todos los productos
createGET/productos/createMuestra el formulario de creación
storePOST/productosGuarda un nuevo producto
showGET/productos/{id}Muestra un producto específico
editGET/productos/{id}/editMuestra el formulario de edición
updatePUT/PATCH/productos/{id}Actualiza un producto
destroyDELETE/productos/{id}Elimina un producto

Registrar las Rutas con Route::resource

En routes/web.php:

use App\Http\Controllers\ProductoController;

Route::resource('productos', ProductoController::class);

Esta sola línea registra las 7 rutas automáticamente. Puedes verificarlas ejecutando:

php artisan route:list

Verás algo como:

GET|HEAD   productos ..................... productos.index
GET|HEAD   productos/create ............. productos.create
POST       productos ..................... productos.store
GET|HEAD   productos/{producto} ......... productos.show
GET|HEAD   productos/{producto}/edit .... productos.edit
PUT|PATCH  productos/{producto} ......... productos.update
DELETE     productos/{producto} ......... productos.destroy

Implementar los Métodos con Modelos

Veamos una implementación real usando el modelo Producto. Primero asumimos que tienes el modelo (lo crearemos en módulos posteriores):

<?php

namespace App\Http\Controllers;

use App\Models\Producto;
use Illuminate\Http\Request;

class ProductoController extends Controller
{
    // GET /productos
    public function index()
    {
        $productos = Producto::all();
        return view('productos.index', compact('productos'));
    }

    // GET /productos/create
    public function create()
    {
        return view('productos.create');
    }

    // POST /productos
    public function store(Request $request)
    {
        $request->validate([
            'nombre' => 'required|string|max:255',
            'precio' => 'required|numeric|min:0',
        ]);

        Producto::create($request->only(['nombre', 'precio']));

        return redirect()->route('productos.index')
            ->with('success', 'Producto creado correctamente.');
    }

    // GET /productos/{producto}
    public function show(Producto $producto)
    {
        return view('productos.show', compact('producto'));
    }

    // GET /productos/{producto}/edit
    public function edit(Producto $producto)
    {
        return view('productos.edit', compact('producto'));
    }

    // PUT /productos/{producto}
    public function update(Request $request, Producto $producto)
    {
        $request->validate([
            'nombre' => 'required|string|max:255',
            'precio' => 'required|numeric|min:0',
        ]);

        $producto->update($request->only(['nombre', 'precio']));

        return redirect()->route('productos.index')
            ->with('success', 'Producto actualizado correctamente.');
    }

    // DELETE /productos/{producto}
    public function destroy(Producto $producto)
    {
        $producto->delete();

        return redirect()->route('productos.index')
            ->with('success', 'Producto eliminado correctamente.');
    }
}

Nota el uso de Route Model Binding: en lugar de recibir string $id y buscar el modelo manualmente, simplemente declaras Producto $producto y Laravel lo busca automáticamente en la base de datos. Si no existe, devuelve un error 404 automáticamente.

Rutas de Recursos Parciales

No siempre necesitas los 7 métodos. Si tu recurso solo necesita listar y mostrar (sin crear ni eliminar), puedes limitar las rutas:

// Solo estas rutas
Route::resource('productos', ProductoController::class)->only([
    'index', 'show'
]);

// Todas excepto estas
Route::resource('productos', ProductoController::class)->except([
    'create', 'edit'
]);

Recursos para APIs

Cuando construyes una API, los métodos create y edit no tienen sentido (no hay formularios HTML). Laravel tiene apiResource para esto:

Route::apiResource('productos', ProductoController::class);

Esto registra solo 5 rutas (omite create y edit). Y puedes crear el controlador ya preparado para API:

php artisan make:controller Api/ProductoController --api

Nombres de Rutas Automáticos

Con Route::resource, cada ruta obtiene automáticamente un nombre que puedes usar en tus vistas y controladores:

// En una vista Blade, generar la URL del listado:
<a href="{{ route('productos.index') }}">Ver todos los productos</a>

// Para mostrar un producto específico:
<a href="{{ route('productos.show', $producto->id) }}">Ver producto</a>

// Para el formulario de edición:
<a href="{{ route('productos.edit', $producto->id) }}">Editar</a>

// Redirigir después de guardar:
return redirect()->route('productos.index');

Usar nombres de ruta en lugar de URLs hardcodeadas es una buena práctica: si cambias la URL en un futuro, solo cambias las rutas y el resto del código sigue funcionando.

Combinar Resource Controller con Model

Puedes crear el modelo, la migración y el resource controller todo de una vez:

php artisan make:model Producto -mcr

Los flags significan:

  • -m: crea la migración de base de datos
  • -c: crea el controlador
  • -r: hace que el controlador sea de tipo resource

Esto te ahorra varios comandos y crea todos los archivos necesarios de golpe.

Formularios HTML y el Método Spoofing

Los formularios HTML solo soportan GET y POST. Para las peticiones PUT, PATCH y DELETE que necesitan los métodos update y destroy, Laravel usa method spoofing:

<!-- Formulario de edición -->
<form action="{{ route('productos.update', $producto->id) }}" method="POST">
    @csrf
    @method('PUT')
    
    <input type="text" name="nombre" value="{{ $producto->nombre }}">
    <input type="number" name="precio" value="{{ $producto->precio }}">
    
    <button type="submit">Actualizar</button>
</form>

<!-- Botón de eliminar -->
<form action="{{ route('productos.destroy', $producto->id) }}" method="POST">
    @csrf
    @method('DELETE')
    <button type="submit">Eliminar</button>
</form>

La directiva @method('PUT') añade un campo oculto que Laravel lee para saber que debe tratar la petición como PUT aunque el formulario use POST.

Personalizar el Nombre del Parámetro de Ruta

Por defecto, el parámetro en la URL se llama igual que el modelo en singular (ej: {producto}). Puedes personalizarlo:

Route::resource('productos', ProductoController::class)
    ->parameters(['productos' => 'item']);

Ahora la URL sería /productos/{item} y el método del controlador recibiría Producto $item.

Resumen

Los Resource Controllers son una de las características más productivas de Laravel. Con un solo comando Artisan y una sola línea de rutas, tienes un CRUD completo y bien organizado. Siguiendo las convenciones de nomenclatura, tu código será legible y predecible para cualquier desarrollador Laravel. En la siguiente lección exploraremos las rutas avanzadas: grupos, prefijos y nombres.

Quiz

Pon a prueba lo aprendido

Responde las preguntas para comprobar que has entendido los conceptos clave.

1. ¿Qué comando Artisan crea un Resource Controller?

2. ¿Cuántos métodos incluye un Resource Controller completo en Laravel?

3. ¿Cómo se registra una ruta de recurso completo en routes/web.php?