En los posts anteriores hemos visto cómo crear rutas y vistas. Ahora es momento de organizar la lógica de nuestra aplicación usando Controllers.
¿Qué es un Controller?
Un Controller es una clase PHP que agrupa la lógica relacionada con las solicitudes HTTP. En lugar de definir toda la lógica en las rutas (usando closures), puedes organizarla en clases Controller.
¿Por qué usar Controllers?
- Mantiene tu código ordenado y fácil de encontrar
- Podés reutilizar métodos en diferentes rutas
- Es más fácil testear clases que closures
- Código más limpio y profesional
Controllers vs Route Closures
Cuando defines una ruta simple, puedes usar un closure:
Route::get('/posts', function () {
$posts = Post::all();
return view('posts.index', compact('posts'));
});
Pero a medida que tu aplicación crece, es mejor usar Controllers:
Route::get('/posts', [PostController::class, 'index']);
Regla simple: Si tu lógica tiene más de 2-3 líneas, usa un Controller.
Creando tu Primer Controller
Laravel incluye el comando Artisan make:controller para crear controllers rápidamente:
php artisan make:controller PostController
Esto creará un archivo app/Http/Controllers/PostController.php:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PostController extends Controller
{
// Tus métodos van aquí
}
Anatomía de un Controller
Un Controller típico tiene varios actions (métodos públicos) que manejan diferentes solicitudes:
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function index()
{
$posts = Post::all();
return view('posts.index', compact('posts'));
}
public function show(Post $post)
{
return view('posts.show', compact('post'));
}
}
Convenciones de naming:
- Controller names:
PostController,UserController(singular o plural, tú decides) - Actions:
index,create,store,show,edit,update,destroy
Resource Controllers
Laravel facilita la creación de controllers CRUD (Create, Read, Update, Delete) con el flag --resource:
php artisan make:controller PostController --resource
Esto genera un controller con 7 métodos estándar:
class PostController extends Controller
{
public function index() // GET /posts
public function create() // GET /posts/create
public function store() // POST /posts
public function show($id) // GET /posts/{id}
public function edit($id) // GET /posts/{id}/edit
public function update($id) // PUT/PATCH /posts/{id}
public function destroy($id)// DELETE /posts/{id}
}
Para conectar todas estas rutas automáticamente:
Route::resource('posts', PostController::class);
Una línea = 7 rutas! Route::resource() es perfecta para operaciones CRUD estándar.
Controller Actions en Detalle
Veamos cada action del resource controller:
1. index - Lista todos los recursos:
public function index()
{
$posts = Post::latest()->paginate(10);
return view('posts.index', compact('posts'));
}
2. create - Muestra el formulario de creación:
public function create()
{
return view('posts.create');
}
3. store - Guarda el nuevo recurso:
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|max:255',
'content' => 'required',
]);
Post::create($validated);
return redirect()->route('posts.index')
->with('success', 'Post creado exitosamente!');
}
4. show - Muestra un recurso específico:
public function show(Post $post)
{
return view('posts.show', compact('post'));
}
5. edit - Muestra el formulario de edición:
public function edit(Post $post)
{
return view('posts.edit', compact('post'));
}
6. update - Actualiza el recurso:
public function update(Request $request, Post $post)
{
$validated = $request->validate([
'title' => 'required|max:255',
'content' => 'required',
]);
$post->update($validated);
return redirect()->route('posts.show', $post)
->with('success', 'Post actualizado!');
}
7. destroy - Elimina el recurso:
public function destroy(Post $post)
{
$post->delete();
return redirect()->route('posts.index')
->with('success', 'Post eliminado!');
}
Dependency Injection en Controllers
Laravel automáticamente inyecta dependencias en los métodos del controller. Por ejemplo, el objeto Request:
public function store(Request $request)
{
// Laravel automáticamente inyecta Request
$title = $request->input('title');
// ...
}
También funciona con Models (Route Model Binding):
public function show(Post $post)
{
// Laravel automáticamente busca el Post por ID
return view('posts.show', compact('post'));
}
Single Action Controllers
Si un controller solo tiene UNA acción, puedes usar un Invokable Controller:
php artisan make:controller ShowDashboard --invokable
Esto crea un controller con un método mágico __invoke:
class ShowDashboard extends Controller
{
public function __invoke()
{
$stats = // ... obtener estadísticas
return view('dashboard', compact('stats'));
}
}
Y lo usas en rutas así:
Route::get('/dashboard', ShowDashboard::class);
Cuándo usar Invokable Controllers: Para acciones únicas que no encajan en un CRUD estándar (ej: exportar PDF, procesar webhook, generar reporte).
Evita “Fat Controllers”
Un error común es poner TODA la lógica de negocio en los controllers:
// ❌ MAL - Controller muy pesado
public function store(Request $request)
{
$validated = $request->validate([...]);
// Mucha lógica de negocio aquí
$user = User::create($validated);
Mail::to($user)->send(new WelcomeEmail());
$user->assignRole('subscriber');
event(new UserRegistered($user));
// ... 50 líneas más
return redirect()->route('home');
}
Mejor práctica: Delega lógica compleja a Services o Actions:
// ✅ MEJOR - Controller delgado
public function store(Request $request, RegisterUserService $service)
{
$validated = $request->validate([...]);
$user = $service->register($validated);
return redirect()->route('home')
->with('success', 'Bienvenido!');
}
El controller solo debe:
- Validar input
- Llamar a services/models
- Retornar una respuesta (view, redirect, json)
Personalizando Resource Routes
Puedes limitar qué rutas generar con only o except:
// Solo index y show
Route::resource('posts', PostController::class)
->only(['index', 'show']);
// Todas excepto delete
Route::resource('posts', PostController::class)
->except(['destroy']);
También puedes cambiar los nombres de las rutas:
Route::resource('posts', PostController::class)
->names([
'index' => 'posts.all',
'show' => 'posts.detail',
]);
PHP Attributes en Laravel 13
Laravel 13 introduce soporte nativo para PHP Attributes en controllers. En lugar de registrar middleware en el constructor, puedes declararlo directamente sobre la clase o el método:
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;
#[Middleware('auth')]
class PostController extends Controller
{
#[Middleware('subscribed')]
public function create()
{
return view('posts.create');
}
}
También puedes usar #[Authorize] para políticas:
use Illuminate\Auth\Access\AuthorizesRequests;
#[Middleware('auth')]
class PostController extends Controller
{
use AuthorizesRequests;
#[Authorize('update', Post::class)]
public function edit(Post $post)
{
return view('posts.edit', compact('post'));
}
}
Ambas formas son válidas en Laravel 13. Los attributes son opcionales y no reemplazan la forma tradicional con $this->middleware().
Siguiente Paso
Ahora que sabes organizar tu lógica con Controllers, en el próximo post veremos cómo trabajar con la base de datos usando Models y Eloquent ORM. Aprenderás a crear, leer, actualizar y eliminar datos de forma elegante.
¿Necesitas ayuda con tu proyecto Laravel? Ofrezco servicios de desarrollo Laravel con arquitectura SOLID, APIs RESTful escalables y código mantenible a largo plazo.
Preguntas Frecuentes
¿Por qué usar Controllers en lugar de closures en rutas?
Los Controllers organizan mejor el código (separan lógica de rutas), son más reutilizables (un método puede usarse en múltiples rutas), facilitan el testing (testear clases es más fácil que closures), y mejoran la mantenibilidad del proyecto a largo plazo.
¿Cómo creo un Controller en Laravel?
Usa el comando Artisan php artisan make:controller PostController. Para crear un resource controller con los 7 métodos CRUD automáticos, usa php artisan make:controller PostController --resource.
¿Qué son los Resource Controllers?
Son controllers con 7 métodos estándar para operaciones CRUD: index (lista), create (formulario crear), store (guardar), show (ver uno), edit (formulario editar), update (actualizar), destroy (eliminar). Una línea Route::resource('posts', PostController::class) genera las 7 rutas automáticamente.
¿Qué es Dependency Injection en Controllers?
Laravel automáticamente inyecta dependencias en los métodos del controller. Por ejemplo, public function store(Request $request) - Laravel inyecta el objeto Request automáticamente. También funciona con Models vía Route Model Binding.
¿Qué es un Invokable Controller?
Es un controller con un ÚNICO método __invoke() para una sola acción. Perfecto para acciones que no encajan en CRUD estándar (exportar PDF, procesar webhook, generar reporte). Se crea con php artisan make:controller ShowDashboard --invokable y se usa como Route::get('/dashboard', ShowDashboard::class).
Recursos Adicionales
Video de la lección
Ver video tutorial: Aprende Laravel - Controllers
Playlist completa en YouTube: Aprende Laravel @ YouTube
