---
title: "Aprende Laravel: Models, Database & Eloquent"
excerpt: "Domina Eloquent ORM en Laravel: crea models, migrations, relaciones y aprende a trabajar con la base de datos de forma elegante."
date: "2026-02-14T11:00:00.000Z"
lastModified: "2026-03-29T00:00:00.000Z"
category: "Laravel"
author:
  name: "angel cruz"
  picture: "https://angelcruzdevcdn.nyc3.cdn.digitaloceanspaces.com/images/me/angel-cruz.png"
ogImage:
  url: "/images/open-graph/laravel-opengraph-image.png"
seo_title: "Eloquent ORM en Laravel 13: Models, Migrations y Relaciones"
seo_description: "Aprende a usar Eloquent ORM en Laravel 13: models, migrations, relaciones, CRUD y UUIDv7. Guía completa en español."
keywords:
  - laravel
  - eloquent
  - models
  - database
  - migrations
  - laravel español
learning_path:
  series: "laravel-fundamentals"
  order: 5
  total: 6
  prev_slug: "aprende-laravel-controllers"
  next_slug: "aprende-laravel-proyecto-blog"
---

![Laravel Eloquent](https://angelcruzdevcdn.nyc3.cdn.digitaloceanspaces.com/images/laravel-banner.svg)

En los posts anteriores aprendiste a crear [rutas](/post/aprende-laravel-rutas), [vistas](/post/aprende-laravel-vistas-layouts) y [controllers](/post/aprende-laravel-controllers). Ahora es momento de trabajar con la base de datos usando el poderoso **Eloquent ORM**.

### ¿Qué es Eloquent ORM?

**ORM** significa Object-Relational Mapping (Mapeo Objeto-Relacional). Eloquent es el ORM de Laravel que te permite trabajar con la base de datos usando objetos PHP en lugar de escribir SQL directamente.

**Beneficios de usar Eloquent:**

- Sintaxis como `Post::all()` en lugar de `SELECT * FROM posts`
- Tu IDE puede autocompletar propiedades
- Define relaciones entre tablas fácilmente
- Previene SQL injection automáticamente
- Versiona cambios en tu base de datos con Migrations

### El patrón Active Record

Eloquent usa el patrón **Active Record**, donde cada model representa una tabla y cada instancia del model representa una fila:

```php
// Una instancia = una fila en la tabla
$post = new Post();
$post->title = 'Mi primer post';
$post->save(); // INSERT INTO posts...
```

### Creando tu Primer Model

Usa el comando Artisan `make:model`. El flag `-m` crea automáticamente una migration:

```bash
php artisan make:model Post -m
```

Esto crea dos archivos:

1. **Model**: `app/Models/Post.php`
2. **Migration**: `database/migrations/xxxx_create_posts_table.php`

**Convenciones de naming:**

- Model: `Post` (singular, PascalCase)
- Tabla asociada: `posts` (plural, snake_case)
- Primary key: `id` (autoincremento por defecto)
- Timestamps: `created_at` y `updated_at` (automáticos)

> **Convención sobre configuración:** Si sigues las convenciones de Laravel, no necesitas configurar casi nada. El model Post automáticamente usa la tabla posts.

### Migrations: Construyendo tu Schema

Las **migrations** son como "control de versiones" para tu base de datos. Cada migration define cómo crear o modificar tablas.

Abre la migration generada (`database/migrations/xxxx_create_posts_table.php`):

```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('content');
            $table->boolean('published')->default(false);
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};
```

**Column types comunes:**

- `id()` - Primary key autoincremento (BIGINT UNSIGNED)
- `string('name', 100)` - VARCHAR(100)
- `text('content')` - TEXT
- `integer('views')` - INTEGER
- `boolean('published')` - BOOLEAN
- `timestamp('published_at')->nullable()` - TIMESTAMP NULL
- `timestamps()` - Crea created_at y updated_at
- `foreignId('user_id')->constrained()` - Foreign key a tabla users

### Ejecutando Migrations

Para crear las tablas en tu base de datos:

```bash
php artisan migrate
```

Verás algo como:

```bash
INFO  Running migrations.

2025_02_14_000001_create_posts_table ............ 10ms DONE
```

**Otros comandos útiles:**

```bash
php artisan migrate:rollback  # Deshace el último batch
php artisan migrate:fresh     # Borra todas las tablas y re-crea
php artisan migrate:refresh   # Rollback + migrate
```

> **Nunca modifiques migrations ya ejecutadas en producción.** Siempre crea una nueva migration para cambios.

### CRUD con Eloquent

Ahora que tienes tu tabla, puedes interactuar con ella usando el model.

**Create - Crear registros:**

```php
// Opción 1: Create + Save
$post = new Post();
$post->title = 'Mi Primer Post';
$post->content = 'Contenido del post...';
$post->save();

// Opción 2: Create (mass assignment)
$post = Post::create([
    'title' => 'Mi Primer Post',
    'content' => 'Contenido del post...',
]);

// Opción 3: firstOrCreate (busca o crea)
$post = Post::firstOrCreate(
    ['title' => 'Mi Primer Post'],
    ['content' => 'Contenido...']
);
```

**Read - Leer registros:**

```php
// Todos los posts
$posts = Post::all();

// Buscar por ID
$post = Post::find(1);

// Buscar por ID o lanzar 404
$post = Post::findOrFail(1);

// Primera coincidencia
$post = Post::where('published', true)->first();

// Múltiples condiciones
$posts = Post::where('published', true)
    ->where('views', '>', 100)
    ->get();

// Ordenar
$posts = Post::orderBy('created_at', 'desc')->get();

// Limitar resultados
$posts = Post::take(10)->get();

// Paginación
$posts = Post::paginate(15);
```

**Update - Actualizar registros:**

```php
// Opción 1: Find + Update
$post = Post::find(1);
$post->title = 'Título Actualizado';
$post->save();

// Opción 2: Update directo
$post = Post::find(1);
$post->update([
    'title' => 'Título Actualizado',
    'published' => true,
]);

// Opción 3: Update masivo (sin instanciar)
Post::where('published', false)
    ->update(['published' => true]);
```

**Delete - Eliminar registros:**

```php
// Opción 1: Find + Delete
$post = Post::find(1);
$post->delete();

// Opción 2: Delete directo por ID
Post::destroy(1);

// Opción 3: Delete múltiples
Post::destroy([1, 2, 3]);

// Opción 4: Delete condicional
Post::where('views', '<', 10)->delete();
```

### Mass Assignment Protection

Por seguridad, Laravel protege contra **mass assignment** (asignación masiva). Debes especificar qué campos son "llenables":

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $fillable = [
        'title',
        'content',
        'published',
    ];
}
```

O especificar qué campos NO son llenables:

```php
protected $guarded = ['id', 'user_id'];
```

> **¿Por qué mass assignment protection?** Imagina un usuario malicioso enviando `is_admin=1` en un formulario. Sin protección, podría hacerse admin!

### Relationships: Conectando Models

Una de las features más poderosas de Eloquent son las **relaciones**.

**Ejemplo: Un User tiene muchos Posts (One-to-Many)**

**1. Agrega foreign key en la migration:**

```php
Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->onDelete('cascade');
    $table->string('title');
    $table->text('content');
    $table->timestamps();
});
```

**2. Define la relación en el Model User:**

```php
class User extends Model
{
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}
```

**3. Define la relación inversa en Post:**

```php
class Post extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
```

**4. Usa las relaciones:**

```php
// Obtener posts de un usuario
$user = User::find(1);
$posts = $user->posts; // Collection de Posts

