1. Eloquent ORM
  2. Eloquent: Relaciones

Introducción

Las tablas de bases de datos a menudo están relacionadas entre sí. Por ejemplo, una publicación de blog puede tener muchos comentarios o un pedido podría estar relacionado con el usuario que lo realizó. Eloquent facilita la gestión y el trabajo con estas relaciones y admite una variedad de relaciones comunes:

Definir Relaciones

Las relaciones Eloquent se definen como métodos en tus clases de modelo Eloquent. Dado que las relaciones también sirven como potentes constructores de consultas, definir relaciones como métodos proporciona capacidades poderosas de encadenamiento de métodos y consulta. Por ejemplo, podemos encadenar restricciones de consulta adicionales en esta relación posts:

$user->posts()->where('active', 1)->get();

Pero, antes de sumergirnos demasiado en el uso de relaciones, aprendamos a definir cada tipo de relación admitida por Eloquent.

Uno a Uno

Una relación de uno a uno es un tipo muy básico de relación de base de datos. Por ejemplo, un modelo User podría estar asociado con un modelo Phone. Para definir esta relación, colocaremos un método phone en el modelo User. El método phone debería llamar al método hasOne y devolver su resultado. El método hasOne está disponible en tu modelo a través de la clase base Illuminate\Database\Eloquent\Model del modelo:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;
 
class User extends Model
{
/**
* Obtener el teléfono asociado al usuario.
*/
public function phone(): HasOne
{
return $this->hasOne(Phone::class);
}
}

El primer argumento pasado al método hasOne es el nombre de la clase del modelo relacionado. Una vez que se define la relación, podemos recuperar el registro relacionado utilizando las propiedades dinámicas de Eloquent. Las propiedades dinámicas te permiten acceder a los métodos de relación como si fueran propiedades definidas en el modelo:

$phone = User::find(1)->phone;

Eloquent determina la clave externa de la relación basándose en el nombre del modelo principal. En este caso, se asume automáticamente que el modelo Phone tiene una clave externa user_id. Si deseas anular esta convención, puedes pasar un segundo argumento al método hasOne:

return $this->hasOne(Phone::class, 'foreign_key');

Además, Eloquent asume que la clave externa debe tener un valor que coincida con la columna de clave primaria del padre. En otras palabras, Eloquent buscará el valor de la columna id del usuario en la columna user_id del registro Phone. Si desea que la relación use un valor de clave primaria que no sea id o la propiedad $primaryKey de su modelo, puede pasar un tercer argumento al método hasOne:

return $this->hasOne(Phone::class, 'foreign_key', 'local_key');

Definir la Inversa de la Relación

Entonces, podemos acceder al modelo Phone desde nuestro modelo User. A continuación, definamos una relación en el modelo Phone que nos permitirá acceder al usuario que es dueño del teléfono. Podemos definir la inversa de una relación hasOne usando el método belongsTo:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Phone extends Model
{
/**
* Obtener al usuario dueño del teléfono.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}

Al invocar el método user, Eloquent intentará encontrar un modelo User que tenga un id que coincida con la columna user_id en el modelo Phone.

Eloquent determina el nombre de la clave externa examinando el nombre del método de relación y agregando el sufijo _id al nombre del método. Entonces, en este caso, Eloquent asume que el modelo Phone tiene una columna user_id. Sin embargo, si la clave externa en el modelo Phone no es user_id, puede pasar un nombre de clave personalizado como segundo argumento al método belongsTo:

/**
* Obtener al usuario dueño del teléfono.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'foreign_key');
}

Si el modelo principal no utiliza id como su clave primaria, o si desea encontrar el modelo asociado utilizando una columna diferente, puede pasar un tercer argumento al método belongsTo especificando la clave personalizada de la tabla principal:

/**
* Obtener al usuario dueño del teléfono.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'foreign_key', 'owner_key');
}

Uno a Muchos

Una relación uno a muchos se utiliza para definir relaciones en las que un solo modelo es el padre de uno o más modelos secundarios. Por ejemplo, una publicación de blog puede tener un número infinito de comentarios. Al igual que todas las demás relaciones de Eloquent, las relaciones uno a muchos se definen mediante la creación de un método en su modelo Eloquent:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
 
class Post extends Model
{
/**
* Obtener los comentarios para la publicación del blog.
*/
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
}

Recuerda, Eloquent determinará automáticamente la columna de clave foránea adecuada para el modelo Comment. Por convención, Eloquent tomará el nombre en "snake case" del modelo principal y lo sufijará con _id. Así que, en este ejemplo, Eloquent asumirá que la columna de clave foránea en el modelo Comment es post_id.

Una vez que se haya definido el método de relación, podemos acceder a la colección de comentarios relacionados accediendo a la propiedad comments. Recuerda, dado que Eloquent proporciona "propiedades de relación dinámicas", podemos acceder a los métodos de relación como si estuvieran definidos como propiedades en el modelo:

use App\Models\Post;
 
$comments = Post::find(1)->comments;
 
foreach ($comments as $comment) {
// ...
}

Dado que todas las relaciones también sirven como generadores de consultas, puede agregar más restricciones a la consulta de relación llamando al método comments y continuando encadenando condiciones en la consulta:

$comment = Post::find(1)->comments()
->where('title', 'foo')
->first();

Al igual que el método hasOne, también puedes anular los nombres de clave foránea y local pasando argumentos adicionales al método hasMany:

return $this->hasMany(Comment::class, 'foreign_key');
 
return $this->hasMany(Comment::class, 'foreign_key', 'local_key');

Uno a Muchos (Inversa) / Pertenece a

Ahora que podemos acceder a todos los comentarios de una publicación, definamos una relación que permita que un comentario acceda a su publicación principal. Para definir la inversa de una relación hasMany, define un método de relación en el modelo secundario que llame al método belongsTo:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Comment extends Model
{
/**
* Obtener la publicación que es dueña del comentario.
*/
public function post(): BelongsTo
{
return $this->belongsTo(Post::class);
}
}

Una vez que se haya definido la relación, podemos recuperar la publicación principal de un comentario accediendo a la "propiedad de relación dinámica" post:

use App\Models\Comment;
 
$comment = Comment::find(1);
 
return $comment->post->title;

En el ejemplo anterior, Eloquent intentará encontrar un modelo Post que tenga un id que coincida con la columna post_id en el modelo Comment.

Eloquent determina el nombre de la clave foránea predeterminado examinando el nombre del método de relación y añadiendo el nombre del método como sufijo con un _ seguido del nombre de la columna de clave primaria del modelo principal. Entonces, en este ejemplo, Eloquent asumirá que la clave foránea del modelo Post en la tabla comments es post_id.

Sin embargo, si la clave foránea de tu relación no sigue estas convenciones, puedes pasar un nombre de clave foránea personalizado como segundo argumento al método belongsTo:

/**
* Obtener la publicación que es dueña del comentario.
*/
public function post(): BelongsTo
{
return $this->belongsTo(Post::class, 'foreign_key');
}

Si tu modelo principal no utiliza id como su clave primaria, o deseas encontrar el modelo asociado usando una columna diferente, puedes pasar un tercer argumento al método belongsTo especificando la clave personalizada de la tabla principal:

/**
* Obtener la publicación que es dueña del comentario.
*/
public function post(): BelongsTo
{
return $this->belongsTo(Post::class, 'foreign_key', 'owner_key');
}

Modelos Predeterminados

Las relaciones belongsTo, hasOne, hasOneThrough y morphOne te permiten definir un modelo predeterminado que se devolverá si la relación dada es null. Este patrón se conoce comúnmente como el patrón de objeto nulo y puede ayudar a eliminar comprobaciones condicionales en tu código. En el siguiente ejemplo, la relación user devolverá un modelo App\Models\User vacío si no hay ningún usuario adjunto al modelo Post:

/**
* Obtener al autor de la publicación.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class)->withDefault();
}

Para poblar el modelo predeterminado con atributos, puedes pasar un array o cierre al método withDefault:

/**
* Obtener al autor de la publicación.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class)->withDefault([
'name' => 'Guest Author',
]);
}
 
/**
* Obtener al autor de la publicación.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class)->withDefault(function (User $user, Post $post) {
$user->name = 'Guest Author';
});
}

Consultar Relaciones "Belongs To"

Cuando consultas los hijos de una relación "belongs to", puedes construir manualmente la cláusula where para recuperar los modelos Eloquent correspondientes:

use App\Models\Post;
 
$posts = Post::where('user_id', $user->id)->get();

Sin embargo, puede resultar más conveniente usar el método whereBelongsTo, que determinará automáticamente la relación y la clave foránea adecuadas para el modelo proporcionado:

$posts = Post::whereBelongsTo($user)->get();

También puedes proporcionar una instancia de colección al método whereBelongsTo. Al hacerlo, Laravel recuperará modelos que pertenecen a cualquiera de los modelos principales dentro de la colección:

$users = User::where('vip', true)->get();
 
$posts = Post::whereBelongsTo($users)->get();

De forma predeterminada, Laravel determinará la relación asociada con el modelo proporcionado según el nombre de la clase del modelo; sin embargo, puedes especificar manualmente el nombre de la relación proporcionándolo como segundo argumento al método whereBelongsTo:

$posts = Post::whereBelongsTo($user, 'author')->get();

Has One Of Many

A veces, un modelo puede tener muchos modelos relacionados, pero deseas recuperar fácilmente el modelo relacionado "más reciente" o "más antiguo". Por ejemplo, un modelo User puede estar relacionado con muchos modelos Order, pero deseas definir una manera conveniente de interactuar con el pedido más reciente que el usuario ha realizado. Puedes lograr esto usando el tipo de relación hasOne combinado con los métodos ofMany:

/**
* Obtener el pedido más reciente del usuario.
*/
public function latestOrder(): HasOne
{
return $this->hasOne(Order::class)->latestOfMany();
}

