You are on page 1of 29

Las Relaciones de Laravel

Eloquent: Una Guía Avanzada

Downloaded on: 02 November 2023


Ship and manage your
web projects faster
Deploy your projects on Google Cloud Platform's top tier
infrastructure. You'll get 25+ data centers to choose
from, 24/7/365 expert support, and advanced security
with DDoS protection.

Try for free

A menudo llega un momento en la vida de todo desarrollador en el que tienes que interactuar
con una base de datos. Aquí es donde Eloquent, el mapeador objeto-relacional (ORM) de
Laravel, hace que el proceso de interactuar con las tablas de tu base de datos sea intuitivo y
natural.

Es vital que, como profesional, reconozcas y comprendas los seis tipos de relaciones clave
que vamos a repasar.

¿Qué Son las Relaciones en Eloquent?


Cuando trabajamos con tablas en una base de datos relacional, podemos caracterizar las
relaciones como conexiones entre tablas. Esto te ayuda a organizar y estructurar los datos
sin esfuerzo, lo que permite una mayor legibilidad y manejo de los datos. En la práctica,
existen tres tipos de relaciones en las bases de datos:

uno a uno – Un registro de una tabla se asocia con uno, y sólo uno, de otra tabla. Por
ejemplo, una persona y un número de la Seguridad Social.
uno a varios – Un registro está asociado a varios registros de otra tabla. Por ejemplo,
un escritor y sus blogs.

varios-a-varios – Varios registros de una tabla están asociados a varios registros de


otra tabla. Por ejemplo, los alumnos y los cursos en los que están matriculados.

Laravel facilita la interacción y la gestión de las relaciones entre bases de datos utilizando la
sintaxis orientada a objetos de Eloquent.

Junto con estas definiciones, Laravel introduce más relaciones, a saber:

Has Many Through


Relaciones Polimórficas
Polimórficas de Varios a Varios

Tomemos, por ejemplo, una tienda cuyo inventario contiene diversos artículos, cada uno en
su propia categoría. Por tanto, dividir la base de datos en varias tablas tiene sentido desde el
punto de vista empresarial. Esto conlleva sus propios problemas, ya que no quieres consultar
todas y cada una de las tablas.

Podemos crear fácilmente una relación simple de uno a muchos en Laravel para ayudarnos,
por ejemplo, cuando necesitemos consultar los productos, podemos hacerlo utilizando el
modelo Producto.
— Esquema de base de datos con tres tablas y una tabla conjunta que representa una relación polimórfica

Relación Uno a Uno


Al ser la primera relación básica que ofrece Laravel, asocian dos tablas de forma que una fila
de la primera tabla está correlacionada con una sola fila de la otra tabla.

Para ver esto en acción, tenemos que crear dos modelos con su propia migración:

php artisan make:model Tenant


Php artisan make:model Rent

En este punto, tenemos dos modelos, uno es el «Tenant» (Inquilino) y el otro es su » Rent»
(Alquiler).
<?php

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class Tenant extends Model


{
/**
* Get the rent of a Tenant
*/
public function rent()
{
return $this->hasOne(Rent::class);
}
}

Como eloquent determina la relación de clave ajena basándose en el nombre del modelo
padre (Inquilino en este caso), el modelo Alquiler asume que existe una clave ajena
tenant_id.

Podemos sobrescribirla fácilmente como con un argumento adicional al método hasOne:

return $this- >hasOne(Rent::class, "custom_key");

Eloquent también asume que existe una coincidencia entre la clave ajena definida y la clave
primaria del padre (modelo Inquilino). Por defecto, buscará la coincidencia de tenant_id con
la clave id del registro Tenant. Podemos sobrescribir esto con un tercer argumento en el
método hasOne, de forma que coincida con otra clave:
return $this->hasOne(Rent::class, "custom_key", "other_key");

Ahora que hemos definido la relación uno a uno entre los modelos, podemos utilizarla
fácilmente, así:

$rent = Tenant::find(10)->rent;

Con esta línea de código, obtenemos el alquiler del inquilino con el id 10 si existe.

Relación Uno a Varios


Al igual que la relación anterior, ésta definirá relaciones entre un modelo monoparental y
varios modelos hijos. Es poco probable que nuestro Inquilino tenga sólo una factura de
Alquiler porque es un pago recurrente, por lo tanto, tendrá múltiples pagos.

