Curso de Laravel

Controladores en Laravel: organizar la lógica de tu aplicación

Por Víctor Peña · Publicado el

Hola, ¿cómo están? Continuando con el curso de Laravel, hoy veremos los controladores: el punto donde llega la petición y se decide qué hacer con ella.

¡Empecemos!

Qué es un controlador

Es la C del patrón MVC que vimos en la primera lección. Su trabajo es recibir la petición, coordinar y devolver una respuesta.

Podrías escribir toda la lógica dentro de las rutas, pero con quince pantallas el archivo web.php se vuelve ilegible. El controlador agrupa por entidad: todo lo relacionado con pacientes vive en PacienteController.

Crear un controlador

php artisan make:controller PacienteController

Se crea en app/Http/Controllers/. Y con la bandera de recurso, que es lo que vas a usar casi siempre:

php artisan make:controller PacienteController --resource --model=Paciente

Eso genera los siete métodos del CRUD, ya con el modelo inyectado.

El controlador de recurso

<?php

namespace App\Http\Controllers;

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

class PacienteController extends Controller
{
    public function index()
    {
        $pacientes = Paciente::orderBy('apellido')->paginate(15);

        return view('pacientes.index', compact('pacientes'));
    }

    public function create()
    {
        return view('pacientes.create');
    }

    public function store(Request $request)
    {
        $datos = $request->validate([
            'cedula' => 'required|unique:pacientes|max:20',
            'nombre' => 'required|max:100',
            'apellido' => 'required|max:100',
            'fecha_nacimiento' => 'required|date|before:today',
        ]);

        Paciente::create($datos);

        return redirect()
            ->route('pacientes.index')
            ->with('exito', 'Paciente registrado correctamente');
    }

    public function show(Paciente $paciente)
    {
        return view('pacientes.show', compact('paciente'));
    }

    public function edit(Paciente $paciente)
    {
        return view('pacientes.edit', compact('paciente'));
    }

    public function update(Request $request, Paciente $paciente)
    {
        $datos = $request->validate([
            'nombre' => 'required|max:100',
            'apellido' => 'required|max:100',
        ]);

        $paciente->update($datos);

        return redirect()
            ->route('pacientes.show', $paciente)
            ->with('exito', 'Datos actualizados');
    }

    public function destroy(Paciente $paciente)
    {
        $paciente->delete();

        return redirect()
            ->route('pacientes.index')
            ->with('exito', 'Paciente eliminado');
    }
}

Cada método corresponde a una de las rutas que genera Route::resource, y esa correspondencia es lo que hace que cualquier desarrollador de Laravel entienda tu proyecto sin explicaciones.

Qué hace cada método

Método Qué muestra o hace
index La lista de registros
create El formulario de creación
store Guarda el registro nuevo
show El detalle de uno
edit El formulario de edición
update Guarda los cambios
destroy Elimina

Fíjate en el patrón: los métodos que muestran algo devuelven una vista; los que modifican datos, una redirección.

Pasar datos a la vista

Tres formas, todas equivalentes:

return view('pacientes.index', compact('pacientes'));
return view('pacientes.index', ['pacientes' => $pacientes]);
return view('pacientes.index')->with('pacientes', $pacientes);

Y con varias variables:

return view('citas.create', compact('pacientes', 'doctores'));

compact() es la forma más habitual, y con dos o tres variables se lee bien. Con siete conviene el arreglo explícito.

La vinculación de modelos

Fíjate en los métodos show, edit, update y destroy: reciben directamente un Paciente, no un id.

public function show(Paciente $paciente)

Como vimos en la lección de rutas, Laravel busca el registro y lo inyecta ya resuelto. Si no existe, devuelve un 404 sin que tú hagas nada.

Eso elimina esta línea de todos tus métodos:

$paciente = Paciente::findOrFail($id);   // ya no hace falta

Inyección de dependencias

El controlador puede recibir cualquier clase que la aplicación sepa construir:

use App\Services\GeneradorDeReportes;

