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?
- Organización: Mantiene tu código ordenado y fácil de encontrar
- Reusabilidad: Puedes reutilizar métodos en diferentes rutas
- Testabilidad: Es más fácil testear clases que closures
- Mantenibilidad: 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',
]);
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.
Recursos Adicionales
Video de la lección
Acá te dejo la playlist en youtube donde iré agregando los videos de la serie: Aprende Laravel @ YouTube