En este caso, nuestra relación anterior tiene defectos, y podemos solucionarlos:

<?php

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class Tenant extends Model


{
/**
* Get the rents of a Tenant
*/
public function rent()
{
return $this->hasMany(Rent::class);
}
}

Antes de llamar al método para obtener los alquileres, conviene saber que las relaciones
sirven como constructores de consultas, por lo que podemos añadir más restricciones (como
alquiler entre fechas, pago mínimo, etc.) y encadenarlas para obtener nuestro resultado
deseado:

$rents = Tenant::find(10)->rent()->where('payment', '>', 500)->first();

Y como en la relación anterior, podemos sobrescribir las claves foráneas y locales pasando
argumentos adicionales:

return $this->hasMany(Rent::class, "foreign_key");

return $this->hasMany(Rent::class, "foreign_key", "local_key");


Ahora tenemos todos los alquileres de un inquilino, pero ¿qué hacemos cuando conocemos
el alquiler y queremos averiguar a quién pertenece? Podemos hacer uso de la propiedad
belongsTo:

<?php

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class Rent extends Model


{
/**
* Return the tenant for the rent
*/
public function tenant()
{
return $this->belongsTo(Tenant::class);
}
}

Y ahora podemos obtener el inquilino fácilmente:

$tenant = Rent::find(1)->tenant;

Para el método belongsTo, también podemos sobrescribir las claves foráneas y locales
como hicimos antes.
Relación Has-One-Of-Many
Como nuestro modelo de Inquilino puede estar asociado a muchos modelos de Alquiler,
queremos recuperar fácilmente el modelo relacionado más reciente o más antiguo de las
relaciones.

Una forma cómoda de hacerlo es combinar los métodos hasOne y ofMany:

public function latestRent() {


return $this->hasOne(Rent::class)->latestOfMany();
}

public function oldestRent() {


return $this->hasOne(Rent::class)->oldestOfMany();
}

Por defecto, obtenemos los datos basándonos en la clave primaria, que es ordenable, pero
podemos crear nuestros propios filtros para el método ofMany:

return $this->hasOne(Rent::class)->ofMany('price', 'min');

Relaciones HasOneThrough y HasManyThrough


Los métodos -Through sugieren que nuestros modelos tendrán que pasar por otro modelo
para establecer una relación con el modelo deseado. Por ejemplo, podemos asociar el
Alquiler con el Arrendador, pero el Alquiler debe pasar primero por el Arrendatario para llegar
al Arrendador.
Las claves de las tablas necesarias para ello tendrían el siguiente aspecto:

rent
id - integer
name - string
value - double

tenants
id - integer
name - string
rent_id - integer

landlord
id - integer
name - string
tenant_id - integer

Después de visualizar el aspecto de nuestras tablas, podemos hacer los modelos:

<?php

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class Rent extends Model


{
/**
* Return the rents' landlord
*/
public function rentLandlord()
{
return $this->hasOneThrough(Landlord::class, Tenant::class);
}
}

El primer argumento del método hasOneThrough es el modelo al que quieres acceder, y el


segundo argumento es el modelo por el que pasarás.

Y al igual que antes, puedes sobrescribir las claves foráneas y locales. Ahora que tenemos
dos modelos, tenemos dos de cada para sobrescribir en este orden:

public function rentLandlord()


{
return $this->hasOneThrough(
Landlord::class,
Tenant::class,
"rent_id", // Foreign key on the tenant table
"tenant_id", // Foreign key on the landlord table
"id", // Local key on the tenant class
"id" // Local key on the tenant table
);
}

Del mismo modo, la relación «Has Many Through» de Laravel Eloquent es útil cuando
quieres acceder a registros de una tabla lejana a través de una tabla intermedia.
Consideremos un ejemplo con tres tablas:

país
usuarios
juegos
Cada País tiene muchos Usuarios, y cada Usuario tiene muchos Juegos. Queremos
recuperar todos los Juegos pertenecientes a un País a través de la tabla Usuario.

Definirías las tablas así

country
id - integer
name - string

user
id - integer
country_id - integer
name - string

games
id - integer
user_id - integer
title - string

Ahora debes definir el modelo Eloquent para cada una de las tablas:

<?php

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class Country extends Model


{
protected $fillable = ['name'];
public function users()
{
return $this->hasMany(User::class);
}

public function games()


{
return $this->hasManyThrough(Games::class, User::class);
}
}