public function reporte(Request $request, GeneradorDeReportes $generador)
{
    return $generador->mensual($request->mes);
}

Laravel se encarga de crear la instancia. Es el mecanismo que vimos en la lección de interfaces del curso de PHP, y lo desarrollamos en la lección de inyección de dependencias del módulo avanzado.

Tipos de respuesta

return view('pacientes.index', compact('pacientes'));

return redirect()->route('pacientes.index');
return redirect()->back();
return redirect()->back()->withInput();

return response()->json($pacientes);
return response()->json(['error' => 'No encontrado'], 404);

return response()->download($rutaArchivo);
return response()->file($rutaArchivo);

abort(404);
abort(403, 'No tienes permiso');

redirect()->back()->withInput() es muy útil: vuelve al formulario conservando lo que el usuario había escrito. Sin withInput(), el formulario aparece vacío y la gente se molesta con razón.

Mensajes entre peticiones

return redirect()
    ->route('pacientes.index')
    ->with('exito', 'Paciente registrado correctamente');

Ese with() guarda el mensaje para la siguiente petición y lo descarta después. En la vista:

@if (session('exito'))
    <div class="alerta">{{ session('exito') }}</div>
@endif

Es lo que permite mostrar la confirmación después de una redirección, en el patrón POST-Redirect-GET que vimos en el curso de PHP.

Controladores de acción única

Cuando un controlador hace una sola cosa:

php artisan make:controller GenerarReporteController --invokable
class GenerarReporteController extends Controller
{
    public function __invoke(Request $request)
    {
        // ...
    }
}

Y en la ruta, sin nombre de método:

Route::get('/reporte', GenerarReporteController::class);

Son útiles para acciones que no encajan en el CRUD: exportar, aprobar, enviar.

Middleware desde el controlador

use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;

class PacienteController extends Controller implements HasMiddleware
{
    public static function middleware(): array
    {
        return [
            'auth',
            new Middleware('verified', only: ['destroy']),
        ];
    }
}

Es la forma actual, con un método estático. Si ves tutoriales con $this->middleware() en el constructor, corresponden a versiones anteriores.

Mantener los controladores delgados

Aquí está el consejo más importante de la lección, y el que más se ignora.

El controlador coordina; no debe contener la lógica del negocio. Un método que hace ochenta líneas de cálculos es una señal de que algo pertenece a otro sitio.

// Mal: toda la lógica en el controlador
public function store(Request $request)
{
    $datos = $request->validate([...]);

    $subtotal = 0;
    foreach ($request->items as $item) {
        $producto = Producto::find($item['id']);
        $subtotal += $producto->precio * $item['cantidad'];
        // ... 40 líneas más
    }

    // ...
}
// Bien: el controlador coordina
public function store(GuardarPedidoRequest $request, ServicioDePedidos $servicio)
{
    $pedido = $servicio->crear($request->validated());

    return redirect()
        ->route('pedidos.show', $pedido)
        ->with('exito', 'Pedido creado');
}

Las tres cosas que sacar del controlador:

La validación, a un Form Request. Lo vemos en la lección de validación de formularios.

La lógica del negocio, a una clase de servicio o al propio modelo.

Las consultas complejas, a scopes del modelo, como vimos en la lección de consultas.

Con eso, un método de controlador rara vez pasa de diez líneas.

Errores comunes

  • Controladores con métodos enormes.
  • Validar a mano con if en lugar de validate().
  • Usar find() y comprobar null teniendo la vinculación de modelos.
  • Devolver una vista tras un POST en lugar de redirigir, y provocar el reenvío del formulario al recargar.
  • Olvidar withInput() al volver por error de validación.
  • Consultas dentro de bucles, provocando el problema N+1.

Para cerrar

El controlador es el director de orquesta: recibe, pide, y entrega. No toca los instrumentos.

Si te llevas una regla: si tu método de controlador pasa de veinte líneas, algo de ahí pertenece a otro sitio.

En la siguiente lección veremos Blade, el sistema de plantillas donde se construye lo que el usuario ve.

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