Del mismo modo, puedes definir un método para recuperar el modelo relacionado "más antiguo", o primero, de una relación:

/**
* Obtener el pedido más antiguo del usuario.
*/
public function oldestOrder(): HasOne
{
return $this->hasOne(Order::class)->oldestOfMany();
}

De forma predeterminada, los métodos latestOfMany y oldestOfMany recuperarán el modelo relacionado más reciente o más antiguo según la clave primaria del modelo, que debe ser ordenable. Sin embargo, a veces puedes desear recuperar un solo modelo de una relación más grande utilizando un criterio de ordenación diferente.

Por ejemplo, usando el método ofMany, puedes recuperar el pedido más caro del usuario. El método ofMany acepta la columna ordenable como su primer argumento y qué función de agregado (min o max) aplicar al consultar el modelo relacionado:

/**
* Obtener el pedido más grande del usuario.
*/
public function largestOrder(): HasOne
{
return $this->hasOne(Order::class)->ofMany('price', 'max');
}

Advertencia Debido a que PostgreSQL no admite la ejecución de la función MAX contra columnas UUID, actualmente no es posible usar relaciones de uno entre muchos en combinación con columnas UUID de PostgreSQL.

Convertir Relaciones "Muchos" a Relaciones "Has One"

A menudo, al recuperar un solo modelo usando los métodos latestOfMany, oldestOfMany o ofMany, ya tienes una relación "has many" definida para el mismo modelo. Por conveniencia, Laravel te permite convertir fácilmente esta relación en una relación "has one" invocando el método one en la relación:

/**
* Obtener los pedidos del usuario.
*/
public function orders(): HasMany
{
return $this->hasMany(Order::class);
}
 
/**
* Obtener el pedido más grande del usuario.
*/
public function largestOrder(): HasOne
{
return $this->orders()->one()->ofMany('price', 'max');
}

Relaciones "Has One Of Many" Avanzadas

Es posible construir relaciones "has one of many" más avanzadas. Por ejemplo, un modelo Product puede tener muchos modelos Price asociados que se retienen en el sistema incluso después de que se publique un nuevo precio. Además, los nuevos datos de precios para el producto pueden publicarse con anticipación para que surtan efecto en una fecha futura a través de una columna published_at.

Entonces, en resumen, necesitamos recuperar la última tarificación publicada donde la fecha de publicación no esté en el futuro. Además, si dos precios tienen la misma fecha de publicación, preferiremos el precio con el mayor ID. Para lograr esto, debemos pasar un array al método ofMany que contenga las columnas ordenables que determinan el último precio. Además, se proporcionará un cierre como segundo argumento al método ofMany. Este cierre será responsable de agregar restricciones adicionales de fecha de publicación a la consulta de la relación:

/**
* Obtener los precios actuales del producto.
*/
public function currentPricing(): HasOne
{
return $this->hasOne(Price::class)->ofMany([
'published_at' => 'max',
'id' => 'max',
], function (Builder $query) {
$query->where('published_at', '<', now());
});
}

Has One Through

La relación "has-one-through" define una relación uno a uno con otro modelo. Sin embargo, esta relación indica que el modelo declarante se puede asociar con una instancia de otro modelo al proceder a través de un tercer modelo.

Por ejemplo, en una aplicación de taller de reparación de vehículos, cada modelo Mechanic puede estar asociado con un modelo Car, y cada modelo Car puede estar asociado con un modelo Owner. Mientras que el mecánico y el propietario no tienen una relación directa en la base de datos, el mecánico puede acceder al propietario a través del modelo Car. Veamos las tablas necesarias para definir esta relación:

mechanics
id - integer
name - string
 
cars
id - integer
model - string
mechanic_id - integer
 
owners
id - integer
name - string
car_id - integer

Ahora que hemos examinado la estructura de la tabla para la relación, definamos la relación en el modelo Mechanic:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
 
class Mechanic extends Model
{
/**
* Obtener al dueño del automóvil.
*/
public function carOwner(): HasOneThrough
{
return $this->hasOneThrough(Owner::class, Car::class);
}
}

El primer argumento pasado al método hasOneThrough es el nombre del modelo final al que deseamos acceder, mientras que el segundo argumento es el nombre del modelo intermedio.

O, si las relaciones relevantes ya se han definido en todos los modelos involucrados en la relación, puedes definir fluidamente una relación "has-one-through" invocando el método through y proporcionando los nombres de esas relaciones. Por ejemplo, si el modelo Mechanic tiene una relación cars y el modelo Car tiene una relación owner, puedes definir una relación "has-one-through" que conecte al mecánico y al propietario así:

// Sintaxis basada en cadena...
return $this->through('cars')->has('owner');
 
// Dynamic syntax...
return $this->throughCars()->hasOwner();

Convenciones de Claves

Se utilizarán las convenciones de clave foránea típicas de Eloquent al realizar las consultas de la relación. Si deseas personalizar las claves de la relación, puedes pasarlas como tercer y cuarto argumento al método hasOneThrough. El tercer argumento es el nombre de la clave foránea en el modelo intermedio. El cuarto argumento es el nombre de la clave foránea en el modelo final. El quinto argumento es la clave local, mientras que el sexto argumento es la clave local del modelo intermedio:

class Mechanic extends Model
{
/**
* Obtener al dueño del automóvil.
*/
public function carOwner(): HasOneThrough
{
return $this->hasOneThrough(
Owner::class,
Car::class,
'mechanic_id', // Foreign key on the cars table...
'car_id', // Foreign key on the owners table...
'id', // Local key on the mechanics table...
'id' // Local key on the cars table...
);
}
}

O, como se discutió anteriormente, si las relaciones relevantes ya se han definido en todos los modelos involucrados en la relación, puedes definir fluidamente una relación "has-one-through" invocando el método through y proporcionando los nombres de esas relaciones. Este enfoque ofrece la ventaja de reutilizar las convenciones de clave ya definidas en las relaciones existentes:

// Sintaxis basada en cadena...
return $this->through('cars')->has('owner');
 
// Dynamic syntax...
return $this->throughCars()->hasOwner();

Has Many Through

La relación "has-many-through" proporciona una manera conveniente de acceder a relaciones distantes a través de una relación intermedia. Por ejemplo, supongamos que estamos construyendo una plataforma de implementación como Laravel Vapor. Un modelo Project podría acceder a muchos modelos Deployment a través de un modelo intermedio Environment. Usando este ejemplo, podrías reunir fácilmente todas las implementaciones para un proyecto dado. Veamos las tablas necesarias para definir esta relación:

projects
id - integer
name - string
 
environments
id - integer
project_id - integer
name - string
 
deployments
id - integer
environment_id - integer
commit_hash - string

Ahora que hemos examinado la estructura de la tabla para la relación, definamos la relación en el modelo Project:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
 
class Project extends Model
{
/**
* Obtener todas las implementaciones para el proyecto.
*/
public function deployments(): HasManyThrough
{
return $this->hasManyThrough(Deployment::class, Environment::class);
}
}

El primer argumento pasado al método hasManyThrough es el nombre del modelo final al que deseamos acceder, mientras que el segundo argumento es el nombre del modelo intermedio:

O, si las relaciones relevantes ya han sido definidas en todos los modelos involucrados en la relación, puedes definir fluidamente una relación de "has-many-through" invocando el método through y suministrando los nombres de esas relaciones. Por ejemplo, si el modelo Project tiene una relación environments y el modelo Environment tiene una relación deployments, puedes definir una relación de "has-many-through" que conecta el proyecto y los despliegues de la siguiente manera:

// Sintaxis basada en cadena...
return $this->through('environments')->has('deployments');
 
// Dynamic syntax...
return $this->throughEnvironments()->hasDeployments();

Aunque la tabla del modelo Deployment no contiene una columna project_id, la relación hasManyThrough proporciona acceso a los despliegues de un proyecto mediante $project->deployments. Para recuperar estos modelos, Eloquent inspecciona la columna project_id en la tabla intermedia del modelo Environment. Después de encontrar los IDs de entornos relevantes, se utilizan para consultar la tabla del modelo Deployment.

Convenciones de Claves

Se utilizarán las convenciones típicas de clave externa de Eloquent al realizar consultas de la relación. Si deseas personalizar las claves de la relación, puedes pasarlas como tercer y cuarto argumento al método hasManyThrough. El tercer argumento es el nombre de la clave externa en el modelo intermedio. El cuarto argumento es el nombre de la clave externa en el modelo final. El quinto argumento es la clave local, mientras que el sexto argumento es la clave local del modelo intermedio:

class Project extends Model
{
public function deployments(): HasManyThrough
{
return $this->hasManyThrough(
Deployment::class,
Environment::class,
'project_id', // Foreign key on the environments table...
'environment_id', // Foreign key on the deployments table...
'id', // Local key on the projects table...
'id' // Local key on the environments table...
);
}
}

O, como se discutió anteriormente, si las relaciones relevantes ya han sido definidas en todos los modelos involucrados en la relación, puedes definir fluidamente una relación de "has-many-through" invocando el método through y suministrando los nombres de esas relaciones. Este enfoque ofrece la ventaja de reutilizar las convenciones de clave ya definidas en las relaciones existentes:

// Sintaxis basada en cadena...
return $this->through('environments')->has('deployments');
 
// Dynamic syntax...
return $this->throughEnvironments()->hasDeployments();

Relaciones Muchos a Muchos

Las relaciones muchos a muchos son ligeramente más complicadas que las relaciones hasOne y hasMany. Un ejemplo de una relación muchos a muchos es un usuario que tiene muchos roles y esos roles también son compartidos por otros usuarios en la aplicación. Por ejemplo, un usuario puede tener asignados los roles de "Autor" y "Editor"; sin embargo, esos roles también pueden asignarse a otros usuarios. Entonces, un usuario tiene muchos roles y un rol tiene muchos usuarios.

Estructura de la Tabla

Para definir esta relación, se necesitan tres tablas de base de datos: users, roles y role_user. La tabla role_user se deriva del orden alfabético de los nombres de modelos relacionados y contiene las columnas user_id e role_id. Esta tabla se utiliza como tabla intermedia que vincula a los usuarios y roles.

Recuerda, dado que un rol puede pertenecer a muchos usuarios, no podemos simplemente colocar una columna user_id en la tabla roles. Esto significaría que un rol solo podría pertenecer a un solo usuario. Para proporcionar soporte para que los roles se asignen a varios usuarios, se necesita la tabla role_user. Podemos resumir la estructura de la tabla de la relación de la siguiente manera:

users
id - integer
name - string
 
roles
id - integer
name - string
 
role_user
user_id - integer
role_id - integer

Estructura del Modelo

Las relaciones muchos a muchos se definen escribiendo un método que devuelve el resultado del método belongsToMany. El método belongsToMany es proporcionado por la clase base Illuminate\Database\Eloquent\Model que es utilizada por todos los modelos Eloquent de tu aplicación. Por ejemplo, definamos un método roles en nuestro modelo User. El primer argumento pasado a este método es el nombre de la clase del modelo relacionado:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
 
class User extends Model
{
/**
* Los roles a los que pertenece el usuario.
*/
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class);
}
}

Una vez que la relación está definida, puedes acceder a los roles del usuario usando la propiedad dinámica de relación roles:

use App\Models\User;
 
$user = User::find(1);
 
foreach ($user->roles as $role) {
// ...
}

Dado que todas las relaciones también sirven como constructores de consultas, puedes agregar restricciones adicionales a la consulta de la relación llamando al método roles y continuando encadenando condiciones en la consulta:

$roles = User::find(1)->roles()->orderBy('name')->get();

Para determinar el nombre de la tabla de la tabla intermedia de la relación, Eloquent unirá los dos nombres de modelo relacionados en orden alfabético. Sin embargo, eres libre de anular esta convención. Puedes hacerlo pasando un segundo argumento al método belongsToMany:

return $this->belongsToMany(Role::class, 'role_user');

Además de personalizar el nombre de la tabla intermedia, también puedes personalizar los nombres de las columnas de las claves en la tabla pasando argumentos adicionales al método belongsToMany. El tercer argumento es el nombre de la clave externa del modelo en el que estás definiendo la relación, mientras que el cuarto argumento es el nombre de la clave externa del modelo al que te estás uniendo:

return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id');

Definir la Inversa de la Relación

Para definir la "inversa" de una relación muchos a muchos, debes definir un método en el modelo relacionado que también devuelva el resultado del método belongsToMany. Para completar nuestro ejemplo de usuario/rol, definamos el método users en el modelo Role:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
 
class Role extends Model
{
/**
* Los usuarios que pertenecen al rol.
*/
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
}

Como puedes ver, la relación se define exactamente de la misma manera que su contraparte del modelo User, con la excepción de referenciar al modelo App\Models\User. Dado que estamos reutilizando el método belongsToMany, todas las opciones habituales de personalización de tabla y clave están disponibles al definir la "inversa" de relaciones muchos a muchos.

Recuperar Columnas de la Tabla Intermedia

Como ya has aprendido, trabajar con relaciones muchos a muchos requiere la presencia de una tabla intermedia. Eloquent proporciona formas muy útiles de interactuar con esta tabla. Por ejemplo, supongamos que nuestro modelo User tiene muchos modelos Role a los que está relacionado. Después de acceder a esta relación, podemos acceder a la tabla intermedia usando el atributo pivot en los modelos:

use App\Models\User;
 
$user = User::find(1);
 
foreach ($user->roles as $role) {
echo $role->pivot->created_at;
}

Observa que cada modelo Role que recuperamos se le asigna automáticamente un atributo pivot. Este atributo contiene un modelo que representa la tabla intermedia.

Por defecto, solo las claves de modelo estarán presentes en el modelo pivot. Si tu tabla intermedia contiene atributos adicionales, debes especificarlos al definir la relación:

return $this->belongsToMany(Role::class)->withPivot('active', 'created_by');

Como se señaló anteriormente, se pueden acceder a los atributos de la tabla intermedia en los modelos a través del atributo pivot. Sin embargo, eres libre de personalizar el nombre de este atributo para reflejar mejor su propósito dentro de tu aplicación.

return $this->belongsToMany(Role::class)->withTimestamps();

Advertencia Las tablas intermedias que utilizan las marcas de tiempo mantenidas automáticamente por Eloquent deben tener columnas de marcas de tiempo created_at y updated_at.

Personalizar el Nombre del Atributo pivot

Por ejemplo, si tu aplicación contiene usuarios que pueden suscribirse a podcasts, es probable que tengas una relación de muchos a muchos entre usuarios y podcasts. Si este es el caso, es posible que desees cambiar el nombre del atributo de la tabla intermedia a subscription en lugar de pivot. Esto se puede hacer utilizando el método as al definir la relación:

Una vez que se ha especificado el atributo personalizado de la tabla intermedia, puedes acceder a los datos de la tabla intermedia utilizando el nombre personalizado:

return $this->belongsToMany(Podcast::class)
->as('subscription')
->withTimestamps();

Una vez que se ha especificado el atributo personalizado de la tabla intermedia, puedes acceder a los datos de la tabla intermedia utilizando el nombre personalizado:

$users = User::with('podcasts')->get();
 
foreach ($users->flatMap->podcasts as $podcast) {
echo $podcast->subscription->created_at;
}

Filtrar Consultas a Través de Columnas de la Tabla Intermedia

También puedes filtrar los resultados devueltos por las consultas de relaciones belongsToMany utilizando los métodos wherePivot, wherePivotIn, wherePivotNotIn, wherePivotBetween, wherePivotNotBetween, wherePivotNull, y wherePivotNotNull al definir la relación:

return $this->belongsToMany(Role::class)
->wherePivot('approved', 1);
 
return $this->belongsToMany(Role::class)
->wherePivotIn('priority', [1, 2]);
 
return $this->belongsToMany(Role::class)
->wherePivotNotIn('priority', [1, 2]);
 
return $this->belongsToMany(Podcast::class)
->as('subscriptions')
->wherePivotBetween('created_at', ['2020-01-01 00:00:00', '2020-12-31 00:00:00']);
 
return $this->belongsToMany(Podcast::class)
->as('subscriptions')
->wherePivotNotBetween('created_at', ['2020-01-01 00:00:00', '2020-12-31 00:00:00']);
 
return $this->belongsToMany(Podcast::class)
->as('subscriptions')
->wherePivotNull('expired_at');
 
return $this->belongsToMany(Podcast::class)
->as('subscriptions')
->wherePivotNotNull('expired_at');

Ordenar Consultas a Través de Columnas de la Tabla Intermedia

Puedes ordenar los resultados devueltos por consultas de relaciones belongsToMany utilizando el método orderByPivot. En el siguiente ejemplo, recuperaremos todas las últimas insignias para el usuario:

return $this->belongsToMany(Badge::class)
->where('rank', 'gold')
->orderByPivot('created_at', 'desc');

Definir Modelos Personalizados de Tabla Intermedia

Si deseas definir un modelo personalizado para representar la tabla intermedia de tu relación muchos a muchos, puedes llamar al método using al definir la relación. Los modelos pivote personalizados te dan la oportunidad de definir comportamientos adicionales en el modelo pivote, como métodos y conversiones.

Los modelos pivote personalizados para relaciones muchos a muchos deben extender la clase Illuminate\Database\Eloquent\Relations\Pivot, mientras que los modelos pivote polimórficos muchos a muchos deben extender la clase Illuminate\Database\Eloquent\Relations\MorphPivot. Por ejemplo, podemos definir un modelo Role que use un modelo pivote personalizado RoleUser:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
 
class Role extends Model
{
/**
* Los usuarios que pertenecen al rol.
*/
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class)->using(RoleUser::class);
}
}

Al definir el modelo RoleUser, debes extender la clase Illuminate\Database\Eloquent\Relations\Pivot:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Relations\Pivot;
 
class RoleUser extends Pivot
{
// ...
}

Advertencia Los modelos pivote no pueden usar el rasgo SoftDeletes. Si necesitas eliminar suavemente registros pivote, considera convertir tu modelo pivote en un modelo Eloquent real.

Modelos Pivot Personalizados e ID Incrementales

Si has definido una relación muchos a muchos que utiliza un modelo pivote personalizado y ese modelo pivote tiene una clave primaria de incremento automático, asegúrate de que tu clase de modelo pivote personalizado defina una propiedad incrementing que esté establecida en true.

/**
* Indica si los ID son autoincrementables.
*
* @var bool
*/
public $incrementing = true;

Relaciones Polimórficas

Una relación polimórfica permite que el modelo hijo pertenezca a más de un tipo de modelo utilizando una sola asociación. Por ejemplo, imagina que estás construyendo una aplicación que permite a los usuarios compartir entradas de blog y videos. En tal aplicación, un modelo Comment podría pertenecer tanto a los modelos Post como a Video.

Uno a Uno (Polimórfico)

Estructura de la Tabla

Una relación polimórfica de uno a uno es similar a una relación de uno a uno típica; sin embargo, el modelo hijo puede pertenecer a más de un tipo de modelo mediante una sola asociación. Por ejemplo, una entrada de blog Post y un User pueden compartir una relación polimórfica con un modelo Image. El uso de una relación polimórfica de uno a uno te permite tener una sola tabla de imágenes únicas que pueden estar asociadas con entradas y usuarios. Primero, examinemos la estructura de la tabla:

posts
id - integer
name - string
 
users
id - integer
name - string
 
images
id - integer
url - string
imageable_id - integer
imageable_type - string

Observa las columnas imageable_id e imageable_type en la tabla images. La columna imageable_id contendrá el valor de ID de la entrada o usuario, mientras que la columna imageable_type contendrá el nombre de clase del modelo principal. La columna imageable_type se utiliza en Eloquent para determinar qué "tipo" de modelo principal devolver al acceder a la relación imageable. En este caso, la columna contendría App\Models\Post o App\Models\User.

Estructura del Modelo

A continuación, examinemos las definiciones de modelos necesarias para construir esta relación:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
 
class Image extends Model
{
/**
* Obtener el modelo parent del modelo imageable (usuario o publicación).
*/
public function imageable(): MorphTo
{
return $this->morphTo();
}
}
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;
 
class Post extends Model
{
/**
* Obtener la imagen de la publicación.
*/
public function image(): MorphOne
{
return $this->morphOne(Image::class, 'imageable');
}
}
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;
 
class User extends Model
{
/**
* Obtener la imagen del usuario.
*/
public function image(): MorphOne
{
return $this->morphOne(Image::class, 'imageable');
}
}

Recuperar la Relación

Una vez que se han definido la tabla de la base de datos y los modelos, puedes acceder a las relaciones a través de tus modelos. Por ejemplo, para recuperar la imagen de una entrada, podemos acceder a la propiedad dinámica de relación image:

use App\Models\Post;
 
$post = Post::find(1);
 
$image = $post->image;

Puedes recuperar el modelo principal del modelo polimórfico accediendo al nombre del método que realiza la llamada a morphTo. En este caso, ese es el método imageable en el modelo Image. Entonces, accederemos a ese método como una propiedad dinámica de relación:

use App\Models\Image;
 
$image = Image::find(1);
 
$imageable = $image->imageable;

La relación imageable en el modelo Image devolverá una instancia de Post o User, según el tipo de modelo que sea propietario de la imagen.

Convenciones de Claves

Si es necesario, puedes especificar el nombre de las columnas "id" y "type" utilizadas por tu modelo hijo polimórfico. Si lo haces, asegúrate de pasar siempre el nombre de la relación como primer argumento al método morphTo. Normalmente, este valor debería coincidir con el nombre del método, por lo que puedes usar la constante __FUNCTION__ de PHP:

/**
* Obtener el modelo al que pertenece la imagen.
*/
public function imageable(): MorphTo
{
return $this->morphTo(__FUNCTION__, 'imageable_type', 'imageable_id');
}

Uno a Muchos (Polimórfico)

Estructura de la Tabla

Una relación polimórfica de uno a muchos es similar a una relación de uno a muchos típica; sin embargo, el modelo hijo puede pertenecer a más de un tipo de modelo mediante una sola asociación. Por ejemplo, imagina que los usuarios de tu aplicación pueden "comentar" en entradas y videos. Usando relaciones polimórficas, puedes usar una sola tabla comments para contener comentarios tanto para entradas como para videos. Primero, examinemos la estructura de la tabla necesaria para construir esta relación:

posts
id - integer
title - string
body - text
 
videos
id - integer
title - string
url - string
 
comments
id - integer
body - text
commentable_id - integer
commentable_type - string

Estructura del Modelo

A continuación, examinemos las definiciones de modelos necesarias para construir esta relación:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
 
class Comment extends Model
{
/**
* Obtener el modelo parent del modelo commentable (publicación o video).
*/
public function commentable(): MorphTo
{
return $this->morphTo();
}
}
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;
 
class Post extends Model
{
/**
* Obtener todos los comentarios de la publicación.
*/
public function comments(): MorphMany
{
return $this->morphMany(Comment::class, 'commentable');
}
}
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;
 
class Video extends Model
{
/**
* Obtener todos los comentarios del video.
*/
public function comments(): MorphMany
{
return $this->morphMany(Comment::class, 'commentable');
}
}

Recuperar la Relación

Una vez que se han definido la tabla de la base de datos y los modelos, puedes acceder a las relaciones a través de las propiedades dinámicas de relación de tu modelo. Por ejemplo, para acceder a todos los comentarios de una entrada, podemos usar la propiedad dinámica comments:

use App\Models\Post;
 
$post = Post::find(1);
 
foreach ($post->comments as $comment) {
// ...
}

También puede recuperar el padre de un modelo hijo polimórfico accediendo al nombre del método que realiza la llamada a morphTo. En este caso, ese es el método commentable en el modelo Comment. Entonces, accederemos a ese método como una propiedad de relación dinámica para acceder al modelo padre del comentario:

use App\Models\Comment;
 
$comment = Comment::find(1);
 
$commentable = $comment->commentable;

La relación commentable en el modelo Comment devolverá una instancia de Post o Video, según el tipo de modelo que sea el padre del comentario.

One Of Many (Polymorphic)

A veces, un modelo puede tener muchos modelos relacionados, pero desea recuperar fácilmente el modelo relacionado más "reciente" o "antiguo". Por ejemplo, un modelo User puede estar relacionado con muchos modelos Image, pero desea definir una forma conveniente de interactuar con la imagen más reciente que el usuario ha subido. Puede lograr esto utilizando el tipo de relación morphOne combinado con los métodos ofMany:

/**
* Obtener la imagen más reciente del usuario.
*/
public function latestImage(): MorphOne
{
return $this->morphOne(Image::class, 'imageable')->latestOfMany();
}

De manera similar, puede definir un método para recuperar el modelo relacionado más "antiguo" o primero de una relación:

/**
* Obtener la imagen más antigua del usuario.
*/
public function oldestImage(): MorphOne
{
return $this->morphOne(Image::class, 'imageable')->oldestOfMany();
}

De forma predeterminada, los métodos latestOfMany y oldestOfMany recuperarán el modelo relacionado más reciente o antiguo según la clave primaria del modelo, que debe ser ordenable. Sin embargo, a veces puede desear recuperar un solo modelo de una relación más grande utilizando un criterio de clasificación diferente.

Por ejemplo, utilizando el método ofMany, puede recuperar la imagen más "gustada" del usuario. El método ofMany acepta la columna ordenable como su primer argumento y la función de agregación (min o max) que se aplicará al consultar el modelo relacionado:

/**
* Obtener la imagen más popular del usuario.
*/
public function bestImage(): MorphOne
{
return $this->morphOne(Image::class, 'imageable')->ofMany('likes', 'max');
}

Nota Es posible construir relaciones "uno de muchos" más avanzadas. Para obtener más información, consulte la documentación de uno de muchos avanzado.

Muchos a Muchos (Polimórfico)

Estructura de la Tabla

Las relaciones polimórficas muchos a muchos son ligeramente más complicadas que las relaciones "morph one" y "morph many". Por ejemplo, un modelo Post y un modelo Video podrían compartir una relación polimórfica con un modelo Tag. El uso de una relación polimórfica muchos a muchos en esta situación permitiría que su aplicación tenga una sola tabla de etiquetas únicas que pueden estar asociadas con publicaciones o videos. Primero, examinemos la estructura de la tabla necesaria para construir esta relación:

posts
id - integer
name - string
 
videos
id - integer
name - string
 
tags
id - integer
name - string
 
taggables
tag_id - integer
taggable_id - integer
taggable_type - string

Nota Antes de adentrarse en relaciones polimórficas muchos a muchos, puede beneficiarse de leer la documentación sobre relaciones típicas muchos a muchos.

Estructura del Modelo

A continuación, estamos listos para definir las relaciones en los modelos. Los modelos Post y Video contendrán ambos un método tags que llamará al método morphToMany proporcionado por la clase base del modelo Eloquent.