<?php

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class User extends Model


{
protected $fillable = [article_id, 'name'];

public function country()


{
return $this->belongsTo(Country::class);
}

public function posts()


{
return $this->hasMany(Post::class);
}
}
<?php

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class Game extends Model


{
protected $fillable = ['user_id', 'title'];

public function user()


{
return $this->belongsTo(User::class);
}
}

Ahora podemos llamar al método games() del modelo País para obtener todos los juegos
porque hemos establecido la relación «Has Many Through» entre País y Juego a través del
modelo Usuario.

<?php

$country = Country::find(159);

// Retrieve all games for the country


$games = $country->games;

Relación Varios-a-Varios
La relación varios-a-varios es más complicada. Un buen ejemplo es un empleado que tiene
varios roles. Un rol también puede asignarse a varios empleados. Ésta es la base de la
relación de muchos a muchos.

Para ello, debemos tener las tablas employees, roles y role_employees.

La estructura de las tablas de nuestra base de datos será la siguiente

employees
id - integer
name - string

roles
id - integer
name - string

role_employees
user_id - integer
role_id - integer

Conociendo la estructura de tablas de la relación, podemos definir fácilmente nuestro


modelo de Employee para belongsToMany Role model.

<?php

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class Employee extends Model


{
public function roles()
{
return $this- >belongsToMany(Role::class);
}
}

Una vez definido esto, podemos acceder a todos los roles de un empleado e incluso filtrarlos:

$employee = Employee::find(1);
$employee->roles->forEach(function($role) { // });

// OR

$employee = Employee::find(1)->roles()->orderBy('name')->where('name', 'ad

Como todos los demás métodos, podemos sobrescribir las claves foráneas y locales del
método belongsToMany.

Para definir la relación inversa del belongsToMany simplemente utilizamos el mismo método
pero sobre el método hijo ahora, con el padre como argumento.

<?php

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class Role extends Model


{
public function employees()
{
return $this->belongsToMany(Employee::class);
}
}

Usos de la tabla intermedia

Como habrás notado, cuando utilizamos la relación varios-a-varios, siempre se supone que
tenemos una tabla intermedia. En este caso, estamos utilizando la tabla role_employees.

Por defecto, nuestra tabla pivotante contendrá sólo los atributos id. Si queremos otros
atributos, tenemos que especificarlos así:

return $this->belongsToMany(Employee::class)->withPivot("active", "created

Si queremos abreviar el pivote para las marcas de tiempo, podemos hacerlo:

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

Un truco que hay que saber es que podemos personalizar el nombre «pivote» en cualquier
cosa que se adapte mejor a nuestra aplicación:
return $this->belongsToMany(Employee::class)->as('subscription')->withPivo

Filtrar los resultados de una consulta elocuente es un conocimiento imprescindible para


cualquier desarrollador que quiera dar un paso adelante y optimizar sus aplicaciones Laravel.

Por lo tanto, Laravel proporciona una fantástica característica de pivotes que se puede
utilizar para filtrar los datos que queremos recopilar. Así, en lugar de utilizar otras funciones
como las transacciones de base de datos para obtener nuestros datos a trozos, podemos
filtrarlos con métodos útiles como wherePivot, wherePivotIn, wherePivotNotIn,
wherePivotBetween, wherePivotNotBetween, wherePivotNull, wherePivotNotNull ¡y
podemos utilizarlos al definir relaciones entre tablas!

return $this->belongsToMany(Employee::class)->wherePivot('promoted', 1);


return $this->belongsToMany(Employee::class)->wherePivotIn('level', [1, 2]
return $this->belongsToMany(Employee::class)->wherePivotNotIn('level', [2,
return $this->belongsToMany(Employee::class)->wherePivotBetween('posted_at
return $this->belongsToMany(Employee::class)->wherePivotNull('expired_at')
return $this->belongsToMany(Employee::class)->wherePivotNotNull('posted_at

Una última característica sorprendente es que podemos ordenar por pivotes:

return $this->belongsToMany(Employee::class)
->where('promoted', true)
->orderByPivot('hired_at', 'desc');
Relaciones Polimórficas
La palabra Polimórfico viene del griego, y significa «muchas formas» Así, un modelo de
nuestra aplicación puede adoptar muchas formas, es decir, puede tener más de una
asociación. Imagina que estamos construyendo una aplicación con blogs, vídeos, encuestas,
etc. Un usuario puede crear un comentario para cualquiera de ellos. Por tanto, un modelo
Comentario podría pertenecer a los modelos Blogs, Vídeos y Encuestas.

Polimórfico Uno a Uno


Este tipo de relación es similar a una relación estándar uno a uno. La única diferencia es que
el modelo hijo puede pertenecer a más de un tipo de modelo con una única asociación.

Tomemos, por ejemplo, un modelo de Tenant y Landlord, puede compartir una relación
polimórfica con un modelo de WaterBill.

La estructura de la tabla puede ser la siguiente:

tenants
id – integer
name – string

landlords
id – integer
name – string

waterbills
id – integer
amount – double
waterbillable_id
waterbillable_type
Utilizamos waterbillable_id para el id del landlord o tenant, mientras que waterbillable_type
contiene el nombre de la clase del modelo padre. La columna tipo la utiliza eloquent para
averiguar qué modelo padre debe devolver.

La definición del modelo para una relación de este tipo tendrá el siguiente aspecto:

<?php

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class WaterBill extends Model


{
public function billable()
{
return $this->morphTo();
}
}

class Tenant extends Model


{
public function waterBill()
{
return $this->morphOne(WaterBill::class, 'billable');
}
}

class Landlord extends Model


{
public function waterBill()
{
return $this->morphOne(WaterBill::class, 'billable');
}
}
Una vez que tengamos todo esto en su sitio, podremos acceder a los datos tanto del modelo
Propietario como del modelo Inquilino:

<?php

$tenant = Tenant::find(1)->waterBill;
$landlord = Landlord::find(1)->waterBill;

Polimórfico Uno a Varios


Es similar a una relación normal de uno a varios, la única diferencia clave es que el modelo
hijo puede pertenecer a más de un tipo de modelo, utilizando una única asociación.

En una aplicación como Facebook, los usuarios pueden comentar publicaciones, vídeos,
encuestas, directos, etc. Con un uno a muchos polimórfico, podemos utilizar una única tabla
de comentarios para almacenar los comentarios de todas las categorías que tengamos. La
estructura de nuestra tabla sería algo así

posts
id – integer
title – string
body – text

videos
id – integer
title – string
url – string

polls
id – integer
title – string

comments
id – integer
body – text
commentable_id – integer
commentable_type – string

El commentable_id siendo el id del registro, y el commentable_type siendo el tipo de clase,


para que eloquent sepa qué buscar. En cuanto a la estructura del modelo, es muy similar al
polimórfico uno a muchos:

<?php

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class Comment extends Model


{
public function commentable()
{
return $this->morphTo();
}
}

class Poll extends Model


{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
class Live extends Model
{
public function comments()
{
return $this->morphMany(Comments::class, 'commentable');
}
}

Ahora, para recuperar los comentarios de un Vivo, basta con llamar al método encontrar con
el id, y ya tenemos acceso a la clase iterable comentarios:

<?php

use App\Models\Live;

$live = Live::find(1);

foreach ($live->comments as $comment) { }

// OR

Live::find(1)->comments()->each(function($comment) { // });
Live::find(1)->comments()->map(function($comment) { // });
Live::find(1)->comments()->filter(function($comment) { // });

// etc.

Y si tenemos el comentario y queremos averiguar a quién pertenece, accedemos al método


commentable:
<?php

use App\Models\Comment;

$comment = Comment::find(10);
$commentable = $comment->commentable;

// commentable – type of Post, Video, Poll, Live

Polimórfico Uno a Varios


En muchas aplicaciones que escalan, queremos una forma fácil de interactuar con los
modelos y entre ellos. Podemos querer el primer o el último comentario de un usuario, lo que
puede hacerse con una combinación de métodos morphOne y ofMany:

<?php

public function latestPost()


{
return $this->morphOne(Post::class, 'postable')->latestOfMany();
}

public function oldestPost()


{
return $this->morphOne(Post::class, 'postable')->oldestOfMany();
}

Los métodos latestOfMany y oldestOfMany recuperan el modelo más reciente o más


antiguo basándose en la clave primaria del modelo, que era la condición de que fuera
ordenable.
En algunos casos, no queremos ordenar por el ID, tal vez cambiamos la fecha de publicación
de algunas entradas y las queremos en ese orden, no por su id.

Para ello podemos pasar 2 parámetros al método ofMany. El primer parámetro es la clave
por la que queremos filtrar, y el segundo es el método de ordenación:

<?php

public function latestPublishedPost()


{
return $this->morphOne(Post::class, "postable")->ofMany("published_at"
}

Teniendo esto en cuenta, ¡es posible construir relaciones más avanzadas para esto! Imagina
que tenemos este escenario. Se nos pide que generemos una lista de todas las entradas
actuales en el orden en que han sido publicadas. El problema surge cuando tenemos 2
entradas con el mismo valor de published_at y cuando las entradas están programadas para
publicarse en el futuro.

Para ello, podemos pasar al método ofMany el orden en el que queremos que se apliquen
los filtros. De esta forma ordenamos por published_at, y si son iguales, ordenamos por id. En
segundo lugar, podemos aplicar una función de consulta al método ofMany para excluir
todas las entradas cuya publicación esté programada

<?php

public function currentPosts()


{
return $this->hasOne(Post::class)->ofMany([
'published_at' => 'max',
'id' => 'max',
], function ($query) {
$query->where('published_at', '<', now());
});
}

Polimórfico Varios a Varios


El varios-a-varios polimórfico es ligeramente más complejo que el normal. Una situación
común es tener etiquetas que se aplican a más activos en tu aplicación. Por ejemplo, en
TikTok, tenemos etiquetas que se pueden aplicar a Vídeos, Cortos, Historias, etc.

El polimórfico varios-a-varios nos permite tener una única tabla de etiquetas asociadas a los
Vídeos, Cortos e Historias.

La estructura de la tabla es sencilla:

videos
id – integer
description – string

stories
id – integer
description – string

taggables
tag_id – integer
taggable_id – integer
taggable_type – string
Con las tablas listas, podemos hacer el modelo y utilizar el método morphToMany. Este
método acepta el nombre de la clase del modelo y el «nombre de la relación»:

<?php

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class Video extends Model


{
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}

Y con esto, podemos definir fácilmente la relación inversa. Sabemos que para cada modelo
hijo queremos llamar al método morphedByMany:

<?php

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class Tag extends Model


{
public function stories()
{
return $this->morphedByMany(Story::class, 'taggable');
}
public function videos()
{
return $this->morphedByMany(Video::class, 'taggable');
}
}

Y ahora, cuando obtengamos una etiqueta, ¡podremos recuperar todos los vídeos e historias
relacionados con esa etiqueta!

<?php
use App\Model\Tag;

$tag = Tag::find(10);
$posts = $tag->stories;
$videos = $tag->stories;

Optimiza la velocidad de Eloquent


Cuando trabajes con el ORM Eloquent de Laravel, es esencial que sepas cómo optimizar las
consultas a la base de datos y minimizar la cantidad de tiempo y memoria que requiere la
obtención de datos. Una forma de hacerlo es implementando el almacenamiento en caché en
tu aplicación.

Laravel proporciona un sistema de almacenamiento en caché flexible que admite varios


backends, como Redis, Memcached y almacenamiento en caché basado en archivos. Al
almacenar en caché los resultados de las consultas de Eloquent, puedes reducir el número
de consultas a la base de datos, haciendo que tu aplicación sea más rápida y valiosa.
Además, puedes utilizar el constructor de consultas de Laravel para crear consultas
complejas adicionales, optimizando aún más el rendimiento de tu aplicación.

Resumen
En conclusión, las relaciones Eloquent son una potente característica de Laravel que permite
a los desarrolladores trabajar fácilmente con datos relacionados. Desde relaciones uno a uno
hasta relaciones varios a varios, Eloquent proporciona una sintaxis sencilla e intuitiva para
definir y consultar estas relaciones.

Como desarrollador de Laravel, dominar las relaciones Eloquent puede mejorar


enormemente tu flujo de trabajo de desarrollo y hacer que tu código sea más eficiente y
legible. Si estás interesado en aprender más sobre Laravel, Kinsta tiene varios recursos
disponibles, incluido un tutorial sobre cómo empezar con Laravel y un artículo sobre los
salarios de los desarrolladores de Laravel.

Kinsta ofrece soluciones de alojamiento gestionado que facilitan el despliegue y la gestión de


aplicaciones Laravel.

You might also like