Laravel con MySQL: conexión, migraciones, Eloquent y consultas
Laravel puede conectarse a MySQL,
crear y modificar tablas mediante migraciones,
consultar datos con Query Builder o Eloquent
y administrar relaciones, índices,
transacciones y paginación
dentro de una misma aplicación.
Pero trabajar bien con Laravel y MySQL
requiere algo más que configurar
cinco variables en el archivo .env:
debes comprender cómo está diseñada
la base de datos y qué consultas
termina ejecutando tu aplicación.
¿Cómo conectar Laravel con MySQL?
Laravel puede conectarse a MySQL configurando la conexión de base de datos de la aplicación, normalmente mediante variables de entorno.
Una configuración habitual utiliza valores como:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=mi_aplicacion
DB_USERNAME=usuario
DB_PASSWORD=clave_segura
Después puedes utilizar migraciones para crear tablas, Eloquent para trabajar con models y relaciones, Query Builder para consultas más directas y transacciones para proteger operaciones que deben completarse como una unidad.
Laravel facilita la interacción con MySQL, pero conocer SQL sigue siendo esencial para diseñar relaciones, índices y consultas eficientes.
Laravel ofrece varias formas de trabajar con MySQL
Migraciones
Definen y versionan la estructura de la base de datos.
Query Builder
Construye consultas mediante una API de Laravel.
Eloquent
Representa registros mediante models.
Relaciones
Conecta usuarios, pedidos, productos y otras entidades.
Transacciones
Protegen operaciones de múltiples pasos.
Índices
Mejoran determinados patrones de consulta.
Laravel no crea automáticamente una buena base de datos
El framework facilita muchísimo la persistencia, pero las decisiones importantes siguen siendo tuyas.
Debes definir correctamente:
Qué entidades existen.
Qué datos necesita cada una.
Cómo se relacionan.
Qué campos son únicos.
Qué columnas necesitan índices.
Qué operaciones necesitan transacciones.
Una mala estructura continuará siendo una mala estructura aunque utilices Eloquent.
Aprende modelado, SQL y relaciones además del framework.
Configura MySQL mediante las variables de entorno de la aplicación
Una conexión típica puede utilizar:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=tienda
DB_USERNAME=tienda_user
DB_PASSWORD=una_clave_segura
DB_CONNECTION
Indica qué conexión utilizará la aplicación.
DB_HOST
Define dónde está el servidor MySQL.
DB_PORT
Corresponde al puerto utilizado por el servicio.
DB_DATABASE
Es la base que utilizará la aplicación.
DB_USERNAME y DB_PASSWORD
Son las credenciales utilizadas para autenticarse contra MySQL.
La configuración sensible debe permanecer fuera del código que compartes públicamente.
Si cambias la configuración y Laravel sigue usando valores anteriores, revisa la caché de configuración
En determinados entornos, la configuración puede haber sido almacenada para mejorar el arranque de la aplicación.
Durante desarrollo puede ser útil limpiar esa configuración cuando cambias variables relacionadas con la conexión.
php artisan config:clear
Si la aplicación continúa sin conectarse, revisa también:
Nombre de la base.
Usuario MySQL.
Contraseña.
Host.
Puerto.
Permisos del usuario.
Las migraciones permiten que la estructura de MySQL forme parte del código del proyecto
Supongamos que necesitamos una tabla de productos.
Una migración puede describirla:
<?php
Schema::create('productos', function (Blueprint $table) {
$table->id();
$table->string(
'nombre',
150
);
$table->decimal(
'precio',
12,
2
);
$table->boolean('activo')
->default(true);
$table->timestamps();
});
Después puedes aplicar las migraciones.
php artisan migrate
¿Por qué es mejor que modificar tablas manualmente?
Porque el cambio puede quedar documentado, versionado y reproducible entre:
desarrollo, pruebas, staging y producción.
Elige tipos de datos según lo que representa cada valor
No todas las columnas deberían ser strings simplemente porque resulta cómodo.
Laravel permite expresar distintos tipos dentro de las migraciones.
Texto
$table->string('nombre', 150);
Booleano
$table->boolean('activo')
->default(true);
Decimal
$table->decimal(
'precio',
12,
2
);
El diseño debería reflejar la semántica del dato y las operaciones que necesitarás realizar con él.
Las relaciones deberían existir también en la base de datos, no solamente en Eloquent
Supongamos que un pedido pertenece a un usuario.
La tabla pedidos puede almacenar una referencia al usuario.
<?php
$table->foreignId('usuario_id')
->constrained('usuarios');
Conceptualmente:
usuarios
│
│ id
↓
pedidos.usuario_id
Esto ayuda a preservar integridad entre registros.
Piensa también qué debería ocurrir al eliminar el registro padre
Según el dominio, podrías necesitar:
restringir, mantener, establecer null o eliminar registros relacionados.
Eliminar un usuario y borrar automáticamente toda su información puede ser correcto en un sistema e incorrecto en otro.
Los índices deben diseñarse según las consultas reales de tu aplicación
Supongamos que buscas constantemente productos por SKU.
Si además el SKU debe ser único, la base puede representar esa regla.
$table->string('sku', 100)
->unique();
Para otras columnas puedes definir índices sin necesidad de imponer unicidad.
$table->index('estado');
No indexes todas las columnas “por si acaso”
Los índices pueden acelerar búsquedas, filtros y joins, pero también:
ocupan espacio y añaden trabajo durante INSERT, UPDATE y DELETE.
Varias columnas pueden formar parte del mismo patrón de búsqueda
Imagina que una aplicación consulta repetidamente pedidos por:
usuario y estado.
Podría existir un caso para evaluar un índice compuesto.
$table->index([
'usuario_id',
'estado'
]);
Pero para diseñarlo correctamente necesitas observar cómo consultas realmente los datos.
El orden y composición de un índice importan.
Por eso, cuando la optimización empieza a ser relevante, conviene analizar la consulta y el plan que utiliza MySQL.
Crea un model para trabajar con los registros desde PHP
Para la tabla productos podemos utilizar:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
final class Producto extends Model
{
protected $fillable = [
'nombre',
'sku',
'precio',
'activo'
];
protected function casts(): array
{
return [
'activo' => 'boolean',
'precio' => 'decimal:2'
];
}
}
Ahora puedes consultar los registros mediante el model.
$productos = Producto::query()
->where('activo', true)
->orderBy('nombre')
->get();
Los casts ayudan a representar determinados valores con tipos adecuados dentro del model.
Laravel puede realizar las cuatro operaciones básicas sobre MySQL mediante Eloquent
Crear
$producto = Producto::create([
'nombre' => 'Monitor 27"',
'sku' => 'MON-27-001',
'precio' => 199990,
'activo' => true
]);
Leer
$producto = Producto::findOrFail($id);
Actualizar
$producto->update([
'precio' => 189990
]);
Eliminar
$producto->delete();
Validación y autorización siguen siendo responsabilidades diferentes.
Valida antes de enviar información a MySQL
Una creación segura no debería comenzar con:
Producto::create(
$request->all()
);
Es preferible definir qué campos espera la operación.
$datos = $request->validate([
'nombre' => [
'required',
'string',
'max:150'
],
'sku' => [
'required',
'string',
'max:100'
],
'precio' => [
'required',
'numeric',
'min:0'
]
]);
$producto = Producto::create($datos);
La validación comprueba la forma del dato.
Las reglas de negocio pueden necesitar comprobaciones adicionales.
MySQL define la relación y Eloquent la expresa desde PHP
Imagina esta estructura:
usuarios
│
│ 1
↓
pedidos
│
│ N
↓
items_pedido
Dentro del model Usuario:
<?php
public function pedidos()
{
return $this->hasMany(
Pedido::class
);
}
Y dentro de Pedido:
<?php
public function usuario()
{
return $this->belongsTo(
Usuario::class
);
}
Eloquent hace cómoda la navegación entre objetos, pero la integridad y estructura de esos datos pertenecen al modelo relacional.
Profundiza en models, relaciones, eager loading y N+1
El ORM es una capa sobre MySQL, no su sustituto.
Una relación cómoda puede provocar decenas o cientos de consultas
Este código parece sencillo:
$pedidos = Pedido::all();
foreach ($pedidos as $pedido) {
echo $pedido->usuario->nombre;
}
Si cada acceso al usuario dispara una consulta adicional, el número de operaciones puede crecer rápidamente.
Eager loading
$pedidos = Pedido::query()
->with('usuario')
->get();
Ahora indicas que la relación será necesaria durante esa consulta.
No todas las consultas necesitan pasar por un model Eloquent
Para determinadas operaciones puedes utilizar Query Builder.
<?php
use Illuminate\Support\Facades\DB;
$productos = DB::table('productos')
->select([
'id',
'nombre',
'precio'
])
->where('activo', true)
->orderBy('nombre')
->get();
Esto puede resultar apropiado para:
Reportes.
Consultas agregadas.
Operaciones sin model.
Consultas más cercanas al esquema.
La pregunta no es: “¿qué herramienta es mejor?”.
Es: “¿qué herramienta expresa mejor esta operación?”.
Deja que MySQL haga el trabajo que corresponde a la base de datos
Si quieres saber cuántos pedidos están pagados, no necesitas recuperar todos los pedidos para contarlos en PHP.
$total = Pedido::query()
->where('estado', 'pagado')
->count();
Lo mismo ocurre con sumas.
$ventas = Pedido::query()
->where('estado', 'pagado')
->sum('total');
La base de datos está diseñada para realizar este tipo de operaciones eficientemente.
No recuperes toda una tabla para mostrar veinte registros
Una tienda podría tener decenas o cientos de miles de productos.
Esto:
$productos = Producto::all();
no debería ser la estrategia por defecto para una interfaz paginada.
Puedes recuperar un conjunto limitado.
$productos = Producto::query()
->where('activo', true)
->orderBy('id')
->paginate(20);
La paginación reduce datos transferidos y memoria utilizada por la aplicación.
Usa transacciones cuando varias operaciones deben completarse juntas
Imagina que crear un pedido requiere:
Crear pedido.
Insertar sus items.
Actualizar stock.
Si la tercera operación falla, quizá no quieras conservar las dos primeras.
<?php
use Illuminate\Support\Facades\DB;
DB::transaction(function () use ($datos) {
$pedido = Pedido::create([
'usuario_id' => $datos['usuario_id'],
'total' => $datos['total']
]);
foreach ($datos['items'] as $item) {
$pedido->items()->create($item);
}
});
Si se produce una excepción durante la operación, la transacción permite revertir los cambios correspondientes.
Dos usuarios pueden intentar modificar los mismos datos al mismo tiempo
Esto aparece en situaciones como:
Stock limitado.
Reservas.
Saldos.
Cupos.
Una aplicación que primero consulta stock y después lo descuenta sin considerar concurrencia puede producir resultados inconsistentes.
Las transacciones pueden formar parte de la solución
Y determinadas operaciones pueden necesitar bloqueos o estrategias específicas de concurrencia.
Siguen siendo problemas de consistencia de datos.
Seeders permiten insertar datos necesarios para desarrollar o inicializar determinados entornos
Durante el desarrollo puedes necesitar:
Roles iniciales.
Categorías.
Configuraciones.
Datos de prueba.
Un seeder permite representar ese proceso mediante código.
<?php
Producto::create([
'nombre' => 'Producto de prueba',
'sku' => 'TEST-001',
'precio' => 9990,
'activo' => true
]);
No confundas datos de desarrollo con datos de producción
La estrategia de seed debería distinguir claramente información necesaria de datos meramente ficticios.
Factories pueden generar registros útiles para testing y desarrollo
Probar una aplicación con solamente dos registros puede esconder problemas que aparecen con volúmenes mayores.
Los datos generados pueden ayudarte a probar:
Paginación.
Filtros.
Relaciones.
Consultas grandes.
Interfaces con muchos resultados.
Esto resulta mucho más útil que crear manualmente cientos de filas.
Poder reconstruir una base de desarrollo reduce configuraciones manuales difíciles de repetir
Una de las ventajas de migraciones y seeders es que puedes describir gran parte del estado inicial necesario por la aplicación.
Esto ayuda cuando:
Entra un nuevo desarrollador.
Creas un entorno de pruebas.
Necesitas reconstruir datos locales.
Aprende a inspeccionar qué consulta estás construyendo
Una consulta Eloquent puede resultar muy legible:
$query = Producto::query()
->where('activo', true)
->where('precio', '>', 50000);
Durante debugging puede resultar útil inspeccionar el SQL que el builder representa.
$sql = $query->toSql();
Recuerda que una representación de este tipo puede contener placeholders en lugar de los valores finales.
La pregunta importante es qué está haciendo MySQL
Si una consulta es lenta, analiza:
Filtros.
JOIN.
Índices.
Ordenamientos.
Cantidad de filas examinadas.
Recupera solamente las columnas que necesita el caso de uso cuando eso aporte valor
Un listado quizá solo necesita:
ID, nombre, precio y estado.
$productos = Producto::query()
->select([
'id',
'nombre',
'precio',
'activo'
])
->where('activo', true)
->paginate(20);
Esto puede reducir datos innecesarios cuando una tabla contiene columnas grandes.
Tampoco optimices cada consulta prematuramente
La optimización debería responder a necesidades reales y mediciones, no solamente a reglas aplicadas mecánicamente.
Usar Laravel no significa que SQL directo esté prohibido
Determinados:
Reportes.
Agregaciones.
Consultas especializadas.
Procesos de análisis.
pueden resultar más claros usando Query Builder o expresiones SQL controladas.
El objetivo no es forzar toda consulta a parecer un model.
El objetivo es mantener código legible, seguro y eficiente.
La base de datos no debería definir automáticamente la respuesta de tu API
Si tienes un model Usuario con veinte atributos, eso no significa que debas devolverlos todos al cliente.
Una API debería exponer únicamente la información necesaria para ese contrato.
{
"id": 45,
"nombre": "Daniel",
"estado": "activo"
}
La estructura interna de MySQL y la estructura pública de una API son decisiones relacionadas, pero no necesariamente idénticas.
Comprende primero HTTP, JSON, autenticación y contratos de API
Después Laravel puede ayudarte a construir esa capa con mucha rapidez.
No mezcles queries, HTML y reglas de negocio dentro del mismo controller
Laravel permite realizar una consulta desde prácticamente cualquier lugar de la aplicación.
Eso no significa que sea una buena idea hacerlo sin una estructura.
Un controller debería evitar convertirse en algo como:
Request
↓
Validación
↓
20 consultas
↓
Reglas de negocio
↓
Pago
↓
Email
↓
HTML
↓
Response
Cuando el proceso crece, separa responsabilidades con criterio.
Eloquent reduce algunos riesgos de consultas, pero la seguridad de los datos va mucho más allá
Debes seguir pensando en:
Validación.
Autorización.
Mass assignment.
Exposición de información.
Credenciales MySQL.
Backups.
Utiliza un usuario MySQL adecuado para la aplicación
No necesitas entregar privilegios ilimitados simplemente para ejecutar consultas del sistema.
No muestres errores SQL internos al usuario final
Los detalles técnicos pueden registrarse de manera apropiada, mientras el usuario recibe una respuesta controlada.
Las migraciones forman parte del despliegue, pero deben ejecutarse con criterio
Un cambio sencillo en desarrollo puede ser mucho más delicado sobre una tabla con millones de filas.
Antes de modificar la base de producción, considera:
Backups.
Impacto de locks.
Compatibilidad del código.
Duración de la operación.
Plan de recuperación.
Construye una tienda mínima para practicar Laravel con MySQL
Puedes modelar:
usuarios
↓
pedidos
↓
items_pedido
↑
productos
↑
categorias
Este proyecto permite practicar:
Migraciones.
Foreign keys.
Índices.
Eloquent.
Relaciones.
N+1.
Transacciones.
Paginación.
Añade reglas reales
Por ejemplo:
un SKU no puede repetirse, un pedido debe pertenecer a un usuario y una compra no debería dejar stock negativo.
Ahí empiezas a aprender bases de datos de verdad y no solamente sintaxis del framework.
En qué orden aprender Laravel con MySQL
SQL básico
SELECT, INSERT, UPDATE y DELETE.
Conexión
Configura Laravel con MySQL.
Migraciones
Define tablas mediante código.
Foreign keys
Modela relaciones reales.
Eloquent
Models y CRUD.
Relaciones
hasMany, belongsTo y pivots.
Índices
Optimiza patrones reales.
Transacciones
Protege operaciones múltiples.
Rendimiento
Detecta N+1 y queries lentas.
Producción
Backups, migraciones y monitoreo.
Qué evitar al trabajar con Laravel y MySQL
Laravel facilita MySQL cuando entiendes lo que ocurre debajo del framework
Configura correctamente la conexión de la aplicación.
Utiliza migraciones para versionar el esquema.
Modela relaciones con foreign keys reales.
Añade índices según patrones de consulta.
Utiliza Eloquent cuando trabajar con models aporte claridad.
Usa Query Builder o SQL cuando expresen mejor determinadas consultas.
Detecta N+1, pagina listados grandes y evita recuperar información innecesaria.
Protege operaciones de múltiples escrituras mediante transacciones cuando corresponda.
Laravel puede hacer que trabajar con MySQL sea mucho más cómodo, pero la calidad de tu aplicación seguirá dependiendo de cuánto entiendas modelado relacional, SQL, índices, consistencia y rendimiento.
Continúa profundizando en Laravel y bases de datos
Preguntas sobre Laravel con MySQL
Configura la conexión MySQL de la aplicación con el host, puerto, nombre de base, usuario y contraseña correspondientes. Laravel utiliza esa configuración para ejecutar migraciones, Query Builder y Eloquent.
No necesariamente. Laravel puede trabajar con distintas soluciones de persistencia y bases de datos. MySQL es una opción muy habitual para aplicaciones web, pero no es la única.
Son archivos que describen cambios en la estructura de la base de datos. Permiten crear, modificar y versionar tablas, columnas, índices y relaciones como parte del proyecto.
MySQL es el sistema de base de datos. Eloquent es una capa de Laravel para consultar y manipular esos datos mediante models y objetos PHP. Eloquent termina generando operaciones que la base de datos ejecuta.
Sí, es muy recomendable. SQL te permite comprender consultas, relaciones, JOIN, índices, agregaciones y transacciones. Esa base resulta clave para utilizar Eloquent y Query Builder correctamente.
Una foreign key representa una relación entre registros de tablas distintas. Por ejemplo, pedidos.usuario_id puede referenciar usuarios.id. Las migraciones permiten definir estas relaciones desde Laravel.
Cuando varias operaciones de base de datos forman una sola operación lógica y no quieres que únicamente una parte quede aplicada si ocurre un error. Crear un pedido y sus líneas es un ejemplo habitual.
No. Eloquent facilita escribir consultas, pero el rendimiento también depende del diseño de tablas, índices, relaciones, volumen, paginación y SQL generado. Necesitas observar el comportamiento real de MySQL.
No aprendas Laravel con MySQL como una colección de comandos de Eloquent
Diseña primero los datos, crea migraciones, define relaciones, entiende SQL, construye consultas y observa cómo responde MySQL. Después Eloquent y Query Builder se convierten en herramientas mucho más poderosas, porque sabes qué están abstrayendo.
Continuar con Eloquent →