El método morphToMany acepta el nombre del modelo relacionado, así como el "nombre de la relación". Según el nombre que asignamos a nuestra tabla intermedia y las claves que contiene, nos referiremos a la relación como "taggable":

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
 
class Post extends Model
{
/**
* Obtener todas las etiquetas para la publicación.
*/
public function tags(): MorphToMany
{
return $this->morphToMany(Tag::class, 'taggable');
}
}

Definir la Inversa de la Relación

A continuación, en el modelo Tag, debe definir un método para cada uno de sus posibles modelos padres. Entonces, en este ejemplo, definiremos un método posts y un método videos. Ambos de estos métodos deben devolver el resultado del método morphedByMany.

El método morphedByMany acepta el nombre del modelo relacionado, así como el "nombre de la relación". Según el nombre que asignamos a nuestra tabla intermedia y las claves que contiene, nos referiremos a la relación como "taggable":

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
 
class Tag extends Model
{
/**
* Obtener todas las publicaciones asignadas a esta etiqueta.
*/
public function posts(): MorphToMany
{
return $this->morphedByMany(Post::class, 'taggable');
}
 
/**
* Obtener todos los videos asignados a esta etiqueta.
*/
public function videos(): MorphToMany
{
return $this->morphedByMany(Video::class, 'taggable');
}
}

Recuperar la Relación

Una vez que se definan la tabla de la base de datos y los modelos, podrá acceder a las relaciones a través de sus modelos. Por ejemplo, para acceder a todas las etiquetas de una publicación, puede usar la propiedad de relación dinámica tags:

use App\Models\Post;
 
$post = Post::find(1);
 
foreach ($post->tags as $tag) {
// ...
}

Puede recuperar el padre de una relación polimórfica desde el modelo hijo polimórfico accediendo al nombre del método que realiza la llamada a morphedByMany. En este caso, esos son los métodos posts o videos en el modelo Tag:

use App\Models\Tag;
 
$tag = Tag::find(1);
 
foreach ($tag->posts as $post) {
// ...
}
 
foreach ($tag->videos as $video) {
// ...
}

Tipos Polimórficos Personalizados

De forma predeterminada, Laravel utilizará el nombre de clase completamente cualificado para almacenar el "tipo" del modelo relacionado. Por ejemplo, dado el ejemplo de relación uno a muchos anterior donde un modelo Comment puede pertenecer a un modelo Post o a un modelo Video, el tipo predeterminado commentable_type sería App\Models\Post o App\Models\Video, respectivamente. Sin embargo, es posible que desee desacoplar estos valores de la estructura interna de su aplicación.

Por ejemplo, en lugar de usar los nombres de modelo como el "tipo", podemos usar cadenas simples como post y video. Al hacerlo, los valores de la columna polimórfica "tipo" en nuestra base de datos seguirán siendo válidos incluso si los modelos se renombran:

use Illuminate\Database\Eloquent\Relations\Relation;
 
Relation::enforceMorphMap([
'post' => 'App\Models\Post',
'video' => 'App\Models\Video',
]);

Puede llamar al método enforceMorphMap en el método boot de su clase App\Providers\AppServiceProvider o crear un proveedor de servicios separado si lo desea.

Puede determinar el alias morfo de un modelo dado en tiempo de ejecución utilizando el método getMorphClass del modelo. A la inversa, puede determinar el nombre de clase completamente cualificado asociado con un alias morfo usando el método Relation::getMorphedModel:

use Illuminate\Database\Eloquent\Relations\Relation;
 
$alias = $post->getMorphClass();
 
$class = Relation::getMorphedModel($alias);

Advertencia Al agregar un "mapa de morfismo" a tu aplicación existente, cada valor de columna *_type morfable en tu base de datos que aún contenga una clase completamente calificada deberá convertirse a su nombre de "mapa".

Relaciones Dinámicas

Puede usar el método resolveRelationUsing para definir relaciones entre modelos Eloquent en tiempo de ejecución. Aunque generalmente no se recomienda para el desarrollo normal de la aplicación, esto puede ser útil ocasionalmente al desarrollar paquetes de Laravel.

El método resolveRelationUsing acepta el nombre de relación deseado como su primer argumento. El segundo argumento pasado al método debe ser un cierre que acepte la instancia del modelo y devuelva una definición de relación Eloquent válida. Por lo general, debe configurar relaciones dinámicas dentro del método boot de un proveedor de servicios:

use App\Models\Order;
use App\Models\Customer;
 
Order::resolveRelationUsing('customer', function (Order $orderModel) {
return $orderModel->belongsTo(Customer::class, 'customer_id');
});

Advertencia Al definir relaciones dinámicas, siempre proporciona argumentos de nombres de clave explícitos a los métodos de relación de Eloquent.

Consultar Relaciones

Dado que todas las relaciones Eloquent se definen mediante métodos, puede llamar a esos métodos para obtener una instancia de la relación sin ejecutar realmente una consulta para cargar los modelos relacionados. Además, todos los tipos de relaciones Eloquent también sirven como constructores de consultas, lo que le permite continuar encadenando restricciones en la consulta de relación antes de ejecutar finalmente la consulta SQL contra su base de datos.

Por ejemplo, imagine una aplicación de blog en la que un modelo User tiene muchos modelos Post asociados:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
 
class User extends Model
{
/**
* Obtener todas las publicaciones del usuario.
*/
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
}

Puede consultar la relación posts y agregar restricciones adicionales a la relación de la siguiente manera:

use App\Models\User;
 
$user = User::find(1);
 
$user->posts()->where('active', 1)->get();

Puede utilizar cualquiera de los métodos del constructor de consultas de Laravel en la relación, así que asegúrese de explorar la documentación del constructor de consultas para conocer todos los métodos que tiene disponibles.

Encadenar Cláusulas orWhere Después de Relaciones

Como se muestra en el ejemplo anterior, tiene libertad para agregar restricciones adicionales a las relaciones al consultarlas. Sin embargo, tenga cuidado al encadenar cláusulas orWhere en una relación, ya que las cláusulas orWhere se agruparán lógicamente al mismo nivel que la restricción de la relación:

$user->posts()
->where('active', 1)
->orWhere('votes', '>=', 100)
->get();

El ejemplo anterior generará el siguiente SQL. Como puede ver, la cláusula or instruye a la consulta a devolver cualquier publicación con más de 100 votos. La consulta ya no está limitada a un usuario específico:

select *
from posts
where user_id = ? and active = 1 or votes >= 100

En la mayoría de las situaciones, debería utilizar grupos lógicos para agrupar las comprobaciones condicionales entre paréntesis:

use Illuminate\Database\Eloquent\Builder;
 
$user->posts()
->where(function (Builder $query) {
return $query->where('active', 1)
->orWhere('votes', '>=', 100);
})
->get();

El ejemplo anterior producirá el siguiente SQL. Observe que el agrupamiento lógico ha agrupado correctamente las restricciones y la consulta sigue estando limitada a un usuario específico:

select *
from posts
where user_id = ? and (active = 1 or votes >= 100)

Métodos de Relación Vs. Propiedades Dinámicas

Si no necesita agregar restricciones adicionales a una consulta de relación Eloquent, puede acceder a la relación como si fuera una propiedad. Por ejemplo, continuando con nuestros modelos de ejemplo User y Post, podemos acceder a todas las publicaciones de un usuario de la siguiente manera:

use App\Models\User;
 
$user = User::find(1);
 
foreach ($user->posts as $post) {
// ...
}

Las propiedades de relación dinámica realizan una "carga perezosa", lo que significa que solo cargarán sus datos de relación cuando realmente las acceda. Debido a esto, los desarrolladores a menudo utilizan carga ansiosa para cargar previamente relaciones que saben que se accederán después de cargar el modelo. La carga ansiosa proporciona una reducción significativa en las consultas SQL que deben ejecutarse para cargar las relaciones de un modelo.

Consultar Existencia de Relaciones

Al recuperar registros de modelos, es posible que desee limitar los resultados en función de la existencia de una relación. Por ejemplo, imagine que desea recuperar todas las publicaciones de blog que tengan al menos un comentario. Para hacerlo, puede pasar el nombre de la relación a los métodos has y orHas:

use App\Models\Post;
 
// Obtener todas las publicaciones que tienen al menos un comentario...
$posts = Post::has('comments')->get();

También puede especificar un operador y un valor de recuento para personalizar aún más la consulta:

// Obtener todas las publicaciones que tienen tres o más comentarios...
$posts = Post::has('comments', '>=', 3)->get();

Las declaraciones has anidadas se pueden construir utilizando la notación "punto". Por ejemplo, puede recuperar todas las publicaciones que tengan al menos un comentario que tenga al menos una imagen:

// Obtener publicaciones que tienen al menos un comentario con imágenes...
$posts = Post::has('comments.images')->get();

Si necesita aún más potencia, puede utilizar los métodos whereHas y orWhereHas para definir restricciones de consulta adicionales en sus consultas has, como inspeccionar el contenido de un comentario:

use Illuminate\Database\Eloquent\Builder;
 
// Obtener publicaciones con al menos un comentario que contiene palabras como code%...
$posts = Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
})->get();
 
// Obtener publicaciones con al menos diez comentarios que contienen palabras como code%...
$posts = Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
}, '>=', 10)->get();

