Laravel

Cómo funciona el patrón MVC en Laravel con un ejemplo práctico

2026-09-09 · Fran Paredes · 15 min

Cómo funciona el patrón MVC en Laravel con un ejemplo práctico

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.