Cuando empiezas a trabajar con Laravel, una de las primeras ideas que conviene comprender bien es el patrón MVC (Model-View-Controller). No se trata simplemente de aprender dónde colocar cada archivo, sino de entender cómo viaja una petición por la aplicación y qué responsabilidad tiene cada parte del código.
MVC permite separar la lógica de una aplicación en diferentes capas. Gracias a ello, el proyecto resulta más fácil de mantener, ampliar, probar y entender.
En este artículo vamos a descubrir cómo funciona MVC en Laravel y construiremos un pequeño ejemplo práctico: una página que obtiene libros almacenados en una base de datos y los muestra al usuario.
¿Qué es el patrón MVC?
MVC significa:
- Model: Modelo.
- View: Vista.
- Controller: Controlador.
La idea fundamental consiste en separar los datos, la lógica de la aplicación y la interfaz que ve el usuario.
De forma simplificada, podemos imaginar el flujo de una aplicación Laravel así:
Usuario
↓
Ruta
↓
Controlador
↓
Modelo
↓
Base de datos
↓
Modelo
↓
Controlador
↓
Vista
↓
Usuario
Aunque Laravel internamente realiza muchas más operaciones, este esquema es perfecto para comenzar a entender cómo se organiza una aplicación basada en MVC.
El Modelo: los datos de nuestra aplicación
El Modelo representa los datos con los que trabaja la aplicación.
Laravel utiliza Eloquent ORM, su sistema de mapeo objeto-relacional, que permite interactuar con una base de datos utilizando objetos y métodos de PHP en lugar de escribir constantemente consultas SQL.
Supongamos que tenemos una tabla llamada books.
Podríamos crear el modelo utilizando Artisan:
php artisan make:model Book
Laravel generará:
app/Models/Book.php
Nuestro modelo podría ser:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
protected $fillable = [
'title',
'author',
'year',
];
}
Ahora Book representa los libros almacenados en nuestra base de datos.
Por ejemplo:
$books = Book::all();
Esta sencilla instrucción obtiene todos los registros correspondientes al modelo.
Laravel realizará internamente la consulta necesaria a la base de datos y nos devolverá una colección de objetos.
El modelo, por tanto, se ocupa de representar y gestionar los datos de nuestra aplicación.
La Vista: lo que finalmente ve el usuario
La Vista (View) contiene la presentación de la información.
En Laravel normalmente utilizamos el motor de plantillas Blade.
Las vistas se encuentran en:
resources/views/
Para nuestro ejemplo podríamos crear:
resources/views/books/index.blade.php
Con este contenido:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mis libros</title>
</head>
<body>
<h1>Mis libros</h1>
@foreach ($books as $book)
<article>
<h2>{{ $book->title }}</h2>
<p>
Autor: {{ $book->author }}
</p>
<p>
Año: {{ $book->year }}
</p>
</article>
@endforeach
</body>
</html>
Aquí aparece una característica fundamental de MVC.
La vista no necesita saber cómo se han obtenido los libros.
Simplemente recibe una variable llamada:
$books
y se encarga de mostrarla.
Esta separación es importante porque evita mezclar consultas a la base de datos, lógica PHP y código HTML en un mismo archivo.
El Controlador: el intermediario entre Modelo y Vista
El Controlador (Controller) recibe una petición y decide qué debe hacer la aplicación.
Podemos crear uno mediante Artisan:
php artisan make:controller BookController
Laravel creará:
app/Http/Controllers/BookController.php
Ahora podemos escribir:
<?php
namespace App\Http\Controllers;
use App\Models\Book;
class BookController extends Controller
{
public function index()
{
$books = Book::all();
return view('books.index', [
'books' => $books
]);
}
}
Aquí están ocurriendo dos cosas importantes.
Primero obtenemos los libros:
$books = Book::all();
El controlador utiliza el modelo Book para solicitar los datos.
Después enviamos esos datos a la vista:
return view('books.index', [
'books' => $books
]);
El controlador actúa así como intermediario entre los datos y la interfaz.
¿Dónde entran las rutas en MVC?
Aquí aparece una pieza importante de Laravel que, aunque no forma parte de las tres letras MVC, resulta imprescindible para comprender el flujo completo: las rutas.
Las rutas web se encuentran normalmente en:
routes/web.php
Podríamos definir nuestra ruta de libros así:
use App\Http\Controllers\BookController;
use Illuminate\Support\Facades\Route;
Route::get('/books', [BookController::class, 'index']);
Esto significa:
GET /books
↓
BookController
↓
index()
Cuando alguien visite:
https://misitio.com/books
Laravel ejecutará:
BookController::index()
Y comenzará el proceso que acabamos de construir.
Ejemplo práctico completo de MVC en Laravel
Ahora podemos unir todas las piezas.
Supongamos que el usuario escribe en su navegador:
https://misitio.com/books
El proceso sería el siguiente:
NAVEGADOR
│
│ GET /books
↓
ROUTER
routes/web.php
│
↓
BookController@index
│
↓
Book::all()
│
↓
BASE DE DATOS
│
↓
Colección de libros
│
↓
BookController
│
↓
books/index.blade.php
│
↓
HTML
│
↓
NAVEGADOR
Vamos a analizarlo paso a paso.
1. El usuario realiza una petición
El navegador solicita:
GET /books
2. Laravel busca una ruta coincidente
Laravel encuentra:
Route::get('/books', [BookController::class, 'index']);
Por tanto, sabe que debe ejecutar el método index() de BookController.
3. El controlador recibe la petición
Laravel ejecuta:
public function index()
{
$books = Book::all();
return view('books.index', [
'books' => $books
]);
}
4. El controlador utiliza el modelo
Esta línea:
$books = Book::all();
solicita al modelo todos los libros.
El modelo utiliza Eloquent para comunicarse con la base de datos.
5. La base de datos devuelve los registros
Imaginemos que tenemos:
ID | TITLE | AUTHOR | YEAR
---------------------------------------------------------
1 | Clean Code | Robert C. Martin | 2008
2 | The Pragmatic Programmer | Andrew Hunt | 1999
3 | Refactoring | Martin Fowler | 1999
Eloquent transforma estos registros en objetos que podemos utilizar desde PHP.
6. El controlador envía los datos a la vista
Después ejecutamos:
return view('books.index', [
'books' => $books
]);
Laravel busca:
resources/views/books/index.blade.php
y pone la variable $books a disposición de esa plantilla.
7. Blade genera el HTML
Nuestra vista recorre los libros:
@foreach ($books as $book)
<h2>{{ $book->title }}</h2>
<p>
{{ $book->author }}
</p>
@endforeach
Finalmente Laravel genera HTML y lo devuelve al navegador.
¿Por qué MVC es mejor que colocar todo en un mismo archivo?
Podríamos crear una aplicación PHP mezclando en un único archivo:
SQL
PHP
HTML
validaciones
lógica
formularios
En proyectos pequeños puede parecer cómodo.
El problema aparece cuando la aplicación empieza a crecer.
Imaginemos un sistema con:
- usuarios;
- libros;
- categorías;
- préstamos;
- documentos;
- permisos;
- buscadores;
- estadísticas;
- panel de administración.
Si toda la lógica estuviera mezclada, mantener el proyecto sería extremadamente complicado.
MVC permite establecer responsabilidades claras.
Modelo
↓
Gestiona los datos
Controlador
↓
Gestiona el flujo y coordina acciones
Vista
↓
Presenta la información
Esta separación hace que cada parte del proyecto tenga una función determinada.
Un ejemplo para entender MVC fuera de la programación
Podemos comparar MVC con un restaurante.
El cliente sería el usuario.
La Vista sería el comedor y el plato que finalmente recibe.
El Controlador podría compararse con el camarero, que recibe la petición del cliente y coordina lo necesario.
El Modelo estaría relacionado con la cocina y los ingredientes disponibles.
El cliente no entra directamente en la cocina.
Hace una petición:
Quiero este plato
El camarero transmite la solicitud, la cocina trabaja con los datos y recursos necesarios y finalmente el resultado vuelve al cliente.
No es una equivalencia técnica perfecta, pero ayuda a comprender la filosofía de separación de responsabilidades que existe detrás de MVC.
MVC no significa que toda la lógica deba estar en el controlador
Este es uno de los errores más habituales cuando empezamos con Laravel.
Podemos pensar:
Si el modelo gestiona los datos, la vista muestra información y el controlador controla la aplicación, entonces toda la lógica debe ir en el controlador.
No necesariamente.
En aplicaciones grandes podemos encontrar controladores demasiado extensos con cientos de líneas de código.
Por ejemplo:
public function store(Request $request)
{
// validar
// calcular
// comprobar permisos
// guardar
// enviar email
// generar documento
// registrar actividad
// actualizar estadísticas
// etc.
}
Laravel permite organizar estas responsabilidades utilizando otras piezas como:
- Form Requests para validaciones.
- Services para lógica de negocio.
- Policies para autorización.
- Jobs para procesos en segundo plano.
- Events y Listeners para eventos de la aplicación.
- Resources para transformar respuestas de APIs.
Por tanto, MVC constituye la estructura fundamental, pero una aplicación Laravel profesional puede incorporar muchas otras capas.
El controlador debería mantenerse relativamente sencillo
Una buena práctica consiste en intentar que el controlador explique claramente qué está ocurriendo.
Por ejemplo:
public function index()
{
$books = Book::latest()->get();
return view('books.index', compact('books'));
}
Podemos leerlo prácticamente como una frase:
Obtén los libros más recientes
y muestra la vista con esos libros.
Eso hace que el código sea mucho más fácil de comprender.
¿Qué ocurre si queremos mostrar un solo libro?
Podemos ampliar nuestro ejemplo.
Añadimos una nueva ruta:
Route::get('/books/{book}', [BookController::class, 'show']);
Y en nuestro controlador:
public function show(Book $book)
{
return view('books.show', compact('book'));
}
Laravel puede utilizar Route Model Binding para obtener automáticamente el libro correspondiente.
Si visitamos:
/books/5
Laravel puede resolver el registro cuyo identificador sea 5 y proporcionarlo directamente al controlador.
Después podemos crear:
resources/views/books/show.blade.php
con:
<h1>{{ $book->title }}</h1>
<p>
Autor: {{ $book->author }}
</p>
<p>
Año: {{ $book->year }}
</p>
El flujo continúa siendo exactamente el mismo:
Ruta
↓
Controlador
↓
Modelo
↓
Controlador
↓
Vista
La estructura MVC dentro de un proyecto Laravel
Si observamos nuestro proyecto, podemos identificar fácilmente las diferentes piezas:
app/
├── Http/
│ └── Controllers/
│ └── BookController.php
│
└── Models/
└── Book.php
resources/
└── views/
└── books/
├── index.blade.php
└── show.blade.php
routes/
└── web.php
Esta organización permite localizar rápidamente cada responsabilidad.
Si tenemos un problema obteniendo datos, probablemente tendremos que revisar el modelo o la consulta.
Si tenemos un problema con el flujo de una petición, revisaremos rutas y controlador.
Si tenemos un problema con la presentación, probablemente estará en la vista.
Esta separación resulta especialmente útil cuando trabajamos en proyectos grandes o con varios desarrolladores.
Cómo pensar en MVC cuando desarrollamos con Laravel
Una forma sencilla de trabajar consiste en hacerse cuatro preguntas.
¿Qué URL debe visitar el usuario?
Eso nos lleva a la ruta.
¿Qué debe ocurrir cuando visite esa URL?
Eso nos lleva al controlador.
¿Qué datos necesitamos?
Eso nos lleva al modelo.
¿Cómo debemos presentar esos datos?
Eso nos lleva a la vista.
Por ejemplo:
Quiero mostrar todos los libros
Pensamos:
URL
/books
↓
Ruta
Route::get('/books', ...)
↓
Controlador
BookController@index
↓
Datos
Book::all()
↓
Vista
books.index
Esta forma de razonar resulta mucho más útil que intentar memorizar archivos, comandos o fragmentos de código.
Conclusión: entender MVC es entender gran parte de Laravel
El patrón MVC en Laravel permite dividir una aplicación en responsabilidades claramente diferenciadas: el Modelo gestiona los datos, la Vista presenta la información y el Controlador coordina las acciones necesarias para responder a una petición.
Laravel añade además elementos fundamentales como el sistema de rutas, Eloquent, Blade, middleware, validaciones, servicios, eventos y muchas otras herramientas.
Pero el flujo básico continúa siendo fácil de visualizar:
Petición
↓
Ruta
↓
Controlador
↓
Modelo
↓
Base de datos
↓
Controlador
↓
Vista
↓
Respuesta
Comprender este recorrido es mucho más importante que memorizar código. Cuando entendemos qué ocurre desde que el usuario solicita una URL hasta que Laravel devuelve una página, empezamos realmente a comprender cómo funciona el framework.
Y esa es precisamente una de las grandes ventajas de MVC: podemos enfrentarnos a aplicaciones cada vez más complejas manteniendo una estructura lógica, organizada y escalable.