Saltar al contenido principal

Qué es una relación

Las tablas de una base de datos suelen estar relacionadas entre sí. Por ejemplo, un post de un blog puede tener múltiples comentarios, o un pedido está asociado al usuario que lo realizó. Eloquent facilita la definición y la manipulación de esas relaciones entre tablas. Cada relación se define como un método del modelo Eloquent.
Esta página utiliza modelos concretos como User, Post, Comment y Tag a modo de ejemplo. Se asume que dichos modelos y sus tablas ya están creados.
Las principales relaciones que soporta Eloquent son:
RelaciónDescripción
hasOneUno a uno (el padre tiene un único hijo)
belongsToInversa del uno a uno (el hijo pertenece al padre)
hasManyUno a muchos (el padre tiene varios hijos)
belongsToManyMuchos a muchos (usa una tabla intermedia)

hasOne (uno a uno)

hasOne representa una relación en la que un modelo tiene un único modelo relacionado. Por ejemplo, cuando un User tiene un Profile.

Definir la relación

Define el método profile en el modelo User.
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;

class User extends Model
{
    public function profile(): HasOne
    {
        return $this->hasOne(Profile::class);
    }
}
Eloquent infiere la clave foránea a partir del nombre del modelo padre. En este caso, asume que la tabla profiles contiene la columna user_id.

Obtener los datos relacionados

Puedes acceder a la relación como si fuera una propiedad.
$user = User::find(1);
$profile = $user->profile;
Al acceder al método de relación como si fuera una propiedad, Eloquent lanza automáticamente la consulta y te devuelve los datos relacionados. A esto se le llama «propiedad dinámica de relación».

belongsTo (inversa del uno a uno)

belongsTo es la relación inversa de hasOne y se define en el modelo que posee la clave foránea. Representa que Profile pertenece a User.

Definir la relación

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Profile extends Model
{
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}
Eloquent usa como clave foránea el nombre del método de relación con el sufijo _id (en este caso, user_id).

Obtener los datos relacionados

$profile = Profile::find(1);
$user = $profile->user;

echo $user->name;

hasMany (uno a muchos)

hasMany es la relación más habitual: un modelo padre tiene varios modelos hijos. Por ejemplo, un Post tiene varios Comment.

Definir la relación

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Post extends Model
{
    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class);
    }
}
Eloquent asume que la tabla comments contiene la columna post_id.

Obtener los datos relacionados

Una relación hasMany devuelve una colección.
$post = Post::find(1);

foreach ($post->comments as $comment) {
    echo $comment->body;
}
También puedes añadir condiciones a la consulta.
$recentComments = Post::find(1)
    ->comments()
    ->latest()
    ->take(5)
    ->get();

Relación inversa (belongsTo)

Para acceder al post desde un comentario, define belongsTo en el modelo Comment.
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Comment extends Model
{
    public function post(): BelongsTo
    {
        return $this->belongsTo(Post::class);
    }
}
$comment = Comment::find(1);
echo $comment->post->title;

belongsToMany (muchos a muchos)

Usa una relación muchos a muchos cuando ambos modelos pueden estar asociados a múltiples registros del otro lado. Por ejemplo, un Post tiene varios Tag y cada Tag pertenece a varios Post.

Estructura de tablas

Muchos a muchos requiere una tabla intermedia. Para Post y Tag se prepara la tabla pivote post_tag.
posts
    id - integer
    title - string

tags
    id - integer
    name - string

post_tag
    post_id - integer
    tag_id - integer
Eloquent infiere el nombre de la tabla pivote uniendo alfabéticamente los nombres de los dos modelos relacionados (post + tagpost_tag).

Definir la relación

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Post extends Model
{
    public function tags(): BelongsToMany
    {
        return $this->belongsToMany(Tag::class);
    }
}
Si defines lo mismo en el modelo Tag, puedes acceder a los datos en sentido inverso.
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Tag extends Model
{
    public function posts(): BelongsToMany
    {
        return $this->belongsToMany(Post::class);
    }
}

Obtener los datos relacionados

$post = Post::find(1);

foreach ($post->tags as $tag) {
    echo $tag->name;
}

Añadir y eliminar relaciones

attach añade un registro a la tabla pivote y detach lo elimina.
$post = Post::find(1);

// Añadir etiqueta
$post->tags()->attach($tagId);

// Eliminar etiqueta
$post->tags()->detach($tagId);

// Reemplazar todas las relaciones actuales
$post->tags()->sync([$tagId1, $tagId2]);
Con sync se eliminan automáticamente los registros de la tabla pivote que no estén en el array, dejando únicamente los IDs indicados como relaciones vigentes.

Eager loading

Qué es el problema N+1

Al acceder a una relación como propiedad, Eloquent lanza una consulta cada vez. A esto se le llama «lazy loading» (carga perezosa). Si accedes a una relación dentro de un bucle, se produce el problema N+1.
// 1 consulta para obtener todos los posts
$posts = Post::all();

foreach ($posts as $post) {
    // Por cada post se ejecuta una consulta para obtener el usuario (N veces)
    echo $post->user->name;
}
Con 100 posts se generarían 101 consultas en total, con el consiguiente impacto en el rendimiento.

Eager loading con with()

Con el método with() puedes obtener los datos relacionados de una sola vez, reduciendo el total a solo 2 consultas.
// Los posts y sus usuarios en 2 consultas
$posts = Post::with('user')->get();

foreach ($posts as $post) {
    // Ya no se lanzan consultas adicionales
    echo $post->user->name;
}
Solo se ejecutan estas 2 sentencias SQL:
select * from posts

select * from users where id in (1, 2, 3, ...)

Cargar varias relaciones a la vez

Puedes indicar varias relaciones mediante un array.
$posts = Post::with(['user', 'comments', 'tags'])->get();

Eager loading anidado

También puedes cargar relaciones anidadas con notación de punto.
// Cargamos los comentarios del post y el usuario de cada comentario
$posts = Post::with('comments.user')->get();
El eager loading es fundamental para evitar el problema N+1. Adopta el hábito de usar with() siempre que accedas a relaciones dentro de un bucle.

Próximos pasos

Introducción a Eloquent

Repasa las operaciones CRUD básicas de Eloquent.
Última modificación el 13 de julio de 2026