5.7 Controladores de Laravel
5.7.1 Introducción
Los controladores son el componente que conecta las rutas con los modelos y las vistas. Con este tema completaremos el ciclo de creación de una aplicación web en Laravel siguiendo el patrón MVC.
Importante
Un controlador recibe las solicitudes de los usuarios, consulta los modelos si necesita datos, y devuelve una respuesta adecuada (generalmente una vista).
5.7.2 Preparación del Entorno
5.7.2.1 Crear la Base de Datos
-
Crear una base de datos nueva llamada
cursoLaravelen tu gestor de base de datos (MySQL, SQLite, etc.). Si la tenemos creada del tema anterior podemos usar: -
Configurar la conexión en el archivo
.env: -
Modificar la migración
create_users_table.php, hemos añadido los camposage,addressyzipCode:
5.7.2.2 Ejecutar las Migraciones
Una vez hemos modificado la migración de users, ejecutamos el comando para crear la tabla:
Comprobar que se crean las tablas correctamente.
5.7.3 Creación del Primer Circuito MVC
Vamos a crear un circuito básico que nos permita ver cómo funcionan los controladores, las vistas y los modelos en Laravel. Para ello crearemos una aplicación (circuito completo) que muestre una lista de usuarios.
5.7.3.1 Crear un Controlador
Hasta ahora hemos trabajado con las vistas blade y los modelos. Pero para completar el ciclo MVC, necesitamos un controlador que conecte las vistas con los modelos. El controlador se encargará de recibir las peticiones del usuario, consultar los modelos y devolver la vista correspondiente.
Crear un nuevo controlador UserController, para ello vamos a usar el comando de Artisan. Los controladores los denominamos con el sufijo Controller y los colocamos en la carpeta app/Http/Controllers.
y obtenemos el siguiente resultado:
Esta clase será la base de nuestro controlador. Se encarga de gestionar las peticiones HTTP y devolver las respuestas adecuadas. En este caso, el controlador UserController se encargará de gestionar las peticiones relacionadas con los usuarios. En este momento no tiene ningún método, pero podemos añadir los que necesitemos.
5.7.3.2 Implementación Básica del Controlador
Crear un método index(), este método mostrará de momento un mensaje de depuración. Este método se encargará de gestionar la petición GET a la ruta / y devolverá una vista con el listado de usuarios.
5.7.3.3 Asociar la Ruta con el Controlador
En routes/web.php:
5.7.3.4 Comprobaciones Iniciales
Acceder a http://localhost:8080/ y comprobar que aparece el mensaje de depuración. Debe aparecer el mensaje Hola desde UserController@index, que creamos en la función index() del controlador UserController.
Si no aparece, comprobar que el servidor de desarrollo está en marcha y que la ruta está correctamente configurada.
5.7.3.5 Crear la Vista de Usuarios
Pero el objetivo no es mostrar un mensaje de depuración, sino una vista con el listado de usuarios. Para ello, vamos a crear una vista usuarios/index.blade.php que mostrará el listado de usuarios.
Crear un archivo /users/index.blade.php con contenido:
5.7.3.6 Probar el Circuito
A
Ahora hay que completar el controlador para que devuelva la vista que acabamos de crear. Para ello, en el método index() del controlador UserController, vamos a devolver la vista users/index.blade.php:
Acceder de nuevo a la URL y comprobar que se carga la vista.
5.7.4 Trabajar Desde el Controlador con el Modelo
El siguiente paso es recuperar los datos de la base de datos. Para ello, vamos a usar el modelo User que hemos creado anteriormente. Este modelo se encarga de gestionar la tabla users de la base de datos.
5.7.4.1 Recuperar Información con all()
Ahora vamos a modificar el método index() del controlador UserController para que recupere todos los usuarios de la base de datos y los pase a la vista.
Para ello primero tenemos que hacer referencia al modelo User en la parte superior del controlador:
Ahora podemos utilizar los métodos del modelo User para recuperar los datos. Como User extiende de Model, podemos usar el método all() para recuperar todos los usuarios de la base de datos. Este método devuelve una colección de usuarios que podemos pasar a la vista. Internemante está ejecutando una sentencia sql como la siguiente:
Más adelante nos pararemos a ver estos métodos estáticos que ofrecen los modelos de Eloquent.
El modelo quedará como sigue:
5.7.4.2 Pasar Datos a la Vista
Si analizamos la clase anterior, vemos que tenemos aún un problema. No estamos pasando los datos a la vista. Para ello, tenemos que modificar la línea que devuelve la vista para que pase la colección de usuarios a la vista.
De esta manera le pasamos un array asociativo a la vista, donde la clave usuarios contendrá la colección de usuarios que hemos recuperado de la base de datos. En Laravelcuando en un array asociativo la clave y el valor son iguales, podemos usar la sintaxis simplificada:
La función compact() crea un array asociativo con las variables que le pasamos como parámetros. En este caso, creará un array con la clave usuarios y el valor de la variable $usuarios. Si tuvieramos que pasar más variables, podríamos hacerlo de la siguiente manera:
Ahora el método index() del controlador quedará así:
5.7.4.3 Modificar la Vista para Mostrar Datos
Vamos a modificar la vista users/index.blade.php para que muestre el listado de usuarios. Para ello, vamos a usar la sintaxis de Blade para mostrar los datos.
La directiva @foreach nos permite recorrer la colección de usuarios y mostrar cada uno de ellos. La variable $loop es una variable especial que nos permite acceder a información sobre el bucle, como el índice actual ($loop->iteration) o si es el primer o último elemento del bucle. Otras opciones son:
$loop->first: Indica si es el primer elemento del bucle.$loop->last: Indica si es el último elemento del bucle.$loop->index: Indica el índice actual del bucle (empezando desde 0).$loop->remaining: Indica cuántos elementos quedan por recorrer.$loop->depth: Indica la profundidad del bucle (en caso de bucles anidados).
Vamos a probar ahora si estas modificaciones funcionan correctamente. Acceder a la URL http://localhost:8080/ y comprobar que se muestra el listado de usuarios. Podemos ver que sólo se muestra el título de la página, ya que no tenemos usuarios en la base de datos. Para poder mejorar esta situación vamos a añadir un mensaje que indique que no hay usuarios disponibles. Para ello utilizaremos la directiva @if de Blade:
Podemos ver que hemos añadido una comprobación para ver si la colección de usuarios está vacía. Si está vacía, mostramos un mensaje indicando que no hay usuarios disponibles. Si hay usuarios, mostramos el listado.
5.7.4.4 Uso de @switch
Blade también nos permite usar la directiva @switch para realizar comprobaciones múltiples. En este caso, vamos a comprobar la edad de cada usuario y mostrar un mensaje diferente según su edad.
En este caso, estamos comprobando la edad de cada usuario y mostrando un mensaje diferente según su edad. La directiva @switch nos permite realizar comprobaciones múltiples de una manera más legible que usando múltiples if.
5.7.5 Crear Datos de Prueba
Ahora que tenemos el circuito completo, vamos a crear algunos usuarios de prueba para poder ver cómo funciona la aplicación. Lo lógico sería tener un formulario para crear usuarios, pero para simplificar el proceso, vamos a crear un método en el controlador que inserte un usuario de prueba directamente en la base de datos. Para ello, vamos a crear un método create() en el controlador UserController que se encargará de crear un usuario de prueba. Este método se encargará de insertar un usuario en la base de datos y redirigir a la vista de listado de usuarios. Este método se llamará cuando accedamos a la ruta /create.
5.7.5.1 Crear Ruta y Método Create
Para crear los usuarios vamos a aprovechar y utilizar diferentes formas de insertar datos en la base de datos. Vamos a crear un usuario de prueba directamente en el controlador y otro usuario usando el método create() del modelo User. Para ello, vamos a crear un nuevo método create() en el controlador UserController que se encargará de crear los usuarios.
Para el password vamos a usar el método Hash::make() para encriptar la contraseña. Este método se encarga de encriptar la contraseña usando el algoritmo de encriptación que tengamos configurado en el archivo .env. Por defecto, Laravel usa el algoritmo bcrypt, que es un algoritmo seguro y recomendado para encriptar contraseñas. Para poder usar este método, tenemos que importar la clase Hash en la parte superior del controlador:
Por último vamos a utilizar la función redirect() para redirigir al usuario a la vista de listado de usuarios después de crear el usuario. Esta función se encarga de redirigir al usuario a la ruta que le indiquemos. En este caso, redirigimos a la ruta usuarios.index, que es la ruta que hemos creado anteriormente para mostrar el listado de usuarios. Así una vez creados los usuarios nos redirigiremos a la vista de listado de usuarios. Resaltar que en la redirección utilizamos el nombre de la ruta que hemos creado anteriormente, usuarios.index, en lugar de la URL completa. Esto es una buena práctica, ya que si cambiamos la URL de la ruta, no tendremos que modificar el código del controlador.
Importante
Acordarse de modificar el modelo y añadir a $fillable los campos que vamos a insertar. En este caso, los campos name, email, password, age, address y zipCode. De esta manera, Eloquent podrá insertar los datos en la base de datos sin problemas.
Una vez hemos modificado el controlador, tenemos que crear la ruta que se encargará de gestionar la petición a la URL /create. Esta ruta se encargará de llamar al método create() del controlador UserController y crear los usuarios. Para ello, vamos a crear una nueva ruta en el archivo routes/web.php que se encargará de gestionar la petición a la URL /create. En el archivo routes/web.php añadimos la siguiente línea:
5.7.5.2 Comprobaciones
Acceder a /create. Si todo va bien primero se crearán los usuarios y luego se nos redirigirá a la vista de listado de usuarios. Por tanto nos debe aparecer el listado de usuarios con los tres usuarios que hemos creado. Si no aparece, comprobar que el servidor de desarrollo está en marcha y que la ruta está correctamente configurada.
5.7.6 Consultas Avanzadas desde el Controlador
Ahora que tenemos los usuarios creados y hemos visto conseguido crear un circuito MVC completo. Vamos a ver de qué manera podemos realizar consultas más avanzadas desde el controlador. Para ello, vamos a ver cómo podemos filtrar los usuarios según diferentes criterios. Vamos a ver cómo podemos filtrar los usuarios por edad, código postal y otros criterios.
5.7.6.1 Uso de where()
El método where() nos permite filtrar los resultados de la consulta según diferentes criterios. Este método se encarga de añadir una cláusula WHERE a la consulta SQL que se genera. Por ejemplo, si queremos filtrar los usuarios por edad, podemos hacerlo de la siguiente manera:
El segundo parámetro es el operador de comparación que queremos usar. Podemos usar los siguientes operadores:
| Operador | Descripción |
|---|---|
= |
Igual |
!= |
Diferente |
> |
Mayor que |
>= |
Mayor o igual que |
< |
Menor que |
<= |
Menor o igual que |
LIKE |
Coincide con patrón |
NOT LIKE |
No coincide con patrón |
Un ejemplo de que podemos usar el operador LIKE para filtrar los usuarios por nombre, por ejemplo si queremos filtrar los usuarios que empiezan por M:
5.7.6.2 Encadenar where()
Encadenar where() nos permite añadir múltiples condiciones a la consulta. Sería como añadir múltiples cláusulas WHERE encadenadas por AND a la consulta SQL. Por ejemplo, si queremos filtrar los usuarios por edad y código postal, podemos hacerlo de la siguiente manera:
5.7.6.3 Otros métodos de Consulta
| Método | Descripción | Ejemplo |
|---|---|---|
whereBetween() |
Filtra los resultados según un rango de valores. | User::whereBetween('age', [18, 30])->get(); |
whereIn() |
Filtra los resultados según una lista de valores. | User::whereIn('zipCode', [28080, 28081])->get(); |
orWhere() |
Añade una cláusula OR a la consulta. |
User::orWhere('age', '<', 18)->get(); |
orderBy() |
Ordena los resultados según un campo. | User::orderBy('name')->get(); |
first() |
Devuelve el primer resultado de la consulta. | User::orderBy('name')->first(); |
find() |
Busca un registro por su ID. | User::find(1); |
findOrFail() |
Busca un registro por su ID y lanza una excepción si no lo encuentra. | User::findOrFail(1); |
5.7.7 Resumen de Todo lo Aprendido
- Creación del circuito MVC completo.
- Recuperación de datos con Eloquent.
- Buenas prácticas de organización.
Nota
Eloquent facilita mucho el trabajo, pero a veces no controlamos el SQL exacto que se genera.
Consejo
En un uso correcto de Eloquent, no es necesario preocuparse por el SQL generado. Eloquent se encarga de optimizar las consultas y generar el SQL adecuado. Sin embargo, es importante conocer cómo funciona Eloquent y cómo se generan las consultas para poder optimizar el rendimiento de la aplicación.
Para otros casos más complejos, podemos usar SQL puro o el constructor de consultas de Laravel. En este caso, Eloquent no es la mejor opción, tenemos otras opciones que analizaremos a continuación.
5.7.8 Acceso a Base de Datos Alternativo
5.7.8.1 Uso de SQL Puro con DB Raw
Laravel nos ofrece la clase DB para acceder a la base de datos de forma más directa. Esta clase nos permite ejecutar consultas SQL puras y obtener los resultados en forma de array. Para ello, podemos usar el método select() de la clase DB.
Si por ejemplo tenemos las tablas users y posts, podemos hacer una consulta SQL que una ambas tablas:
$usuarios = DB::select(DB::raw('SELECT users.*, posts.title FROM users INNER JOIN posts ON users.id = posts.user_id'));
Esto nos devolverá un array con los resultados de la consulta. Sin embargo, al usar SQL puro, no tenemos acceso a las funcionalidades de Eloquent, como la paginación o la relación entre modelos. Por lo tanto, es recomendable usar Eloquent siempre que sea posible.
5.7.8.2 Métodos estáticos de DB
Laravel también nos ofrece métodos estáticos para acceder a la base de datos de forma más sencilla. Estos métodos nos permiten realizar operaciones CRUD (Crear, Leer, Actualizar y Borrar) de forma más sencilla. Por ejemplo, para insertar un nuevo usuario en la tabla users, podemos usar el método insert() de la clase DB.
Primero tenemos el método ::table() que nos permite acceder a una tabla específica de la base de datos. Este método nos devuelve una instancia de la clase QueryBuilder, que nos permite realizar consultas a la base de datos de forma más sencilla. Por ejemplo, para acceder a la tabla users, podemos usar el siguiente código:
QueryBuilder para realizar consultas a la base de datos. Por ejemplo, para filtrar los usuarios por edad, podemos usar el método where():
| Método | Descripción | Ejemplo |
|---|---|---|
insert() |
Inserta un nuevo registro en la tabla. | DB::table('users')->insert(['name' => 'Pedro', 'email' => 'correo@example.com', 'password' => Hash::make('password')]); |
update() |
Actualiza un registro en la tabla. | DB::table('users')->where('id', 1)->update(['name' => 'Pedro Modificado']); |
delete() |
Elimina un registro de la tabla. | DB::table('users')->where('id', 1)->delete(); |
get() |
Recupera todos los registros de la tabla. | DB::table('users')->get(); |
find() |
Busca un registro por su ID. | DB::table('users')->find(1); |
first() |
Recupera el primer registro de la tabla. | DB::table('users')->first(); |
where() |
Filtra los resultados según una condición. | DB::table('users')->where('age', '>=', 18)->get(); |
whereIn() |
Filtra los resultados según una lista de valores. | DB::table('users')->whereIn('zipCode', [28080, 28081])->get(); |
orderBy() |
Ordena los resultados según un campo. | DB::table('users')->orderBy('name')->get(); |
count() |
Cuenta el número de registros de la tabla. | DB::table('users')->count(); |
sum() |
Suma un campo de la tabla. | DB::table('users')->sum('age'); |
avg() |
Calcula el promedio de un campo de la tabla. | DB::table('users')->avg('age'); |
max() |
Devuelve el valor máximo de un campo de la tabla. | DB::table('users')->max('age'); |
min() |
Devuelve el valor mínimo de un campo de la tabla. | DB::table('users')->min('age'); |
Otra opción es usar el método DB::table() para acceder a la tabla y luego usar los métodos de la clase QueryBuilder para realizar consultas a la base de datos. Por ejemplo, para acceder a la tabla users, podemos usar el siguiente código:
5.7.8.3 Comparativa
| Técnica | Ventajas | Inconvenientes |
|---|---|---|
| Eloquent | Código limpio y fácil de mantener. | Menos control sobre SQL generado. |
| DB Raw | Total control del SQL. | Mayor riesgo de errores o inyecciones. |
| DB Table | Punto intermedio: estructura clara, control aceptable. |
5.7.9 Otras Opciones ORM en PHP
Eloquent (Laravel)
- Estilo activo ("Active Record"): Cada modelo representa una tabla y cada instancia una fila.
- Simplicidad: Muy expresivo y fácil de aprender; integradísimo en Laravel.
- Consultas rápidas: Métodos como
where,orderBy,first,getson encadenables. - Relaciones: Define relaciones como
hasOne,hasMany,belongsTo, etc. - Migraciones y Seeders: Integración directa para manejar la estructura y datos de la base.
- Eventos del modelo: Hooks como
creating,updating,deleting. - Carga perezosa y ansiosa (
lazy/eager loading): Optimización de consultas relacionadas. - Limitaciones: No es lo más flexible para consultas muy complejas o estructuras fuera del estándar relacional.
Doctrine (Symfony y otros)
- Estilo de "Data Mapper": Los objetos son independientes de la base de datos.
- ORM altamente flexible: Perfecto para proyectos con modelos complejos o sistemas DDD (Domain-Driven Design).
- Unit of Work: Maneja cambios en objetos y sincroniza en lote a la base de datos.
- Consultas DQL (Doctrine Query Language): Un lenguaje similar a SQL pero orientado a objetos.
- Migraciones poderosas: Control detallado del esquema de la base de datos.
- Muy configurado por anotaciones, XML o YAML: Más configuración que Eloquent, pero más potencia.
- Más pesado de aprender: Pero excelente para proyectos grandes o altamente customizados.
Propel
- Estilo activo (Active Record): Como Eloquent, los objetos representan directamente filas.
- Generador de código: Genera las clases PHP a partir de un esquema de base de datos o XML.
- Consultas orientadas a objetos: Fluent interface para construir consultas.
- Soporte para múltiples bases de datos: Puede trabajar con más de un motor en el mismo proyecto.
- Migraciones: Sistema de migraciones incorporado.
- Algo menos popular hoy: Ha sido desplazado en popularidad por Eloquent y Doctrine en nuevos proyectos.
- Ideal para proyectos donde quieres definir tu modelo primero y generar el código después.
RedBeanPHP
- Cero configuración: No defines esquemas ni modelos manualmente.
- Auto-creación de tablas y columnas: A medida que insertas datos, RedBeanPHP crea lo necesario.
- Muy sencillo y rápido de usar: Perfecto para prototipos o pequeños proyectos.
- Modo congelado (
freeze) para producción: Para evitar cambios automáticos en el esquema. - ORM basado en Active Record: Pero muy dinámico (menos control sobre la estructura a mano).
- Limitaciones: No es ideal para bases de datos muy complejas o requerimientos de optimización avanzada.
Resumen general
| ORM | Estilo | Ideal para | Principal ventaja |
|---|---|---|---|
| Eloquent | Active Record | Aplicaciones Laravel y CRUD rápidos | Sencillez y fluidez |
| Doctrine | Data Mapper | Grandes sistemas, arquitecturas complejas | Flexibilidad y control total |
| Propel | Active Record | Sistemas donde defines primero el esquema | Generación automática de modelos |
| RedBeanPHP | Active Record (muy dinámico) | Prototipos, apps pequeñas | Cero configuración |
5.7.10 Ejercicios
Enunciado
✅ Crear un pequeño circuito MVC completo.\
✅ Practicar creación de rutas, controladores, vistas, y consultas a base de datos.\
✅ Utilizar métodos de Eloquent (all, create, where, orderBy).\
✅ Insertar datos de prueba y mostrarlos en la vista.\
✅ Usar Blade para imprimir datos, manejar condicionales y bucles.
Enunciado del Ejercicio
Vas a crear una pequeña aplicación web que permita:
-
Mostrar un listado de productos.
-
Insertar productos de prueba en la base de datos.
-
Filtrar productos en función del precio.
Preparación
-
Crea una nueva migración
create_products_table. -
Definir los campos:
-
id(autoincremental) -
name(string 255) -
description(text) -
price(decimal 8,2) -
stock(integer) -
timestamps
-
-
Ejecuta
php artisan migratepara crear la tabla.
Crear el Modelo
- Crea un modelo
Product:
En el modelo Product.php, define $fillable para los campos.
Crear el Controlador
Crea un controlador ProductController:
ProductController:
-
Define en el controlador:
-
Método
index()para listar productos. -
Método
createProducts()para insertar datos de prueba.
-
Definir las Rutas
En routes/web.php:
Crear la Vista
- Crea
resources/views/products/index.blade.php.
En la vista:
-
Mostrar un título.
-
Mostrar una lista
<ul>de productos con nombre y precio. -
Mostrar un mensaje si no hay productos.
-
Usar
@foreach,$loop->iteration,@if.
Código Específico
En ProductController:
ProductController.php
En resources/views/products/index.blade.php:
index.blade.php
Entregable esperado
✅ Si todo está bien:
-
Accediendo a
/products/createcrearás datos de prueba. -
Accediendo a
/productsverás la lista ordenada por precio.
✅ Deberías ver en pantalla los productos listados, o un mensaje si no hay productos.
Soluciones
1. Migración
Comando para crear la migración:
Contenido del archivo database/migrations/xxxx_xx_xx_create_products_table.php:
create_products_table.php
2. Modelo
Comando para crear el modelo:
Archivo: app/Models/Product.php
Product.php
3. Controlador
Comando para crear el controlador:
Archivo: app/Http/Controllers/ProductController.php
ProductController.php
4. Rutas
Archivo: routes/web.php
web.php
5. Vista
Archivo: resources/views/products/index.blade.php
index.blade.php
Comprobaciones finales
- ✅ Ejecutar
php artisan migratepara aplicar la migración. - ✅ Acceder a
/products/createpara insertar productos. - ✅ Acceder a
/productspara ver la lista ordenada por precio.
Rúbrica de evaluación
| Criterio | Puntos |
|---|---|
| Migración creada correctamente con todos los campos | 2 |
Modelo creado con $fillable adecuado |
1 |
Controlador con métodos index() y createProducts() |
2 |
| Rutas correctas definidas | 1 |
Vista que muestra productos con Blade (@foreach, @if) |
2 |
| Aplicación funcional de principio a fin | 2 |
| Total | 10 |