Advertencia Eloquent no admite actualmente la consulta de la existencia de relaciones entre bases de datos. Las relaciones deben existir dentro de la misma base de datos.

Consultas de Existencia de Relaciones en Línea

Si desea consultar la existencia de una relación con una única condición simple adjunta a la consulta de relación, puede resultar más conveniente utilizar los métodos whereRelation, orWhereRelation, whereMorphRelation y orWhereMorphRelation. Por ejemplo, podemos consultar todas las publicaciones que tienen comentarios no aprobados:

use App\Models\Post;
 
$posts = Post::whereRelation('comments', 'is_approved', false)->get();

Por supuesto, al igual que las llamadas al método where del constructor de consultas, también puede especificar un operador:

$posts = Post::whereRelation(
'comments', 'created_at', '>=', now()->subHour()
)->get();

Consultar Ausencia de Relaciones

Al recuperar registros de modelos, es posible que desee limitar los resultados en función de la ausencia de una relación. Por ejemplo, imagine que desea recuperar todas las publicaciones de blog que no tengan ningún comentario. Para hacerlo, puede pasar el nombre de la relación a los métodos doesntHave y orDoesntHave:

use App\Models\Post;
 
$posts = Post::doesntHave('comments')->get();

Si necesita aún más potencia, puede utilizar los métodos whereDoesntHave y orWhereDoesntHave para agregar restricciones de consulta adicionales a sus consultas doesntHave, como inspeccionar el contenido de un comentario:

use Illuminate\Database\Eloquent\Builder;
 
$posts = Post::whereDoesntHave('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
})->get();

Puede utilizar la notación "punto" para ejecutar una consulta contra una relación anidada. Por ejemplo, la siguiente consulta recuperará todas las publicaciones que no tengan comentarios; sin embargo, se incluirán en los resultados las publicaciones que tengan comentarios de autores que no están prohibidos:

use Illuminate\Database\Eloquent\Builder;
 
$posts = Post::whereDoesntHave('comments.author', function (Builder $query) {
$query->where('banned', 0);
})->get();

Consultar Relaciones morphTo

Para consultar la existencia de relaciones "morph to", puede utilizar los métodos whereHasMorph y whereDoesntHaveMorph. Estos métodos aceptan el nombre de la relación como su primer argumento. A continuación, los métodos aceptan los nombres de los modelos relacionados que desea incluir en la consulta. Finalmente, puede proporcionar un cierre que personalice la consulta de relación:

use App\Models\Comment;
use App\Models\Post;
use App\Models\Video;
use Illuminate\Database\Eloquent\Builder;
 
// Obtener comentarios asociados a publicaciones o videos con un título como code%...
$comments = Comment::whereHasMorph(
'commentable',
[Post::class, Video::class],
function (Builder $query) {
$query->where('title', 'like', 'code%');
}
)->get();
 
// Obtener comentarios asociados a publicaciones con un título que no sea como code%...
$comments = Comment::whereDoesntHaveMorph(
'commentable',
Post::class,
function (Builder $query) {
$query->where('title', 'like', 'code%');
}
)->get();

Ocasionalmente, puede que necesite agregar restricciones de consulta basadas en el "tipo" del modelo polimórfico relacionado. El cierre pasado al método whereHasMorph puede recibir un valor $type como su segundo argumento. Este argumento le permite inspeccionar el "tipo" de la consulta que se está construyendo:

use Illuminate\Database\Eloquent\Builder;
 
$comments = Comment::whereHasMorph(
'commentable',
[Post::class, Video::class],
function (Builder $query, string $type) {
$column = $type === Post::class ? 'content' : 'title';
 
$query->where($column, 'like', 'code%');
}
)->get();

Consultar Todos los Modelos Relacionados

En lugar de pasar un array de posibles modelos polimórficos, puede proporcionar * como un valor comodín. Esto indicará a Laravel que recupere todos los posibles tipos polimórficos de la base de datos. Laravel ejecutará una consulta adicional para realizar esta operación:

use Illuminate\Database\Eloquent\Builder;
 
$comments = Comment::whereHasMorph('commentable', '*', function (Builder $query) {
$query->where('title', 'like', 'foo%');
})->get();

Agregar Modelos Relacionados

Contar Modelos Relacionados

A veces, es posible que desee contar el número de modelos relacionados para una relación dada sin cargar realmente los modelos. Para lograr esto, puede utilizar el método withCount. El método withCount colocará un atributo {relación}_count en los modelos resultantes:

use App\Models\Post;
 
$posts = Post::withCount('comments')->get();
 
foreach ($posts as $post) {
echo $post->comments_count;
}

Al pasar un array al método withCount, puede agregar los "contadores" para múltiples relaciones, así como agregar restricciones adicionales a las consultas:

use Illuminate\Database\Eloquent\Builder;
 
$posts = Post::withCount(['votes', 'comments' => function (Builder $query) {
$query->where('content', 'like', 'code%');
}])->get();
 
echo $posts[0]->votes_count;
echo $posts[0]->comments_count;

También puede darle un alias al resultado del recuento de la relación, lo que permite realizar varios recuentos en la misma relación:

use Illuminate\Database\Eloquent\Builder;
 
$posts = Post::withCount([
'comments',
'comments as pending_comments_count' => function (Builder $query) {
$query->where('approved', false);
},
])->get();
 
echo $posts[0]->comments_count;
echo $posts[0]->pending_comments_count;

Carga Diferida de Conteo

Utilizando el método loadCount, puede cargar un recuento de relación después de que se haya recuperado el modelo principal:

$book = Book::first();
 
$book->loadCount('genres');

Si necesita establecer restricciones de consulta adicionales en la consulta de recuento, puede pasar un array indexado por las relaciones que desea contar. Los valores del array deben ser cierres que reciban la instancia del constructor de consultas:

$book->loadCount(['reviews' => function (Builder $query) {
$query->where('rating', 5);
}])

Conteo de Relaciones y Declaraciones de Selección Personalizadas

Si combina withCount con una declaración select, asegúrese de llamar a withCount después del método select:

$posts = Post::select(['title', 'body'])
->withCount('comments')
->get();

Otras Funciones Agregadas

Además del método withCount, Eloquent proporciona los métodos withMin, withMax, withAvg, withSum y withExists. Estos métodos colocarán un atributo {relación}_{función}_{columna} en los modelos resultantes:

use App\Models\Post;
 
$posts = Post::withSum('comments', 'votes')->get();
 
foreach ($posts as $post) {
echo $post->comments_sum_votes;
}

Si desea acceder al resultado de la función de agregación utilizando otro nombre, puede especificar su propio alias:

$posts = Post::withSum('comments as total_comments', 'votes')->get();
 
foreach ($posts as $post) {
echo $post->total_comments;
}

Al igual que el método loadCount, también están disponibles versiones diferidas de estos métodos. Estas operaciones de agregación adicionales se pueden realizar en modelos Eloquent que ya se han recuperado:

$post = Post::first();
 
$post->loadSum('comments', 'votes');

Si combina estos métodos de agregación con una declaración select, asegúrese de llamar a los métodos de agregación después del método select:

$posts = Post::select(['title', 'body'])
->withExists('comments')
->get();

Contar Modelos Relacionados en Relaciones morphTo

Si desea cargar ansiosamente una relación "morph to", así como los recuentos de modelos relacionados para las diversas entidades que pueden ser devueltas por esa relación, puede utilizar el método with en combinación con el método morphWithCount de la relación morphTo.

En este ejemplo, supongamos que los modelos Photo y Post pueden crear modelos ActivityFeed. Supondremos que el modelo ActivityFeed define una relación "morph to" llamada parentable que nos permite recuperar el modelo principal Photo o Post para una instancia dada de ActivityFeed. Además, supongamos que los modelos Photo "tienen muchos" modelos Tag y los modelos Post "tienen muchos" modelos Comment.

Ahora, imaginemos que queremos recuperar instancias de ActivityFeed y cargar ansiosamente los modelos principales parentable para cada instancia de ActivityFeed. Además, queremos recuperar el número de etiquetas asociadas con cada foto principal y el número de comentarios asociados con cada publicación principal:

use Illuminate\Database\Eloquent\Relations\MorphTo;
 
$activities = ActivityFeed::with([
'parentable' => function (MorphTo $morphTo) {
$morphTo->morphWithCount([
Photo::class => ['tags'],
Post::class => ['comments'],
]);
}])->get();

Carga Diferida de Conteo

Supongamos que ya hemos recuperado un conjunto de modelos ActivityFeed y ahora nos gustaría cargar los recuentos de relaciones anidadas para los diversos modelos parentable asociados con las actividades. Puede utilizar el método loadMorphCount para lograr esto:

$activities = ActivityFeed::with('parentable')->get();
 
$activities->loadMorphCount('parentable', [
Photo::class => ['tags'],
Post::class => ['comments'],
]);

Carga Diferida

Cuando se accede a las relaciones de Eloquent como propiedades, los modelos relacionados se cargan "perezosamente". Esto significa que los datos de la relación no se cargan realmente hasta que accede a la propiedad por primera vez. Sin embargo, Eloquent puede cargar ansiosamente las relaciones en el momento en que consulta el modelo principal. La carga ansiosa alivia el problema de la consulta "N + 1". Para ilustrar el problema de la consulta N + 1, considere un modelo Book que "pertenece a" a un modelo Author:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Book extends Model
{
/**
* Obtener al autor que escribió el libro.
*/
public function author(): BelongsTo
{
return $this->belongsTo(Author::class);
}
}

