Eloquent ORM en PHP: modelos, relaciones hasMany/belongsTo y Query Builder

Eloquent es el ORM incluido en Laravel, pero puede usarse en cualquier proyecto PHP instalando el paquete illuminate/database. Ofrece una sintaxis fluida para definir modelos, relaciones y consultas sin necesidad del framework completo.

Instalación

composer require illuminate/database

Configurar Capsule Manager

Capsule es el punto de entrada de Eloquent fuera de Laravel:

<?php
require 'vendor/autoload.php';

use IlluminateDatabaseCapsuleManager as Capsule;

$capsule = new Capsule();
$capsule->addConnection([
    'driver'    => 'mysql',
    'host'      => 'localhost',
    'database'  => 'tienda',
    'username'  => 'root',
    'password'  => 'secret',
    'charset'   => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix'    => '',
]);
$capsule->setAsGlobal();
$capsule->bootEloquent();
?>

Definir un modelo

<?php
use IlluminateDatabaseEloquentModel;

class Producto extends Model
{
    protected $table      = 'productos';
    protected $primaryKey = 'id';
    public $timestamps    = true;   // created_at, updated_at

    protected $fillable = ['nombre', 'precio', 'stock', 'categoria_id'];
    protected $casts    = [
        'precio' => 'float',
        'stock'  => 'integer',
    ];
}
?>

CRUD básico

<?php
// Crear
$p = Producto::create(['nombre' => 'Teclado', 'precio' => 49.99, 'stock' => 10]);

// Leer
$p = Producto::find(1);
echo $p->nombre;

// Buscar con condición
$agotados = Producto::where('stock', 0)->get();

// Actualizar
Producto::where('id', 1)->update(['precio' => 44.99]);

// Eliminar
Producto::destroy(1);
?>

Relación hasMany

Un pedido tiene muchas líneas. En el modelo Pedido:

<?php
class Pedido extends Model
{
    protected $fillable = ['cliente_id', 'total'];

    public function lineas()
    {
        return $this->hasMany(LineaPedido::class, 'pedido_id');
    }
}
?>

Uso:

<?php
$pedido = Pedido::with('lineas')->find(5);
foreach ($pedido->lineas as $linea) {
    echo "{$linea->producto_nombre}: {$linea->cantidad}n";
}
?>

with('lineas') ejecuta un único JOIN en lugar de una query por línea (eager loading).

Relación belongsTo

Cada línea pertenece a un pedido:

<?php
class LineaPedido extends Model
{
    protected $table    = 'lineas_pedido';
    protected $fillable = ['pedido_id', 'producto_id', 'cantidad', 'precio_unitario'];

    public function pedido()
    {
        return $this->belongsTo(Pedido::class, 'pedido_id');
    }

    public function producto()
    {
        return $this->belongsTo(Producto::class, 'producto_id');
    }
}
?>

Relación belongsToMany (tabla pivot)

Un producto pertenece a muchas categorías y viceversa:

<?php
class Producto extends Model
{
    public function categorias()
    {
        return $this->belongsToMany(Categoria::class, 'producto_categoria', 'producto_id', 'categoria_id');
    }
}

class Categoria extends Model
{
    public function productos()
    {
        return $this->belongsToMany(Producto::class, 'producto_categoria');
    }
}

// Sincronizar categorías de un producto
$producto->categorias()->sync([1, 3, 7]);
?>

Query Builder

Eloquent expone el Query Builder de Laravel cuando no necesitas un modelo completo:

<?php
use IlluminateDatabaseCapsuleManager as DB;

$resultados = DB::table('productos')
    ->select('nombre', 'precio')
    ->where('stock', '>', 0)
    ->orderBy('precio', 'asc')
    ->limit(10)
    ->get();

foreach ($resultados as $fila) {
    echo "{$fila->nombre}: {$fila->precio}n";
}
?>

Migraciones sin Laravel

Capsule también puede crear tablas mediante el Schema Builder:

<?php
Capsule::schema()->create('productos', function ($table) {
    $table->id();
    $table->string('nombre');
    $table->decimal('precio', 10, 2);
    $table->unsignedInteger('stock')->default(0);
    $table->foreignId('categoria_id')->constrained();
    $table->timestamps();
});
?>

Errores comunes

  • MassAssignmentException: los campos que quieres crear/actualizar masivamente deben estar en $fillable o usar $guarded = [].
  • timestamps desactivados: si la tabla no tiene created_at/updated_at, añade public $timestamps = false;.
  • Nombre de tabla automático: Eloquent infiere el nombre en plural inglés. Si tu tabla se llama diferente, declara $table.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP