7: Modelos y Eloquent ORM en Laravel 12 - Guía Práctica
Introducción
Eloquent es el ORM (Object-Relational Mapping) de Laravel que te permite interactuar con tu base de datos de manera elegante y expresiva. En lugar de escribir SQL directamente, trabajas con objetos PHP que representan tus tablas. En este tutorial, aprenderás todo sobre modelos y Eloquent en Laravel 12.
Índice
¿Qué es Eloquent?
Creación de Modelos
Convenciones de Eloquent
Operaciones CRUD Básicas
Scopes (Consultas Personalizadas)
Relaciones entre Modelos
Mass Assignment y Fillable
Soft Deletes
Consultas Avanzadas
Ejercicio Práctico: Sistema de Pastelería
1. ¿Qué es Eloquent?
Eloquent es el ORM de Laravel que proporciona una implementación elegante y sencilla del patrón Active Record. Cada modelo representa una tabla en tu base de datos y te permite:
Realizar consultas sin escribir SQL
Definir relaciones entre tablas
Usar scopes para consultas reutilizables
Manejar eventos del ciclo de vida
Utilizar mutators y accessors
Ventajas de Eloquent
// ✅ Con Eloquent - Código limpio y legible
$pasteles = Pastel::where('sabor', 'chocolate')
->where('precio', '>', 100)
->orderBy('nombre')
->get();
// ❌ Sin Eloquent - SQL directo
$pasteles = DB::select("SELECT * FROM pasteles
WHERE sabor = 'chocolate'
AND precio > 100
ORDER BY nombre");
// ✅ Con Eloquent - Inserción elegante
$pastel = new Pastel();
$pastel->nombre = 'Pastel de Chocolate';
$pastel->precio = 150;
$pastel->save();
// ❌ Sin Eloquent - Inserción manual
DB::insert("INSERT INTO pasteles (nombre, precio) VALUES (?, ?)",
['Pastel de Chocolate', 150]);2. Creación de Modelos
Comandos Artisan
# Crear un modelo básico
php artisan make:model Pastel
# Crear modelo con migración
php artisan make:model Pastel -m
# Crear modelo con migración, factory y seeder
php artisan make:model Pastel -mf
# Crear modelo con todos los recursos
php artisan make:model Pastel -a
# Crear modelo en un subdirectorio
php artisan make:model Models/PastelModelo básico
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Pastel extends Model
{
// Especificar la tabla (opcional si sigue la convención)
protected $table = 'pasteles';
// Campos que pueden ser asignados masivamente
protected $fillable = [
'nombre', 'sabor', 'precio', 'disponible', 'descripcion'
];
// Campos que no deben ser mostrados en arrays/JSON
protected $hidden = [];
// Campos que deben ser tratados como fechas
protected $dates = ['created_at', 'updated_at'];
// Casts de tipos de datos
protected $casts = [
'precio' => 'float',
'disponible' => 'boolean',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
}Modelo con todos los recursos
# Crear modelo con migración, factory y controlador
php artisan make:model Pastel -mfc3. Convenciones de Eloquent
Convenciones de nombres
| Elemento | Convención | Ejemplo |
|---|---|---|
| Modelo | Singular, UpperCamelCase | User, Pastel, Categoria |
| Tabla | Plural, snake_case | users, pasteles, categorias |
| Clave primaria | id | id |
| Clave foránea | modelo_id | pastel_id, user_id |
| Tabla pivote | Singular alfabético | pastel_ingrediente |
| Timestamps | created_at, updated_at | - |
| Soft Deletes | deleted_at | - |
Ejemplo de convenciones
// Modelo (singular, UpperCamelCase)
class Pastel extends Model
{
// Tabla (plural, snake_case) - auto-detectada
protected $table = 'pasteles';
// Clave primaria - por defecto 'id'
protected $primaryKey = 'id';
// Timestamps - por defecto true
public $timestamps = true;
}
// Relación (clave foránea: modelo_id)
public function ingrediente()
{
return $this->belongsTo(Ingrediente::class, 'ingrediente_id');
}Especificar la tabla manualmente
class Pastel extends Model
{
// Forzar el nombre de la tabla (necesario en español)
protected $table = 'pasteles';
// Si la clave primaria no es 'id'
protected $primaryKey = 'pastel_id';
// Si la clave primaria no es autoincrementable
public $incrementing = false;
// Si la clave primaria no es un entero
protected $keyType = 'string';
// Deshabilitar timestamps
public $timestamps = false;
}4. Operaciones CRUD Básicas
CREATE - Crear registros
// Método 1: Usando new
$pastel = new Pastel();
$pastel->nombre = 'Pastel de Chocolate';
$pastel->sabor = 'chocolate';
$pastel->precio = 150.00;
$pastel->save();
// Método 2: Usando create (requiere $fillable)
$pastel = Pastel::create([
'nombre' => 'Pastel de Vainilla',
'sabor' => 'vainilla',
'precio' => 120.00,
'disponible' => true
]);
// Método 3: Usando firstOrCreate
$pastel = Pastel::firstOrCreate(
['nombre' => 'Pastel Especial'], // Buscar por estos campos
['sabor' => 'chocolate', 'precio' => 200] // Crear con estos datos
);
// Método 4: Usando firstOrNew
$pastel = Pastel::firstOrNew(
['nombre' => 'Pastel Nuevo'],
['sabor' => 'fresa', 'precio' => 180]
);
$pastel->save();
// Método 5: Usando updateOrCreate
$pastel = Pastel::updateOrCreate(
['nombre' => 'Pastel Especial'],
['sabor' => 'chocolate', 'precio' => 250]
);READ - Leer registros
// Obtener todos los registros
$pasteles = Pastel::all();
// Obtener con condiciones
$pasteles = Pastel::where('sabor', 'chocolate')->get();
// Obtener el primer registro
$pastel = Pastel::where('sabor', 'chocolate')->first();
// Obtener por ID
$pastel = Pastel::find(1);
// Obtener por ID o lanzar excepción
$pastel = Pastel::findOrFail(1);
// Obtener con paginación
$pasteles = Pastel::paginate(10);
// Obtener solo algunos campos
$pasteles = Pastel::select('nombre', 'precio')->get();
// Obtener contando registros
$count = Pastel::where('sabor', 'chocolate')->count();
// Verificar si existe
$existe = Pastel::where('nombre', 'Pastel Especial')->exists();
// Obtener valores específicos
$nombres = Pastel::pluck('nombre');
$precios = Pastel::pluck('precio', 'nombre');UPDATE - Actualizar registros
// Método 1: Encontrar y actualizar
$pastel = Pastel::find(1);
$pastel->precio = 180.00;
$pastel->save();
// Método 2: Actualizar directamente
Pastel::where('sabor', 'chocolate')
->update(['precio' => 200]);
// Método 3: Usando update en modelo existente
$pastel = Pastel::find(1);
$pastel->update([
'precio' => 190,
'disponible' => true
]);
// Método 4: Incrementar/Decrementar
Pastel::where('sabor', 'chocolate')
->increment('precio', 10);
Pastel::where('sabor', 'vainilla')
->decrement('precio', 5);DELETE - Eliminar registros
// Método 1: Encontrar y eliminar
$pastel = Pastel::find(1);
$pastel->delete();
// Método 2: Eliminar por ID
Pastel::destroy(1);
// Método 3: Eliminar múltiples IDs
Pastel::destroy([1, 2, 3]);
// Método 4: Eliminar con condición
Pastel::where('precio', '<', 50)->delete();
// Método 5: Eliminar todos (¡cuidado!)
Pastel::truncate();5. Scopes (Consultas Personalizadas)
Los scopes te permiten definir consultas reutilizables en tus modelos.
Scopes locales (locales)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Pastel extends Model
{
protected $table = 'pasteles';
protected $fillable = ['nombre', 'sabor', 'precio', 'disponible'];
// Scope para pasteles de chocolate
public function scopeChocolate($query)
{
return $query->where('sabor', 'chocolate');
}
// Scope para pasteles de vainilla
public function scopeVainilla($query)
{
return $query->where('sabor', 'vainilla');
}
// Scope para pasteles disponibles
public function scopeDisponibles($query)
{
return $query->where('disponible', true);
}
// Scope dinámico (con parámetros)
public function scopeSabor($query, $sabor)
{
return $query->where('sabor', $sabor);
}
// Scope con múltiples parámetros
public function scopePrecioEntre($query, $min, $max)
{
return $query->whereBetween('precio', [$min, $max]);
}
// Scope con lógica compleja
public function scopeRecomendados($query)
{
return $query->where('disponible', true)
->where('precio', '>', 100)
->orderBy('precio', 'desc')
->limit(5);
}
// Scope con like
public function scopeBuscar($query, $termino)
{
return $query->where('nombre', 'LIKE', "%{$termino}%")
->orWhere('descripcion', 'LIKE', "%{$termino}%");
}
}Usar scopes
// Scope simple
$pasteles = Pastel::chocolate()->get();
// Múltiples scopes
$pasteles = Pastel::disponibles()->chocolate()->get();
// Scope con parámetros
$pasteles = Pastel::sabor('chocolate')->get();
$pasteles = Pastel::precioEntre(50, 200)->get();
// Scope con lógica compleja
$pasteles = Pastel::recomendados()->get();
// Scope con like
$pasteles = Pastel::buscar('chocolate')->get();
// Combinando scopes con otros métodos
$pasteles = Pastel::disponibles()
->sabor('chocolate')
->orderBy('precio', 'asc')
->take(10)
->get();Scopes globales
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class Pastel extends Model
{
// Scope global para siempre incluir pasteles disponibles
protected static function booted()
{
static::addGlobalScope('disponibles', function (Builder $builder) {
$builder->where('disponible', true);
});
}
// Eliminar un scope global
public static function withoutDisponibles()
{
return static::withoutGlobalScope('disponibles');
}
}
// Uso
$pasteles = Pastel::all(); // Solo disponibles
$todos = Pastel::withoutGlobalScope('disponibles')->get();6. Relaciones entre Modelos
Relaciones comunes
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Pastel extends Model
{
// Relación belongsTo (pertenece a)
public function categoria()
{
return $this->belongsTo(Categoria::class);
}
// Relación belongsTo con clave personalizada
public function creador()
{
return $this->belongsTo(User::class, 'user_id');
}
// Relación hasMany (tiene muchos)
public function ingredientes()
{
return $this->hasMany(Ingrediente::class);
}
// Relación hasMany con orden
public function ingredientesOrdenados()
{
return $this->hasMany(Ingrediente::class)->orderBy('nombre');
}
// Relación belongsToMany (muchos a muchos)
public function presentaciones()
{
return $this->belongsToMany(Presentacion::class, 'pastel_presentacion')
->withPivot('cantidad', 'unidad')
->withTimestamps();
}
// Relación hasOne (tiene uno)
public function detalle()
{
return $this->hasOne(DetallePastel::class);
}
// Relación hasManyThrough (a través de)
public function ventas()
{
return $this->hasManyThrough(Venta::class, DetalleVenta::class);
}
// Relación polimórfica
public function imagenes()
{
return $this->morphMany(Imagen::class, 'imagenable');
}
}Ejemplos de relaciones inversas
// Modelo Categoria
class Categoria extends Model
{
// Relación inversa de belongsTo
public function pasteles()
{
return $this->hasMany(Pastel::class);
}
}
// Modelo Ingrediente
class Ingrediente extends Model
{
// Relación inversa de hasMany
public function pastel()
{
return $this->belongsTo(Pastel::class);
}
}Eager Loading (Carga anticipada)
// Sin eager loading (N+1 problema)
$pasteles = Pastel::all();
foreach ($pasteles as $pastel) {
echo $pastel->categoria->nombre; // Hace consulta adicional por cada pastel
}
// Con eager loading (una sola consulta adicional)
$pasteles = Pastel::with('categoria')->get();
foreach ($pasteles as $pastel) {
echo $pastel->categoria->nombre; // No hay consultas adicionales
}
// Cargar múltiples relaciones
$pasteles = Pastel::with(['categoria', 'ingredientes'])->get();
// Cargar relaciones anidadas
$pasteles = Pastel::with('categoria.pasteles')->get();
// Cargar solo ciertas columnas
$pasteles = Pastel::with('categoria:id,nombre')->get();
// Cargar con condiciones
$pasteles = Pastel::with(['ingredientes' => function ($query) {
$query->where('cantidad', '>', 10)->orderBy('nombre');
}])->get();Lazy Eager Loading
$pasteles = Pastel::all();
$pasteles->load('categoria');
$pasteles->load(['ingredientes', 'presentaciones']);
// Cargar condicionalmente
if ($algo) {
$pasteles->load('categoria');
}7. Mass Assignment y Fillable
Protección contra asignación masiva
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Pastel extends Model
{
// Opción 1: Campos permitidos para asignación masiva
protected $fillable = [
'nombre',
'sabor',
'precio',
'disponible',
'descripcion',
'categoria_id'
];
// Opción 2: Campos protegidos (no permitidos)
protected $guarded = [
'id',
'created_at',
'updated_at'
];
// Opción 3: Proteger todo (usar solo fillable)
protected $guarded = [];
// Opción 4: Permisos específicos
protected $fillable = ['nombre', 'sabor'];
protected $guarded = ['id'];
}Ejemplos de asignación masiva
// Con fillable
$pastel = Pastel::create($request->all());
// Con guarded
$pastel = Pastel::create($request->except(['id', 'created_at']));
// Asignar y guardar
$pastel = new Pastel($request->all());
$pastel->save();
// Actualizar con asignación masiva
$pastel = Pastel::find(1);
$pastel->fill($request->all());
$pastel->save();8. Soft Deletes
Habilitar Soft Deletes
// Migración
Schema::create('pasteles', function (Blueprint $table) {
$table->id();
$table->string('nombre');
$table->string('sabor');
$table->decimal('precio', 8, 2);
$table->boolean('disponible')->default(true);
$table->softDeletes(); // Añade columna deleted_at
$table->timestamps();
});
// Modelo
use Illuminate\Database\Eloquent\SoftDeletes;
class Pastel extends Model
{
use SoftDeletes;
protected $dates = ['deleted_at'];
}Operaciones con Soft Deletes
// Eliminar (soft delete)
$pastel->delete();
// Verificar si está eliminado
if ($pastel->trashed()) {
echo 'El pastel está eliminado';
}
// Incluir eliminados en consultas
$pasteles = Pastel::withTrashed()->get();
// Solo eliminados
$pasteles = Pastel::onlyTrashed()->get();
// Restaurar
$pastel->restore();
// Eliminar permanentemente (hard delete)
$pastel->forceDelete();
// Forzar eliminación de varios
Pastel::where('disponible', false)->forceDelete();9. Consultas Avanzadas
Condiciones complejas
// OR y AND combinados
$pasteles = Pastel::where('sabor', 'chocolate')
->orWhere('sabor', 'vainilla')
->where('precio', '>', 50)
->get();
// Subconsultas
$pasteles = Pastel::whereIn('categoria_id', function ($query) {
$query->select('id')
->from('categorias')
->where('activa', true);
})->get();
// Exists
$pasteles = Pastel::whereHas('categoria', function ($query) {
$query->where('nombre', 'Tortas');
})->get();
// DoesntHave
$pasteles = Pastel::doesntHave('ingredientes')->get();
// Where con JSON
$pasteles = Pastel::where('datos->color', 'rojo')->get();Agregaciones y agrupamiento
// Count
$count = Pastel::where('sabor', 'chocolate')->count();
// Sum
$total = Pastel::where('sabor', 'chocolate')->sum('precio');
// Average
$promedio = Pastel::where('sabor', 'chocolate')->avg('precio');
// Max
$maximo = Pastel::where('sabor', 'chocolate')->max('precio');
// Min
$minimo = Pastel::where('sabor', 'chocolate')->min('precio');
// Group by
$pasteles = Pastel::select('sabor', Pastel::raw('count(*) as total'))
->groupBy('sabor')
->get();
// Having
$pasteles = Pastel::select('sabor', Pastel::raw('avg(precio) as promedio'))
->groupBy('sabor')
->having('promedio', '>', 100)
->get();Subconsultas
// Subconsulta en select
$pasteles = Pastel::select('nombre', 'precio')
->selectSub(function ($query) {
$query->select('precio')
->from('pasteles')
->whereColumn('sabor', 'pasteles.sabor')
->orderBy('precio', 'desc')
->limit(1);
}, 'precio_maximo_sabor')
->get();
// Subconsulta en where
$pasteles = Pastel::where('precio', '>', function ($query) {
$query->select(Pastel::raw('avg(precio)'))
->from('pasteles');
})->get();Raw Queries
// Select raw
$pasteles = Pastel::select(
Pastel::raw('nombre, precio, precio * 1.16 as precio_con_iva')
)->get();
// Where raw
$pasteles = Pastel::whereRaw('precio > ?', [100])->get();
// Order by raw
$pasteles = Pastel::orderByRaw('precio DESC, nombre ASC')->get();
// Raw con valores
$pasteles = Pastel::whereRaw('precio > :precio', ['precio' => 100])->get();10. Ejercicio Práctico: Sistema de Pastelería
Paso 1: Crear modelos
# Crear modelos con migraciones
php artisan make:model Pastel -m
php artisan make:model Categoria -m
php artisan make:model Ingrediente -m
php artisan make:model Venta -mPaso 2: Configurar modelos
// app/Models/Pastel.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Pastel extends Model
{
use SoftDeletes;
protected $table = 'pasteles';
protected $fillable = [
'nombre', 'sabor', 'precio', 'disponible',
'descripcion', 'categoria_id'
];
protected $casts = [
'precio' => 'float',
'disponible' => 'boolean',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime'
];
// Relaciones
public function categoria()
{
return $this->belongsTo(Categoria::class);
}
public function ingredientes()
{
return $this->belongsToMany(Ingrediente::class, 'pastel_ingrediente')
->withPivot('cantidad', 'unidad')
->withTimestamps();
}
public function ventas()
{
return $this->hasMany(Venta::class);
}
// Scopes
public function scopeDisponible($query)
{
return $query->where('disponible', true);
}
public function scopeSabor($query, $sabor)
{
return $query->where('sabor', $sabor);
}
public function scopePrecioEntre($query, $min, $max)
{
return $query->whereBetween('precio', [$min, $max]);
}
public function scopeBuscar($query, $termino)
{
return $query->where('nombre', 'LIKE', "%{$termino}%")
->orWhere('descripcion', 'LIKE', "%{$termino}%");
}
// Accesors y Mutators
public function getNombreCompletoAttribute()
{
return "{$this->nombre} - Sabor: {$this->sabor}";
}
public function getPrecioConIvaAttribute()
{
return $this->precio * 1.16;
}
public function setNombreAttribute($value)
{
$this->attributes['nombre'] = ucwords(strtolower($value));
}
}
// app/Models/Categoria.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Categoria extends Model
{
protected $fillable = ['nombre', 'descripcion'];
public function pasteles()
{
return $this->hasMany(Pastel::class);
}
}
// app/Models/Ingrediente.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Ingrediente extends Model
{
protected $fillable = ['nombre', 'stock', 'unidad'];
public function pasteles()
{
return $this->belongsToMany(Pastel::class, 'pastel_ingrediente')
->withPivot('cantidad', 'unidad')
->withTimestamps();
}
}
// app/Models/Venta.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Venta extends Model
{
protected $fillable = [
'pastel_id', 'cantidad', 'precio_total',
'cliente_nombre', 'cliente_email', 'fecha_venta'
];
public function pastel()
{
return $this->belongsTo(Pastel::class);
}
}Paso 3: Crear controlador para pruebas
php artisan make:controller PastelPruebaController<?php
namespace App\Http\Controllers;
use App\Models\Pastel;
use App\Models\Categoria;
use Illuminate\Http\Request;
class PastelPruebaController extends Controller
{
public function index()
{
// 1. Crear categorías
$categoria = Categoria::create([
'nombre' => 'Tortas Especiales',
'descripcion' => 'Tortas para ocasiones especiales'
]);
// 2. Crear pasteles
$pastel1 = Pastel::create([
'nombre' => 'Pastel de Chocolate Especial',
'sabor' => 'chocolate',
'precio' => 250.00,
'disponible' => true,
'descripcion' => 'Delicioso pastel de chocolate con ganache',
'categoria_id' => $categoria->id
]);
$pastel2 = Pastel::create([
'nombre' => 'Pastel de Vainilla',
'sabor' => 'vainilla',
'precio' => 180.00,
'disponible' => true,
'descripcion' => 'Pastel de vainilla con crema de mantequilla',
'categoria_id' => $categoria->id
]);
// 3. Consultas con scopes
$pastelesDisponibles = Pastel::disponible()->get();
$pastelesChocolate = Pastel::sabor('chocolate')->get();
$pastelesCaros = Pastel::precioEntre(200, 500)->get();
// 4. Buscar pasteles
$resultadosBusqueda = Pastel::buscar('chocolate')->get();
// 5. Actualizar pastel
$pastel1->update([
'precio' => 280.00
]);
// 6. Eliminar pastel (soft delete)
$pastel2->delete();
// 7. Recuperar pasteles eliminados
$eliminados = Pastel::onlyTrashed()->get();
// 8. Restaurar pastel
$pastel2->restore();
// 9. Crear venta
$venta = $pastel1->ventas()->create([
'cantidad' => 2,
'precio_total' => 560.00,
'cliente_nombre' => 'Juan Pérez',
'cliente_email' => 'juan@email.com',
'fecha_venta' => now()
]);
// 10. Usar accesors
$nombreCompleto = $pastel1->nombre_completo;
$precioConIva = $pastel1->precio_con_iva;
// 11. Eager loading
$pastelesConCategoria = Pastel::with('categoria')->get();
// 12. Join de tabla pivot
$pastelConIngredientes = Pastel::with('ingredientes')->find(1);
return response()->json([
'pasteles_disponibles' => $pastelesDisponibles,
'pasteles_chocolate' => $pastelesChocolate,
'pasteles_caros' => $pastelesCaros,
'resultados_busqueda' => $resultadosBusqueda,
'eliminados' => $eliminados,
'venta' => $venta,
'nombre_completo' => $nombreCompleto,
'precio_con_iva' => $precioConIva,
'pasteles_con_categoria' => $pastelesConCategoria,
'pastel_con_ingredientes' => $pastelConIngredientes
]);
}
}Paso 4: Crear ruta de prueba
// routes/web.php
Route::get('/pruebas-pastel', [PastelPruebaController::class, 'index'])->name('pruebas.pastel');Paso 5: Crear migraciones
// Crear migraciones para las tablas// database/migrations/xxxx_create_categorias_table.php
Schema::create('categorias', function (Blueprint $table) {
$table->id();
$table->string('nombre', 100);
$table->text('descripcion')->nullable();
$table->timestamps();
});
// database/migrations/xxxx_create_pasteles_table.php
Schema::create('pasteles', function (Blueprint $table) {
$table->id();
$table->string('nombre', 100);
$table->string('sabor', 50);
$table->decimal('precio', 8, 2);
$table->boolean('disponible')->default(true);
$table->text('descripcion')->nullable();
$table->foreignId('categoria_id')->constrained()->onDelete('cascade');
$table->softDeletes();
$table->timestamps();
});
// database/migrations/xxxx_create_ingredientes_table.php
Schema::create('ingredientes', function (Blueprint $table) {
$table->id();
$table->string('nombre', 100);
$table->decimal('stock', 8, 2)->default(0);
$table->string('unidad', 20);
$table->timestamps();
});
// database/migrations/xxxx_create_pastel_ingrediente_table.php (pivot)
Schema::create('pastel_ingrediente', function (Blueprint $table) {
$table->id();
$table->foreignId('pastel_id')->constrained()->onDelete('cascade');
$table->foreignId('ingrediente_id')->constrained()->onDelete('cascade');
$table->decimal('cantidad', 8, 2);
$table->string('unidad', 20);
$table->timestamps();
});
// database/migrations/xxxx_create_ventas_table.php
Schema::create('ventas', function (Blueprint $table) {
$table->id();
$table->foreignId('pastel_id')->constrained()->onDelete('cascade');
$table->integer('cantidad');
$table->decimal('precio_total', 10, 2);
$table->string('cliente_nombre', 100);
$table->string('cliente_email', 100);
$table->timestamp('fecha_venta');
$table->timestamps();
});Paso 6: Ejecutar y probar
# Ejecutar migraciones
php artisan migrate
# Ir a la URL de prueba
http://localhost:8000/pruebas-pastelComandos Útiles
# Crear modelo
php artisan make:model Pastel
# Crear modelo con migración
php artisan make:model Pastel -m
# Crear modelo con todos los recursos
php artisan make:model Pastel -a
# Crear modelo en subdirectorio
php artisan make:model Models/Pastel
# Crear controlador con modelo
php artisan make:controller PastelController --model=Pastel
# Ver consultas SQL ejecutadas
php artisan tinker
# Luego: DB::enableQueryLog(); Pastel::all(); DB::getQueryLog();Resumen
| Concepto | Descripción | Ejemplo |
|---|---|---|
| Modelo | Representa una tabla | class Pastel extends Model |
| Fillable | Campos asignables masivamente | protected $fillable = ['nombre'] |
| Scopes | Consultas reutilizables | public function scopeChocolate($query) |
| Relaciones | Conexiones entre modelos | belongsTo(), hasMany() |
| Eager Loading | Carga anticipada de relaciones | with('categoria') |
| Soft Deletes | Eliminación lógica | use SoftDeletes |
| Accessors | Transformar datos al leer | getNombreAttribute() |
| Mutators | Transformar datos al guardar | setNombreAttribute() |
¡Con Eloquent puedes manejar tu base de datos de manera elegante y productiva en Laravel
Comentarios
Publicar un comentario