// Obtener el autor de un post
$post = Post::find(1);
$author = $post->user; // User model
```

### El Problema N+1 y Eager Loading

Sin **eager loading**, Eloquent ejecuta 1 query por cada relación:

```php
// ❌ N+1 Problem - 1 query + N queries
$posts = Post::all(); // 1 query

foreach ($posts as $post) {
    echo $post->user->name; // 1 query por post!
}
// Total: 1 + 10 = 11 queries para 10 posts
```

**Solución: usa `with()` para eager loading:**

```php
// ✅ Eager Loading - Solo 2 queries
$posts = Post::with('user')->get(); // 2 queries total

foreach ($posts as $post) {
    echo $post->user->name; // Sin queries adicionales
}
```

> **El problema N+1 es una de las causas principales de lentitud en aplicaciones Laravel.** Siempre usa eager loading cuando accedas a relaciones en loops.

### UUIDv7 en Laravel 13

**Novedad en Laravel 13:** Ahora puedes usar UUIDs versión 7 como primary keys. Los UUIDv7 son time-ordered (ordenados por tiempo), lo que mejora el performance en bases de datos.

```php
use Illuminate\Database\Eloquent\Concerns\HasUuids;

class Post extends Model
{
    use HasUuids;  // Usa UUIDv7 automáticamente

