Lección 18 de 45 15 min de lectura

Modelos Eloquent

Eloquent es el ORM de Laravel que te permite trabajar con la base de datos usando clases PHP. Cada modelo representa una tabla y proporciona métodos intuitivos para consultar y manipular datos.

¿Qué es Eloquent?

Eloquent es un ORM (Object-Relational Mapping) que implementa el patrón Active Record. En lugar de escribir SQL directamente, trabajas con objetos PHP que representan filas de la base de datos.

Ventajas de usar Eloquent:

  • Sintaxis elegante: Código más legible que SQL puro
  • Independiente de la BD: Funciona con MySQL, PostgreSQL, SQLite, etc.
  • Relaciones integradas: Define relaciones entre tablas fácilmente
  • Protección automática: Previene inyección SQL

El modelo User

Laravel incluye un modelo User por defecto en app/Models/User.php:

php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    // El modelo asume que la tabla se llama 'users'
    // y la clave primaria es 'id'
}

Por convención, Eloquent asume que el modelo User corresponde a la tabla users (nombre en plural y minúsculas).

Crear un modelo

Para crear un nuevo modelo, usa el comando make:model:

bash
# Crear solo el modelo
php artisan make:model Post

# Crear modelo con migración
php artisan make:model Post -m

# Crear modelo con migración y controlador
php artisan make:model Post -mc

El modelo se crea en app/Models/Post.php:

php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    //
}

Convenciones de nombres

Eloquent sigue convenciones para reducir la configuración necesaria:

  • Tabla: Plural del modelo en snake_case (Postposts, BlogPostblog_posts)
  • Clave primaria: id
  • Timestamps: created_at y updated_at

Si tu tabla no sigue estas convenciones, puedes especificarlas:

php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    // Nombre de tabla personalizado
    protected $table = 'blog_articles';

    // Clave primaria diferente
    protected $primaryKey = 'post_id';

    // Desactivar timestamps
    public $timestamps = false;
}

Fillable y guarded

Por seguridad, Eloquent protege contra la asignación masiva. Debes especificar qué campos se pueden rellenar:

php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    // Campos que se pueden asignar masivamente
    protected $fillable = [
        'title',
        'slug',
        'content',
        'is_published',
    ];
}

Alternativamente, puedes usar $guarded para especificar los campos que NO se pueden asignar:

php
<?php

class Post extends Model
{
    // Solo 'id' está protegido, el resto se puede asignar
    protected $guarded = ['id'];
}
Nunca uses $guarded = []

Un array vacío en $guarded permite asignar cualquier campo, incluyendo id o is_admin. Esto es un riesgo de seguridad.

Casting de atributos

Los datos de la base de datos se devuelven como strings. Puedes convertirlos automáticamente a tipos PHP:

php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $fillable = [
        'title',
        'content',
        'is_published',
        'views',
        'published_at',
    ];

    protected function casts(): array
    {
        return [
            'is_published' => 'boolean',
            'views' => 'integer',
            'published_at' => 'datetime',
        ];
    }
}

El método casts() retorna un array que define las conversiones de tipo. Tipos de casting disponibles:

  • boolean - Convierte a true/false
  • integer, float - Números
  • string - Texto
  • array, json - Convierte JSON a array PHP
  • datetime, date - Objetos Carbon para fechas
  • decimal:2 - Decimal con precisión

Atributos ocultos

Cuando conviertes un modelo a JSON (por ejemplo, en una API), puedes ocultar campos sensibles:

php
<?php

class User extends Model
{
    // Campos ocultos en JSON
    protected $hidden = [
        'password',
        'remember_token',
    ];

    // O especifica solo los visibles
    protected $visible = [
        'id',
        'name',
        'email',
    ];
}

Accessors y mutators

Los accessors modifican el valor al leerlo. Los mutators lo modifican al guardarlo:

php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    // Accessor: modifica al leer
    protected function title(): Attribute
    {
        return Attribute::make(
            get: fn (string $value) => ucfirst($value),
        );
    }

    // Mutator: modifica al guardar
    protected function slug(): Attribute
    {
        return Attribute::make(
            set: fn (string $value) => strtolower(str_replace(' ', '-', $value)),
        );
    }

    // Accessor y mutator combinados
    protected function name(): Attribute
    {
        return Attribute::make(
            get: fn (string $value) => ucwords($value),
            set: fn (string $value) => strtolower($value),
        );
    }
}
Atributos virtuales

Puedes crear atributos que no existen en la base de datos. Por ejemplo, un full_name que combine first_name y last_name.

Ejemplo completo

Veamos un modelo completo para un blog:

php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    protected $fillable = [
        'user_id',
        'title',
        'slug',
        'excerpt',
        'content',
        'is_published',
        'published_at',
    ];

    protected $hidden = [
        'user_id',
    ];

    protected function casts(): array
    {
        return [
            'is_published' => 'boolean',
            'published_at' => 'datetime',
        ];
    }

    // Accessor para el título
    protected function title(): Attribute
    {
        return Attribute::make(
            get: fn (string $value) => ucfirst($value),
        );
    }

    // Atributo virtual: resumen automático
    protected function summary(): Attribute
    {
        return Attribute::make(
            get: fn () => $this->excerpt ?? substr($this->content, 0, 150) . '...',
        );
    }
}

Ejercicios

Ejercicio 1: Crear modelo Product

Crea un modelo Product con su migración. El modelo debe tener como fillable: name, sku, description, price, stock, is_active. Añade casts para price (decimal:2), stock (integer) e is_active (boolean).

Ver solución
# Crear modelo con migración
php artisan make:model Product -m
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $fillable = [
        'name',
        'sku',
        'description',
        'price',
        'stock',
        'is_active',
    ];

    protected function casts(): array
    {
        return [
            'price' => 'decimal:2',
            'stock' => 'integer',
            'is_active' => 'boolean',
        ];
    }
}

Ejercicio 2: Accessor para precio formateado

Añade al modelo Product un accessor llamado formatted_price que devuelva el precio con el símbolo del euro (ej: "29,99 EUR").

Ver solución
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $fillable = [
        'name',
        'sku',
        'description',
        'price',
        'stock',
        'is_active',
    ];

    protected function casts(): array
    {
        return [
            'price' => 'decimal:2',
            'stock' => 'integer',
            'is_active' => 'boolean',
        ];
    }

    protected function formattedPrice(): Attribute
    {
        return Attribute::make(
            get: fn () => number_format($this->price, 2, ',', '.') . ' EUR',
        );
    }
}
<?php

// Uso:
$product = Product::find(1);
echo $product->formatted_price; // "29,99 EUR"

Ejercicio 3: Modelo con tabla personalizada

Crea un modelo Category que use la tabla product_categories en lugar de categories. La tabla no tiene timestamps. Los campos fillable son: name, slug, description.

Ver solución
# Crear el modelo
php artisan make:model Category
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    protected $table = 'product_categories';

    public $timestamps = false;

    protected $fillable = [
        'name',
        'slug',
        'description',
    ];
}

Resumen

  • Eloquent es el ORM de Laravel que implementa Active Record
  • Los modelos se crean con php artisan make:model Nombre
  • Por convención, Post usa la tabla posts
  • $fillable define qué campos se pueden asignar masivamente
  • El método casts() convierte atributos a tipos PHP (boolean, integer, datetime...)
  • $hidden oculta campos sensibles al convertir a JSON
  • Los accessors modifican valores al leer, los mutators al guardar
  • Usa -m para crear modelo con migración, -mc para añadir controlador

¿Te está gustando el curso?

Tenemos cursos premium con proyectos reales, soporte personalizado y certificado.

Descubrir cursos premium