Professional Documents
Culture Documents
Laravel Relaciones
Laravel Relaciones
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.
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.
Laravel facilita la interacción y la gestión de las relaciones entre bases de datos utilizando la
sintaxis orientada a objetos de Eloquent.
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
Para ver esto en acción, tenemos que crear dos modelos con su propia migración:
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;
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.
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.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
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:
Y como en la relación anterior, podemos sobrescribir las claves foráneas y locales pasando
argumentos adicionales:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
$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.
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:
rent
id - integer
name - string
value - double
tenants
id - integer
name - string
rent_id - integer
landlord
id - integer
name - string
tenant_id - integer
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
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:
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.
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;
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
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);
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.
employees
id - integer
name - string
roles
id - integer
name - string
role_employees
user_id - integer
role_id - integer
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
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
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;
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)->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
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)
->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.
Tomemos, por ejemplo, un modelo de Tenant y Landlord, puede compartir una relación
polimórfica con un modelo de WaterBill.
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;
<?php
$tenant = Tenant::find(1)->waterBill;
$landlord = Landlord::find(1)->waterBill;
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
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
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);
// OR
Live::find(1)->comments()->each(function($comment) { // });
Live::find(1)->comments()->map(function($comment) { // });
Live::find(1)->comments()->filter(function($comment) { // });
// etc.
use App\Models\Comment;
$comment = Comment::find(10);
$commentable = $comment->commentable;
<?php
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
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
El polimórfico varios-a-varios nos permite tener una única tabla de etiquetas asociadas a los
Vídeos, Cortos e Historias.
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;
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;
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;
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.