    // No necesitas definir $keyType, Laravel lo detecta
}
```

Y en tu migration:

```php
Schema::create('posts', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->string('title');
    // ...
});
```

### Casting: Transformación Automática

Eloquent puede convertir automáticamente tipos de datos:

```php
class Post extends Model
{
    protected $casts = [
        'published_at' => 'datetime',  // String → Carbon instance
        'settings' => 'array',          // JSON → PHP array
        'published' => 'boolean',       // 0/1 → true/false
        'views' => 'integer',
    ];
}
```

Ahora puedes usar:

```php
$post->published_at->format('Y-m-d'); // Carbon methods
$post->settings['theme']; // Array access
```

### Accessors & Mutators (Breve)

Desde Laravel 9 existe una sintaxis moderna usando `Attribute::make()`. En Laravel 13 es la forma recomendada:

```php
use Illuminate\Database\Eloquent\Casts\Attribute;

// Accessor + Mutator combinados en un método
protected function title(): Attribute
{
    return Attribute::make(
        get: fn (string $value) => strtoupper($value),
        set: fn (string $value) => trim(strip_tags($value)),
    );
}

echo $post->title; // "MI PRIMER POST"
$post->title = '<h1>  Mi Post  </h1>'; // Guarda "Mi Post"
```

La sintaxis antigua (`getTitleAttribute` / `setTitleAttribute`) sigue funcionando en Laravel 13 pero es considerada legacy:

```php
// Forma legacy (todavía válida pero no recomendada)
public function getTitleAttribute($value)
{
    return strtoupper($value);
}
```

### Siguiente Paso

Ahora que dominas Models, Migrations y Eloquent, es momento de poner todo en práctica. En el próximo post crearemos un **Blog completo desde cero**, usando todo lo aprendido: rutas, controllers, vistas, models y relaciones.

## Preguntas Frecuentes

### ¿Qué es Eloquent ORM?

Eloquent es el ORM (Object-Relational Mapping) de Laravel que te permite trabajar con la base de datos usando objetos PHP en lugar de escribir SQL directamente. Por ejemplo: `Post::all()` en lugar de `SELECT * FROM posts`. Previene SQL injection automáticamente y hace el código más legible.

### ¿Cómo creo un Model en Laravel?

Usa `php artisan make:model Post -m`. El flag `-m` crea automáticamente una migration asociada. El model Post (singular, PascalCase) se asocia automáticamente con la tabla `posts` (plural, snake_case) por convención.

### ¿Qué son las Migrations?

Las migrations son como "control de versiones" para tu base de datos. Cada migration define cómo crear o modificar tablas. Ejecutas `php artisan migrate` para aplicar los cambios. Nunca modifiques migrations ya ejecutadas en producción - siempre crea una nueva migration para cambios.

### ¿Qué es Mass Assignment Protection?

Es una protección de seguridad contra asignación masiva. Debes especificar qué campos son "llenables" con `protected $fillable = ['title', 'content']` o qué NO son llenables con `protected $guarded = ['id']`. Esto previene que usuarios maliciosos envíen campos como `is_admin=1` en formularios.

### ¿Qué es el problema N+1 y cómo se soluciona?

El problema N+1 ocurre cuando haces 1 query + N queries adicionales en un loop. Ejemplo: `Post::all()` (1 query) + `$post->user->name` dentro del loop (N queries). **Solución:** Usar eager loading con `Post::with('user')->get()` - solo 2 queries total en lugar de N+1.

### ¿Qué son los UUIDs en Laravel 13?

Laravel 13 soporta UUIDs versión 7 como primary keys. Los UUIDv7 son **time-ordered** (ordenados por tiempo), lo que mejora el performance en bases de datos. Usa `use HasUuids;` en tu model y `$table->uuid('id')->primary()` en la migration.

### Recursos Adicionales

- [Documentación de Eloquent](https://laravel.com/docs/12.x/eloquent)
- [Migrations](https://laravel.com/docs/12.x/migrations)
- [Eloquent Relationships](https://laravel.com/docs/12.x/eloquent-relationships)
- [UUIDs en Laravel 13](https://laravel.com/docs/12.x/eloquent#uuid-and-ulid-keys)

### Video de la lección

Ver video tutorial: [Aprende Laravel - Models y Database](https://www.youtube.com/watch?v=8Fv2BNGLw_8)

Playlist completa en YouTube: [Aprende Laravel @ YouTube](https://www.youtube.com/playlist?list=PLPFfjDS32gikCkR3s7pLN40MJuSlOFu6h)

---

## Sitemap

Índice completo del sitio: [/sitemap.md](https://angelcruz.dev/sitemap.md)

Canónico HTML: [https://angelcruz.dev/post/aprende-laravel-models-database](https://angelcruz.dev/post/aprende-laravel-models-database)