Ahora, recuperemos todos los libros y sus autores:

use App\Models\Book;
 
$books = Book::all();
 
foreach ($books as $book) {
echo $book->author->name;
}

Este bucle ejecutará una consulta para recuperar todos los libros dentro de la tabla de la base de datos y luego otra consulta para cada libro con el fin de recuperar el autor del libro. Entonces, si tenemos 25 libros, el código anterior ejecutaría 26 consultas: una para el libro original y 25 consultas adicionales para recuperar el autor de cada libro.

Afortunadamente, podemos usar la carga ansiosa para reducir esta operación a solo dos consultas. Al construir una consulta, puede especificar qué relaciones deben cargarse ansiosamente utilizando el método with:

$books = Book::with('author')->get();
 
foreach ($books as $book) {
echo $book->author->name;
}

Para esta operación, solo se ejecutarán dos consultas: una consulta para recuperar todos los libros y una consulta para recuperar todos los autores de todos los libros:

select * from books
 
select * from authors where id in (1, 2, 3, 4, 5, ...)

Carga Diferida de Múltiples Relaciones

A veces es posible que necesite cargar ansiosamente varias relaciones diferentes. Para hacerlo, simplemente pase un array de relaciones al método with:

$books = Book::with(['author', 'publisher'])->get();

Carga Diferida Anidada

Para cargar ansiosamente las relaciones de las relaciones de una relación, puede utilizar la notación "punto". Por ejemplo, carguemos ansiosamente todos los autores del libro y todos los contactos personales del autor:

$books = Book::with('author.contacts')->get();

Alternativamente, puede especificar relaciones anidadas cargadas ansiosamente proporcionando un array anidado al método with, lo cual puede ser conveniente al cargar varias relaciones anidadas:

$books = Book::with([
'author' => [
'contacts',
'publisher',
],
])->get();

Carga Diferida Anidada Relaciones morphTo

Si desea cargar ansiosamente una relación morphTo, así como relaciones anidadas en las diversas entidades que pueden ser devueltas por esa relación, puede utilizar el método with en combinación con el método morphWith de la relación morphTo. Para ilustrar este método, consideremos el siguiente modelo:

<?php
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
 
class ActivityFeed extends Model
{
/**
* Obtener el padre del registro de actividad del feed.
*/
public function parentable(): MorphTo
{
return $this->morphTo();
}
}

En este ejemplo, supongamos que los modelos Event, Photo y Post pueden crear modelos ActivityFeed. Además, supongamos que los modelos Event pertenecen a un modelo Calendar, los modelos Photo están asociados con modelos Tag y los modelos Post pertenecen a un modelo Author.

Utilizando estas definiciones y relaciones de modelos, podemos recuperar instancias del modelo ActivityFeed y cargar ansiosamente todos los modelos parentable y sus respectivas relaciones anidadas:

use Illuminate\Database\Eloquent\Relations\MorphTo;
 
$activities = ActivityFeed::query()
->with(['parentable' => function (MorphTo $morphTo) {
$morphTo->morphWith([
Event::class => ['calendar'],
Photo::class => ['tags'],
Post::class => ['author'],
]);
}])->get();

Carga Diferida de Columnas Específicas

No siempre necesitará todas las columnas de las relaciones que está recuperando. Por esta razón, Eloquent le permite especificar qué columnas de la relación le gustaría recuperar:

$books = Book::with('author:id,name,book_id')->get();

Advertencia Cuando uses esta función, siempre debes incluir la columna id y cualquier columna de clave externa relevante en la lista de columnas que deseas recuperar.

Carga Diferida por Defecto

A veces es posible que desee cargar siempre algunas relaciones al recuperar un modelo. Para lograr esto, puede definir una propiedad $with en el modelo:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Book extends Model
{
/**
* Las relaciones que siempre deben cargarse.
*
* @var array
*/
protected $with = ['author'];
 
/**
* Obtener al autor que escribió el libro.
*/
public function author(): BelongsTo
{
return $this->belongsTo(Author::class);
}
 
/**
* Obtener el género del libro.
*/
public function genre(): BelongsTo
{
return $this->belongsTo(Genre::class);
}
}

Si desea eliminar un elemento de la propiedad $with para una sola consulta, puede utilizar el método without:

$books = Book::without('author')->get();

Si desea anular todos los elementos dentro de la propiedad $with para una sola consulta, puede utilizar el método withOnly:

$books = Book::withOnly('genre')->get();

Restringir Cargas Diferidas

A veces, puede desear cargar ansiosamente una relación pero también especificar condiciones de consulta adicionales para la consulta de carga ansiosa. Puede lograr esto pasando un array de relaciones al método with, donde la clave del array es el nombre de la relación y el valor del array es un cierre que agrega condiciones adicionales a la consulta de carga ansiosa:

use App\Models\User;
use Illuminate\Contracts\Database\Eloquent\Builder;
 
$users = User::with(['posts' => function (Builder $query) {
$query->where('title', 'like', '%code%');
}])->get();

En este ejemplo, Eloquent solo cargará ansiosamente las publicaciones donde la columna title de la publicación contiene la palabra code. Puede llamar a otros métodos del generador de consultas para personalizar aún más la operación de carga ansiosa:

$users = User::with(['posts' => function (Builder $query) {
$query->orderBy('created_at', 'desc');
}])->get();

Advertencia Los métodos constructores de consultas limit y take no se pueden usar al restringir las cargas ansiosas.

Restringir Carga Diferida de Relaciones morphTo

Si está cargando ansiosamente una relación morphTo, Eloquent ejecutará varias consultas para recuperar cada tipo de modelo relacionado. Puede agregar condiciones adicionales a cada una de estas consultas usando el método constrain de la relación MorphTo:

use Illuminate\Database\Eloquent\Relations\MorphTo;
 
$comments = Comment::with(['commentable' => function (MorphTo $morphTo) {
$morphTo->constrain([
Post::class => function ($query) {
$query->whereNull('hidden_at');
},
Video::class => function ($query) {
$query->where('type', 'educational');
},
]);
}])->get();

En este ejemplo, Eloquent solo cargará ansiosamente las publicaciones que no hayan sido ocultas y los videos que tengan un valor type de "educativo".

Restringir Cargas Diferidas con Existencia de Relaciones

A veces puede ser necesario verificar la existencia de una relación al mismo tiempo que se carga la relación según las mismas condiciones. Por ejemplo, puede desear solo recuperar modelos User que tengan modelos secundarios Post que coincidan con una condición de consulta dada, al mismo tiempo que carga ansiosamente las publicaciones coincidentes. Puede lograr esto usando el método withWhereHas:

use App\Models\User;
 
$users = User::withWhereHas('posts', function ($query) {
$query->where('featured', true);
})->get();

Carga Perezosa

A veces puede ser necesario cargar ansiosamente una relación después de que el modelo principal ya ha sido recuperado. Por ejemplo, esto puede ser útil si necesita decidir dinámicamente si cargar o no modelos relacionados:

use App\Models\Book;
 
$books = Book::all();
 
if ($someCondition) {
$books->load('author', 'publisher');
}

Si necesita establecer condiciones de consulta adicionales en la consulta de carga ansiosa, puede pasar un array con claves de las relaciones que desea cargar. Los valores del array deben ser instancias de cierre que reciban la instancia de la consulta:

$author->load(['books' => function (Builder $query) {
$query->orderBy('published_date', 'asc');
}]);

Para cargar una relación solo cuando aún no se ha cargado, use el método loadMissing:

$book->loadMissing('author');

Carga Diferida Perezosa Anidada & morphTo

Si desea cargar ansiosamente una relación morphTo, así como relaciones anidadas en las diversas entidades que pueden ser devueltas por esa relación, puede usar el método loadMorph.

Este método acepta el nombre de la relación morphTo como su primer argumento y un array de pares modelo/relación como su segundo argumento. Para ilustrar este método, consideremos el siguiente modelo:

<?php
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
 
class ActivityFeed extends Model
{
/**
* Obtener el padre del registro de actividad del feed.
*/
public function parentable(): MorphTo
{
return $this->morphTo();
}
}

En este ejemplo, supongamos que los modelos Event, Photo y Post pueden crear modelos ActivityFeed. Además, supongamos que los modelos Event pertenecen a un modelo Calendar, los modelos Photo están asociados con modelos Tag y los modelos Post pertenecen a un modelo Author.

Utilizando estas definiciones y relaciones de modelos, podemos recuperar instancias del modelo ActivityFeed y cargar ansiosamente todos los modelos parentable y sus respectivas relaciones anidadas:

$activities = ActivityFeed::with('parentable')
->get()
->loadMorph('parentable', [
Event::class => ['calendar'],
Photo::class => ['tags'],
Post::class => ['author'],
]);

Evitar la Carga Perezosa

Como se mencionó anteriormente, la carga ansiosa de relaciones a menudo puede proporcionar beneficios significativos de rendimiento para su aplicación. Por lo tanto, si lo desea, puede indicar a Laravel que siempre evite la carga perezosa de relaciones. Para lograr esto, puede invocar el método preventLazyLoading ofrecido por la clase base del modelo Eloquent. Típicamente, debería llamar a este método dentro del método boot de la clase AppServiceProvider de su aplicación.

El método preventLazyLoading acepta un argumento booleano opcional que indica si se debe evitar la carga perezosa. Por ejemplo, es posible que desee desactivar la carga perezosa solo en entornos que no sean de producción para que su entorno de producción continúe funcionando normalmente incluso si hay una relación cargada perezosamente en el código de producción por accidente:

use Illuminate\Database\Eloquent\Model;
 
/**
* Inicializar todos los servicios de la aplicación.
*/
public function boot(): void
{
Model::preventLazyLoading(! $this->app->isProduction());
}

Después de evitar la carga perezosa, Eloquent lanzará una excepción Illuminate\Database\LazyLoadingViolationException cuando su aplicación intente cargar perezosamente cualquier relación de Eloquent.

Puede personalizar el comportamiento de las violaciones de carga perezosa utilizando el método handleLazyLoadingViolationsUsing. Por ejemplo, mediante este método, puede instruir a las violaciones de carga perezosa que solo se registren en lugar de interrumpir la ejecución de la aplicación con excepciones:

Model::handleLazyLoadingViolationUsing(function (Model $model, string $relation) {
$class = $model::class;
 
info("Attempted to lazy load [{$relation}] on model [{$class}].");
});

Insertar y Actualizar Modelos Relacionados

Método save

Eloquent proporciona métodos convenientes para agregar nuevos modelos a relaciones. Por ejemplo, tal vez necesitas agregar un nuevo comentario a una publicación. En lugar de establecer manualmente el atributo post_id en el modelo Comment, puedes insertar el comentario usando el método save de la relación:

use App\Models\Comment;
use App\Models\Post;
 
$comment = new Comment(['message' => 'A new comment.']);
 
$post = Post::find(1);
 
$post->comments()->save($comment);

Ten en cuenta que no accedimos a la relación comments como una propiedad dinámica. En su lugar, llamamos al método comments para obtener una instancia de la relación. El método save agregará automáticamente el valor post_id apropiado al nuevo modelo Comment.

Si necesitas guardar varios modelos relacionados, puedes usar el método saveMany:

$post = Post::find(1);
 
$post->comments()->saveMany([
new Comment(['message' => 'A new comment.']),
new Comment(['message' => 'Another new comment.']),
]);

Los métodos save y saveMany persistirán las instancias de modelos proporcionadas, pero no agregarán los modelos recién persistidos a ninguna relación en memoria que ya esté cargada en el modelo principal. Si planeas acceder a la relación después de usar los métodos save o saveMany, puedes usar el método refresh para volver a cargar el modelo y sus relaciones:

$post->comments()->save($comment);
 
$post->refresh();
 
// Todos los comentarios, incluido el comentario recién guardado...
$post->comments;

Guardar Modelos y Relaciones de Forma Recursiva

Si deseas guardar tu modelo y todas sus relaciones asociadas, puedes usar el método push. En este ejemplo, se guardará el modelo Post así como sus comentarios y los autores de los comentarios:

$post = Post::find(1);
 
$post->comments[0]->message = 'Message';
$post->comments[0]->author->name = 'Author Name';
 
$post->push();

El método pushQuietly se puede usar para guardar un modelo y sus relaciones asociadas sin lanzar eventos:

$post->pushQuietly();

Método create

Además de los métodos save y saveMany, también puedes usar el método create, que acepta un array de atributos, crea un modelo e lo inserta en la base de datos. La diferencia entre save y create es que save acepta una instancia completa de modelo Eloquent mientras que create acepta un array PHP plano. El modelo recién creado será devuelto por el método create:

use App\Models\Post;
 
$post = Post::find(1);
 
$comment = $post->comments()->create([
'message' => 'A new comment.',
]);

Puedes usar el método createMany para crear varios modelos relacionados:

$post = Post::find(1);
 
$post->comments()->createMany([
['message' => 'A new comment.'],
['message' => 'Another new comment.'],
]);

Los métodos createQuietly y createManyQuietly se pueden usar para crear un modelo o varios modelos sin despachar eventos:

$user = User::find(1);
 
$user->posts()->createQuietly([
'title' => 'Post title.',
]);
 
$user->posts()->createManyQuietly([
['title' => 'First post.'],
['title' => 'Second post.'],
]);

También puedes usar los métodos findOrNew, firstOrNew, firstOrCreate y updateOrCreate para crear y actualizar modelos en relaciones.

Nota Antes de usar el método create, asegúrese de revisar la documentación sobre asignación masiva.

Relaciones "Belongs To"

Si deseas asignar un modelo secundario a un nuevo modelo principal, puedes usar el método associate. En este ejemplo, el modelo User define una relación belongsTo con el modelo Account. Este método associate establecerá la clave foránea en el modelo secundario:

use App\Models\Account;
 
$account = Account::find(10);
 
$user->account()->associate($account);
 
$user->save();

Para quitar un modelo principal de un modelo secundario, puedes usar el método dissociate. Este método establecerá la clave foránea de la relación a null:

$user->account()->dissociate();
 
$user->save();

Relaciones Muchos a Muchos

Adjuntar / Desadjuntar

Eloquent también proporciona métodos para facilitar el trabajo con relaciones de muchos a muchos. Por ejemplo, imaginemos que un usuario puede tener muchos roles y un rol puede tener muchos usuarios. Puedes usar el método attach para adjuntar un rol a un usuario insertando un registro en la tabla intermedia de la relación:

use App\Models\User;
 
$user = User::find(1);
 
$user->roles()->attach($roleId);

Al adjuntar una relación a un modelo, también puedes pasar un array de datos adicionales que se insertarán en la tabla intermedia:

$user->roles()->attach($roleId, ['expires' => $expires]);

A veces puede ser necesario quitar un rol de un usuario. Para quitar un registro de relación de muchos a muchos, usa el método detach. El método detach eliminará el registro correspondiente de la tabla intermedia; sin embargo, ambos modelos permanecerán en la base de datos:

// Desvincular un solo rol del usuario...
$user->roles()->detach($roleId);
 
// Desvincular todos los roles del usuario...
$user->roles()->detach();

Para mayor comodidad, attach y detach también aceptan arrays de IDs como entrada:

$user = User::find(1);
 
$user->roles()->detach([1, 2, 3]);
 
$user->roles()->attach([
1 => ['expires' => $expires],
2 => ['expires' => $expires],
]);

Sincronizar Asociaciones

También puedes usar el método sync para construir asociaciones de muchos a muchos. El método sync acepta un array de IDs para colocar en la tabla intermedia. Cualquier ID que no esté en el array dado se eliminará de la tabla intermedia. Entonces, después de que se complete esta operación, solo existirán en la tabla intermedia los IDs en el array dado:

$user->roles()->sync([1, 2, 3]);

También puedes pasar valores adicionales de la tabla intermedia con los IDs:

$user->roles()->sync([1 => ['expires' => true], 2, 3]);

Si deseas insertar los mismos valores de la tabla intermedia con cada uno de los IDs de modelo sincronizados, puedes usar el método syncWithPivotValues:

$user->roles()->syncWithPivotValues([1, 2, 3], ['active' => true]);

Si no deseas separar los IDs existentes que faltan en el array dado, puedes usar el método syncWithoutDetaching:

$user->roles()->syncWithoutDetaching([1, 2, 3]);

Alternar Asociaciones

La relación de muchos a muchos también proporciona un método toggle que "cambia" el estado de adjunto de los IDs de modelo relacionados dados. Si el ID dado está actualmente adjunto, se separará. Del mismo modo, si actualmente está separado, se adjuntará:

$user->roles()->toggle([1, 2, 3]);

También puedes pasar valores adicionales de la tabla intermedia con los IDs:

$user->roles()->toggle([
1 => ['expires' => true],
2 => ['expires' => true],
]);

Actualizar un Registro en la Tabla Intermedia

Si necesitas actualizar una fila existente en la tabla intermedia de tu relación, puedes usar el método updateExistingPivot. Este método acepta la clave foránea del registro intermedio y un array de atributos para actualizar:

$user = User::find(1);
 
$user->roles()->updateExistingPivot($roleId, [
'active' => false,
]);

Actualizar Marcas de Tiempo del Padre

Cuando un modelo define una relación belongsTo o belongsToMany con otro modelo, como un Comment que pertenece a un Post, a veces es útil actualizar la marca de tiempo del padre cuando el modelo secundario se actualiza.

Por ejemplo, cuando se actualiza un modelo Comment, es posible que desees "tocar" automáticamente la marca de tiempo updated_at del Post propietario para que se establezca en la fecha y hora actuales. Para lograr esto, puedes agregar una propiedad touches a tu modelo secundario que contenga los nombres de las relaciones cuyas marcas de tiempo updated_at deben actualizarse cuando se actualiza el modelo secundario:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Comment extends Model
{
/**
* Todas las relaciones que deben actualizarse siempre.
*
* @var array
*/
protected $touches = ['post'];
 
/**
* Obtener la publicación a la que pertenece el comentario.
*/
public function post(): BelongsTo
{
return $this->belongsTo(Post::class);
}
}

Advertencia Las marcas de tiempo del modelo principal solo se actualizarán si el modelo secundario se actualiza mediante el método save de Eloquent.