Los modelos y sus relaciones son el corazón de Laravel Eloquent. Si te hacen pasar un mal rato o no puedes encontrar una guía simple, amigable y completa, ¡comienza aquí!
Sentado en el otro lado de su artículo de programación, es fácil para el escritor fingir o hacer estallar el aura de experiencia / prestigio que brinda la plataforma. Pero seré honesto, tuve una extremadamente tiempo difícil aprendiendo Laravel, aunque solo sea porque fue mi primer marco de pila completa. Una razón fue que no lo estaba usando en el trabajo y lo estaba explorando por curiosidad; así que lo intentaba, llegaba a un punto, me confundía, me rendía y, finalmente, me olvidaba de todo. Debo haber hecho esto 5-6 veces antes de que comenzara a tener sentido para mí (por supuesto, la documentación no ayuda).
Pero lo que todavía no tenía sentido era Eloquent. O al menos, las relaciones entre modelos (porque Eloquent es demasiado grande para aprenderlo por completo). Los ejemplos que modelan a autores y publicaciones de blogs son una broma porque los proyectos reales son mucho más complejos; lamentablemente, los documentos oficiales utilizan los mismos (o similares) ejemplos. O incluso si me encontré con algún artículo / recurso útil, la explicación fue tan mala o tan desafortunada que simplemente no sirvió de nada.
(Por cierto, he sido atacado por atacar la documentación oficial antes, así que si tiene ideas similares, aquí está mi respuesta estándar: consulte la documentación de Django y luego hable conmigo).
Con el tiempo, poco a poco, se juntó y tuvo sentido. Finalmente pude modelar proyectos correctamente y usar los modelos cómodamente. Entonces, un día me encontré con algunos trucos de Colecciones ingeniosos que hacen que este trabajo sea más agradable. En este artículo, pretendo cubrirlo todo, comenzando desde los conceptos básicos y luego cubriendo todos los casos de uso posibles que encontrará en proyectos reales.
¿Por qué son difíciles las relaciones modelo Eloquent?
Lamentablemente, me encuentro con demasiados desarrolladores de Laravel que no entienden los modelos correctamente.
Pero ¿por qué?
Incluso hoy, cuando hay una explosión de cursos, artículos y videos en Laravel, la comprensión general es deficiente. Creo que es un punto importante y merece una reflexión.
Si me preguntas, diré que las relaciones de modelo Eloquent no son difíciles en absoluto. Al menos cuando se ve desde la perspectiva de la definición de "duro". Las migraciones de esquemas en vivo son difíciles; escribir un nuevo motor de plantillas es difícil; Contribuir con código al núcleo de Laravel es difícil. En comparación con estos, aprender y usar un ORM. . . bueno, eso no puede ser difícil! 🤭🤭
Lo que realmente pasa es que Desarrolladores PHP aprendiendo Laravel encuentra difícil a Eloquent. Ese es el problema subyacente real y, en mi opinión, hay varios factores que contribuyen a esto (¡alerta de opinión dura e impopular!):
- Antes de Laravel, la exposición a un marco para la mayoría de los desarrolladores de PHP ha sido CodeIgniter (todavía es vivo, por cierto, incluso si se ha vuelto más parecido a Laravel / CakePHP). En la comunidad CodeIgniter más antigua (si había una), la "mejor práctica" era pegar directamente las consultas SQL donde fuera necesario. Y aunque hoy tenemos un nuevo CodeIgniter, los hábitos se han trasladado. Como resultado, cuando aprendiendo Laravel, la idea de un ORM es 100% nueva para los desarrolladores de PHP.
- Descartando el porcentaje muy pequeño de PHP expuesto a frameworks como Yii, CakePHP, etc., el resto está acostumbrado a trabajar en PHP core o en un entorno como WordPress. Y aquí de nuevo, no existe una mentalidad basada en OOP, por lo que un marco, un contenedor de servicios, un patrón de diseño, un ORM. . . estos son conceptos extraños.
- Hay poco o ningún concepto de aprendizaje continuo en el mundo PHP. El desarrollador promedio está contento de trabajar con configuraciones de un solo servidor utilizando bases de datos relacionales y emitiendo consultas escritas como cadenas. Programación asincrónica, sockets web, HTTP 2/3, Linux (olvídese de Docker), pruebas unitarias, diseño basado en dominios: todas estas son ideas extrañas para una abrumadora proporción de desarrolladores PHP. Como resultado, leer sobre algo nuevo y desafiante, hasta el punto en que uno lo encuentra cómodo, no sucede cuando se encuentra con Eloquent.
- La comprensión general de las bases de datos y el modelado también es deficiente. Dado que el diseño de la base de datos está directamente vinculado de manera inseparable a los modelos de Eloquent, eleva el listón de dificultad más alto.
No pretendo ser severo y generalizar globalmente, hay excelente, Los desarrolladores de PHP también, y muchos de ellos, pero su porcentaje general es muy bajo.
Si estás leyendo esto, significa que has cruzado todas estas barreras, te has encontrado con Laravel y te has metido con Eloquent.
¡Felicidades! 👏
Ya casi estás ahí. Todos los bloques de construcción están en su lugar y solo tenemos que revisarlos en el orden y detalle adecuados. En otras palabras, comencemos en el nivel de la base de datos.
Modelos de base de datos: relaciones y cardinalidad
Para simplificar las cosas, supongamos que estamos trabajando con bases de datos relacionales sólo a lo largo de este artículo. Una razón es que los ORM se desarrollaron originalmente para bases de datos relacionales; la otra razón es que los RDBMS todavía son abrumadoramente populares.
Modelo de datos
Primero, comprendamos mejor los modelos de datos. La idea de un modelo (o un datos modelo, para ser más precisos), proviene de la base de datos. Sin base de datos, sin datos y, por lo tanto, sin modelo de datos. ¿Y qué es un modelo de datos? En pocas palabras, es la forma en que decide almacenar / estructurar sus datos. Por ejemplo, en una tienda de comercio electrónico, puede almacenar todo en una mesa gigante (práctica HORRIBLE, pero lamentablemente, no es infrecuente en el mundo PHP); ese sería su modelo de datos. También puede dividir los datos en 20 tablas principales y 16 de conexión; también es un modelo de datos.
Además, tenga en cuenta que la forma en que se estructuran los datos en la base de datos no tiene por qué coincidir al 100% con la forma en que están organizados en el ORM del marco. Sin embargo, el esfuerzo siempre es mantener las cosas lo más cerca posible para que no tengamos nada más de lo que tener en cuenta al desarrollar.
Cardinalidad
También saquemos rápidamente este término: cardinalidad. Simplemente se refiere a "contar", hablando libremente. Entonces, 1, 2, 3. . . todo puede ser la cardinalidad de algo. Fin de la historia. ¡Sigamos moviéndonos!
Relaciones
Ahora, siempre que almacenamos datos en cualquier tipo de sistema, existen formas en que los puntos de datos pueden relacionarse entre sí. Sé que esto suena abstracto y aburrido, pero tengan paciencia conmigo. Las formas en que se conectan los diferentes elementos de datos se conocen como relaciones. Primero veamos algunos ejemplos que no son de base de datos para estar convencidos de que entendemos completamente la idea.
- Si almacenamos todo en una matriz, una posible relación es: el siguiente elemento de datos está en un índice mayor que el índice anterior por
1
. - Si almacenamos datos en un árbol binario, una posible relación es que el árbol hijo a la izquierda siempre tiene valores más pequeños que los del nodo padre (si elegimos mantener el árbol de esa manera).
- Si almacenamos datos como una matriz de matrices de igual longitud, podemos imitar una matriz y luego sus propiedades se convierten en las relaciones de nuestros datos.
Entonces vemos que la palabra "relación", en el contexto de los datos, no tiene un significado fijo. De hecho, si dos personas estuvieran mirando los mismos datos, podrían identificar dos relaciones de datos muy diferentes (¡hola, estadísticas!) Y ambas podrían ser válidas.
Relacional bases de datos
Con base en todos los términos que hemos discutido hasta ahora, finalmente podemos hablar sobre algo que tiene un enlace directo a los modelos en un marco web (Laravel): bases de datos relacionales. Para la mayoría de nosotros, la base de datos principal utilizada es MySQL, MariaDB, PostgreSQL, MSSQL, SQL Server, SQLite o algo por el estilo. También podríamos saber vagamente que estos se llaman RDBMS, pero la mayoría de nosotros hemos olvidado lo que realmente significa y por qué es importante.
La "R" en RDBMS significa relacional, por supuesto. Este no es un término elegido arbitrariamente; por esto, destacamos el hecho de que estos sistemas de bases de datos están diseñados para trabajar de manera eficiente con relaciones entre los datos almacenados. De hecho, "relación" aquí tiene un significado matemático estricto, y aunque ningún desarrollador necesita preocuparse por ello, es útil saber que hay un riguroso fundación debajo de estos tipos de bases de datos.
Explore estos recursos para aprender SQL y NoSQL.
Bien, sabemos por experiencia que los datos en RDBMS se almacenan como tablas. ¿Dónde, entonces, están las relaciones?
Tipos de relaciones en RDBMS
Esta es quizás la parte más importante de todo el tema de Laravel y las relaciones del modelo. Si no entiende esto, Eloquent nunca tendrá sentido, así que preste atención durante los próximos minutos (ni siquiera es tan difícil).
Un RDBMS permite que tengamos relaciones entre los datos, a nivel de base de datos. Esto significa que estas relaciones no son imprácticas / imaginarias / subjetivas y pueden ser creadas o inferidas por diferentes personas con el mismo resultado.
Al mismo tiempo, existen ciertas capacidades / herramientas dentro de un RDBMS que nos permiten crear y hacer cumplir estas relaciones, tales como:
- Clave primaria
- Clave externa
- Limitaciones
No quiero que este artículo se convierta en un curso de bases de datos, así que supongo que sabes cuáles son estos conceptos. Si no es así, o en caso de que no tenga confianza en sí mismo, le recomiendo este amigable video (siéntase libre de explorar toda la serie):

Da la casualidad de que estas relaciones de estilo RDBMS también son las más comunes que ocurren en aplicaciones del mundo real (no siempre, ya que una red social se modela mejor como un gráfico y no como una colección de tablas). Por lo tanto, echémosle un vistazo uno por uno y también intentemos comprender dónde pueden ser útiles.
Relación uno a uno
En casi todas las aplicaciones web, hay cuentas de usuario. Además, lo siguiente es cierto (en términos generales) sobre los usuarios y las cuentas:
- Un usuario solo puede tener una cuenta.
- Una cuenta solo puede ser propiedad de un usuario.
Sí, podemos argumentar que una persona puede registrarse con otro correo electrónico y así crear dos cuentas, pero desde la perspectiva de la aplicación web, esas son dos personas diferentes con dos cuentas diferentes. La aplicación, por ejemplo, no mostrará los datos de una cuenta en otra.
Lo que significa todo esto de dividir el cabello es: si tiene una situación como esta en su aplicación y está utilizando una base de datos relacional, necesitaría diseñarla como una relación uno a uno. Tenga en cuenta que nadie lo está forzando artificialmente: hay una situación clara en el ámbito empresarial y sucede que está utilizando una base de datos relacional. . . sólo cuando se cumplen estas dos condiciones, se busca una relación de uno a uno.
Para este ejemplo (usuarios y cuentas), así es como podemos implementar esta relación al crear el esquema:
CREATE TABLE users(
id INT NOT NULL AUTO_INCREMENT,
email VARCHAR(100) NOT NULL,
password VARCHAR(100) NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE accounts(
id INT NOT NULL AUTO_INCREMENT,
role VARCHAR(50) NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(id) REFERENCES users(id)
);
¿Notas el truco aquí? Es bastante poco común cuando se crean aplicaciones en general, pero en el accounts
mesa, tenemos el campo id
establecer como clave principal y clave externa! La propiedad de la clave externa la vincula a la users
tabla (por supuesto 🙄) mientras que la propiedad de clave primaria hace que la id
columna única: ¡una verdadera relación uno a uno!
Por supuesto, la fidelidad de esta relación no está garantizada. Por ejemplo, no hay nada que me impida agregar 200 nuevos usuarios sin agregar una sola entrada al accounts
mesa. Si hago eso, termino con un uno acero ¡relación! 🤭🤭 Pero dentro de los límites de la estructura pura, eso es lo mejor que podemos hacer. Si queremos evitar agregar usuarios sin cuentas, debemos tomar la ayuda de algún tipo de lógica de programación, ya sea en forma de activadores de base de datos o validaciones impuestas por Laravel.
Si está empezando a estresarse, tengo un muy buen consejo:
- Tomar con calma. Tan lento como sea necesario. En lugar de tratar de terminar este artículo y los otros 15 que ha marcado para hoy, apéguese a este. Deje que tome 3, 4, 5 días si eso es lo que se necesita; su objetivo debe ser eliminar las relaciones modelo Eloquent de su lista para siempre. Ha pasado de un artículo a otro antes, desperdiciando varios cientos de horas y, sin embargo, no ayudó. Entonces, haz algo diferente esta vez. 😇
- Si bien este artículo trata sobre Laravel Eloquent, todo eso viene mucho después. La base de todo es el esquema de la base de datos, por lo que nuestro enfoque debe ser hacerlo bien primero. Si no puede trabajar exclusivamente a nivel de base de datos (suponiendo que no haya marcos en el mundo), los modelos y las relaciones nunca tendrán pleno sentido. Entonces, olvídate de Laravel por ahora. Completamente. Solo estamos hablando y haciendo diseño de bases de datos por ahora. Sí, haré referencias a Laravel de vez en cuando, pero tu trabajo es ignorarlas por completo si te complican la imagen.
- Más adelante, lea un poco más sobre las bases de datos y lo que ofrecen. Índices, rendimiento, disparadores, estructuras de datos subyacentes y su comportamiento, almacenamiento en caché, relaciones en MongoDB. . . cualquier tema tangencial que pueda cubrir le ayudará como ingeniero. Recuerde, los modelos de framework son simplemente cáscaras fantasma; la funcionalidad real de una plataforma proviene de sus bases de datos subyacentes.
Relación uno a muchos
No estoy seguro si se dio cuenta de esto, pero este es el tipo de relación que todos creamos intuitivamente en nuestro trabajo diario. Cuando creamos un orders
tabla (un ejemplo hipotético), por ejemplo, para almacenar una clave externa en el users
tabla, creamos una relación de uno a muchos entre usuarios y pedidos. ¿Porqué es eso? Bueno, mírelo de nuevo desde la perspectiva de quién puede tener cuántos: un usuario puede tener más de un pedido, que es más o menos cómo funciona todo el comercio electrónico. Y visto desde el lado opuesto, la relación dice que un pedido solo puede pertenecer a un usuario, lo que también tiene mucho sentido.
En el modelado de datos, los libros RDBMS y la documentación del sistema, esta situación se representa esquemáticamente de la siguiente manera:
¿Observas las tres líneas que forman una especie de tridente? Este es el símbolo de "muchos", por lo que este diagrama dice que un usuario puede tener muchos pedidos.
Por cierto, estos recuentos de “muchos” y “uno” que nos encontramos repetidamente son lo que se llama la cardinalidad de una relación (¿recuerdas esta palabra de una sección anterior?). Nuevamente, para este artículo, el término no tiene ningún uso, pero ayuda a conocer el concepto en caso de que surja durante las entrevistas o la lectura adicional.
Simple, ¿verdad? Y en términos de SQL real, crear esta relación también es simple. De hecho, ¡es mucho más simple que el caso de una relación uno a uno!
CREATE TABLE users(
id INT NOT NULL AUTO_INCREMENT,
email VARCHAR(100) NOT NULL,
password VARCHAR(100) NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE orders(
id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
description VARCHAR(50) NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(user_id) REFERENCES users(id)
);
La orders
La tabla almacena los ID de usuario para cada pedido. Dado que no hay ninguna restricción (restricción) de que los ID de usuario en el orders
La tabla debe ser única, lo que significa que podemos repetir una sola ID muchas veces. Esto es lo que crea la relación de uno a muchos, y no una magia arcana que se esconde debajo. Los ID de usuario se almacenan de una forma tonta en el orders
tabla, y SQL no tiene ningún concepto de uno a muchos, uno a uno, etc. Pero una vez que estamos almacenando datos de esta manera, podemos pensar de que haya una relación de uno a varios.
Con suerte, ahora tiene sentido. O al menos, con más sentido común que antes. 😅 Recuerde que, al igual que cualquier otra cosa, esto es una mera cuestión de práctica, y una vez que lo haya hecho 4-5 veces en situaciones del mundo real, ni siquiera lo pensará.
Relaciones de muchos a muchos
El siguiente tipo de relación que surge en la práctica es la denominada relación de muchos a muchos. Una vez más, antes de preocuparnos por los marcos o incluso sumergirnos en bases de datos, pensemos en un análogo del mundo real: libros y autores. Piense en su autor favorito; han escrito más de un libro, ¿verdad? Al mismo tiempo, es bastante común ver a varios autores colaborando en un libro (al menos en el género de no ficción). Por lo tanto, un autor puede escribir muchos libros y muchos autores pueden escribir un libro. Entre las dos entidades (libro y autor), esto forma una relación de varios a varios.
Ahora, dado que es muy poco probable que cree una aplicación del mundo real que involucre bibliotecas o libros y autores, pensemos en algunos ejemplos más. En un entorno B2B, un fabricante solicita artículos a un proveedor y, a su vez, recibe una factura. La factura contendrá varios artículos de línea, cada uno de ellos con la cantidad y el artículo suministrado; por ejemplo, piezas de tubería de 5 pulgadas x 200, etc. En esta situación, los artículos y las facturas tienen una relación de muchos a muchos (piénselo y convénzase usted mismo). En un sistema de gestión de flotas, los vehículos y los conductores tendrán una relación similar. En un sitio de comercio electrónico, los usuarios y los productos pueden tener una relación de muchos a muchos si consideramos funciones como favoritos o listas de deseos.
Bastante justo, ahora, ¿cómo crear esta relación de muchos a muchos en SQL? Basándonos en nuestro conocimiento de cómo funciona la relación uno a muchos, podría ser tentador pensar que deberíamos almacenar claves foráneas en la otra tabla en ambas tablas. Sin embargo, nos encontramos con problemas importantes si intentamos hacer esto. Eche un vistazo a este ejemplo donde se supone que los libros son autores que tienen una relación de varios a varios:
A primera vista, todo parece estar bien: los libros se asignan a los autores exactamente de una manera de muchos a muchos. Pero mira de cerca authors
datos de la tabla: las identificaciones de libros 12 y 13 están escritas por Peter M. (identificación de autor 2), por lo que no tenemos más remedio que repetir las entradas. No solo el authors
tabla ahora tiene problemas de integridad de datos (adecuada normalización y todo eso), los valores en el id
columna ahora se repiten. Esto significa que en el diseño que hemos elegido, no puede haber una columna de clave primaria (porque las claves primarias no pueden tener valores duplicados) y todo se desmorona.
Claramente, necesitamos una nueva forma de hacer esto y, afortunadamente, este problema ya se ha resuelto. Dado que almacenar claves foráneas directamente en ambas tablas estropea las cosas, la forma correcta de crear relaciones de muchos a muchos en RDBMS es creando una llamada "tabla de unión". Básicamente, la idea es dejar que las dos tablas originales permanezcan tranquilas y crear una tercera tabla para demostrar el mapeo de muchos a muchos.
Rehagamos el ejemplo fallido para que contenga una tabla de unión:
Tenga en cuenta que ha habido cambios drásticos:
- El número de columnas en el
authors
la mesa se reduce. - El número de columnas en el
books
la mesa se reduce. - El número de filas en el
authors
La tabla se reduce porque ya no hay necesidad de repetir. - Una nueva mesa llamada
authors_books
Ha aparecido, que contiene información sobre qué ID de autor está conectado a qué ID de libro. Podríamos haber llamado cualquier cosa a la tabla de unión, pero por convención es el resultado de simplemente unir las dos tablas que representa, usando un guión bajo.
La tabla de unión no tiene clave principal y, en la mayoría de los casos, solo contiene dos columnas: ID de las dos tablas. Es casi como si elimináramos las columnas de clave externa de nuestro ejemplo anterior y las pegáramos en esta nueva tabla. Dado que no hay una clave principal, puede haber tantas repeticiones como sea necesario para registrar todas las relaciones.
Ahora, podemos ver con nuestros ojos cómo la tabla de unión muestra claramente las relaciones, pero ¿cómo accedemos a ellas en nuestras aplicaciones? El secreto está vinculado al nombre: unión mesa. Este no es un curso sobre consultas SQL, por lo que no me sumergiré en él, pero la idea es que si desea que todos los libros de un autor en particular en una consulta eficiente, una las tablas SQL en el mismo orden -> authors
, authors_books
y books
. authors
y authors_books
las mesas están unidas sobre el id
y author_id
columnas, respectivamente, mientras que authors_books
y books
las mesas están unidas en el book_id
y id
columnas, respectivamente.
Agotador, sí. Pero mira el lado positivo, hemos terminado todos la teoría / base necesaria que necesitábamos hacer antes de abordar los modelos Eloquent. ¡Y déjame recordarte que todo esto no es opcional! No saber el diseño de la base de datos lo dejará en la tierra de la confusión de Eloquent para siempre. Además, lo que sea que Eloquent haga o intente hacer, refleja perfectamente estos detalles a nivel de base de datos, por lo que es fácil ver por qué tratar de aprender Eloquent mientras huye de RDBMS es un ejercicio inútil.
Creating model relationships in Laravel Eloquent
Finalmente, luego de un desvío que duró unas 70,000 millas, llegamos al punto en el que podemos hablar de Eloquent, sus modelos y cómo crearlos / usarlos. Ahora, aprendimos en la parte anterior del artículo que todo comienza con la base de datos y cómo modelas tus datos. Esto me hizo darme cuenta de que debería usar un solo ejemplo completo en el que empiece un proyecto nuevo. Al mismo tiempo, quiero que este ejemplo sea del mundo real, y no sobre blogs y autores o libros y estanterías (que también son del mundo real, pero que están muertos).
Imaginemos una tienda que venda peluches. Supongamos también que se nos ha proporcionado el documento de requisitos, a partir del cual podemos identificar estas cuatro entidades en el sistema: usuarios, pedidos, facturas, artículos, categorías, subcategorías y transacciones. Sí, es probable que haya más complicaciones, pero dejemos eso a un lado y centrémonos en cómo pasamos de un documento a una aplicación.
Una vez que se han identificado las principales entidades del sistema, debemos pensar en cómo se relacionan entre sí, en términos de las relaciones de la base de datos que hemos discutido hasta ahora. Aquí están los que se me ocurren:
- Usuarios y pedidos: uno a muchos.
- Pedidos y facturas: Uno a uno. Me doy cuenta de que este no es sencillo y, según el dominio de su negocio, puede haber una relación de uno a muchos, de muchos a uno o de muchos a muchos. Pero cuando se trata de una pequeña tienda de comercio electrónico promedio, un pedido solo resultará en una factura y viceversa.
- Pedidos y artículos: muchos a muchos.
- Artículos y categorías: muchos a uno. Nuevamente, esto no es así en los grandes sitios de comercio electrónico, pero tenemos una pequeña operación.
- Categorías y subcategorías: de una a muchas. Nuevamente, encontrará la mayoría de los ejemplos del mundo real que contradicen esto, pero bueno, Eloquent es lo suficientemente difícil como es, ¡así que no hagamos el modelado de datos más difícil!
- Pedidos y transacciones: uno a varios. También me gustaría agregar estos dos puntos como justificación de mi elección: 1) También podríamos haber agregado una relación entre Transacciones y Facturas. Es solo una decisión de modelado de datos. 2) ¿Por qué uno a muchos aquí? Bueno, es común que el pago de un pedido falle por algún motivo y tenga éxito la próxima vez. En este caso, tenemos dos transacciones creadas para ese pedido. Si deseamos mostrar esas transacciones fallidas o no es una decisión comercial, pero siempre es una buena idea capturar datos valiosos.
¿Hay otras relaciones? Bueno, son posibles muchas más relaciones, pero no son prácticas. Por ejemplo, podemos decir que un usuario tiene muchas transacciones, por lo que debería existir una relación entre ellas. Lo que hay que tener en cuenta aquí es que ya existe una relación indirecta: usuarios -> pedidos -> transacciones, y en general, es lo suficientemente bueno ya que los RDBMS son bestias en la unión de tablas. En segundo lugar, crear esta relación significaría agregar un user_id
columna al transactions
mesa. Si hiciéramos esto para todas las posibles relaciones directas, estaríamos agregando mucha más carga en la base de datos (en forma de más almacenamiento, especialmente si se utilizan UUID y se mantienen índices), encadenando el sistema en general. Claro, si la empresa dice que necesita datos de transacciones y los necesita en 1.5 segundos, podríamos decidir agregar esa relación y acelerar las cosas (compensaciones, compensaciones ...).
Y ahora, señoras y señores, ¡ha llegado el momento de escribir el código real!
Laravel model relationships — real example with code
La siguiente fase de este artículo trata sobre ensuciarnos las manos, pero de una manera útil. Recogeremos las mismas entidades de base de datos que en el ejemplo anterior de comercio electrónico, y veremos cómo se crean y conectan los modelos en Laravel, ¡desde la instalación de Laravel!
Naturalmente, supongo que tiene configurado su entorno de desarrollo y sabe cómo instalar y usar Composer para administrar dependencias.
$ composer global require laravel/installer -W
$ laravel new model-relationships-study
Estos dos comandos de consola instalan el instalador de Laravel (el -W
parte se usa para actualizar ya que ya tenía instalada una versión anterior). Y en caso de que tenga curiosidad, al momento de escribir, la versión de Laravel que se instaló es 8.5.9. ¿Debería entrar en pánico y actualizar también? Lo desaconsejaría, ya que no espero ningún cambio importante entre Laravel 5 y Laravel 8 en el contexto de nuestra aplicación. Algunas cosas han cambiado y afectarán este artículo (como las fábricas de modelos), pero creo que podrá transferir el código.
Como ya hemos pensado en el modelo de datos y sus relaciones, la parte de crear los modelos será trivial. Y también verá (¡ahora estoy sonando como un disco rayado!) ¡Cómo refleja el esquema de la base de datos ya que depende al 100% de él!
En otras palabras, primero debemos crear las migraciones (y archivos de modelo) para todos los modelos, que se aplicarán a la base de datos. Más tarde, podemos trabajar en los modelos y abordar las relaciones.
Entonces, ¿con qué modelo comenzamos? El más simple y el menos conectado, por supuesto. En nuestro caso, esto significa que User
modelo. Dado que Laravel se envía con este modelo (y no puede funcionar sin él), modifiquemos el archivo de migración y también limpiemos el modelo para satisfacer nuestras necesidades simples.
Aquí está la clase de migración:
class CreateUsersTable extends Migration
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
});
}
}
Dado que en realidad no estamos construyendo un proyecto, no necesitamos ingresar a las contraseñas, is_active y todo eso. Nuestro users
La tabla tendrá solo dos columnas: la identificación y el nombre del usuario.
Creemos la migración para Category
Siguiente. Dado que Laravel nos permite la conveniencia de generar el modelo también con un solo comando, lo aprovecharemos, aunque no tocaremos el archivo del modelo por ahora.
$ php artisan make:model Category -m
Model created successfully.
Created Migration: 2021_01_26_093326_create_categories_table
Y aquí está la clase de migración:
class CreateCategoriesTable extends Migration
{
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
});
}
}
Si te sorprende la ausencia del down()
función, no lo seas; en la práctica, rara vez terminas usándolo porque eliminar una columna o tabla o cambiar un tipo de columna da como resultado una pérdida de datos que no se puede recuperar. En desarrollo, se encontrará descartando toda la base de datos y luego volviendo a ejecutar las migraciones. Pero estamos divagando, así que volvamos y abordemos la siguiente entidad. Dado que las subcategorías están directamente relacionadas con las categorías, creo que es una buena idea hacerlo a continuación.
$ php artisan make:model SubCategory -m
Model created successfully.
Created Migration: 2021_01_26_140845_create_sub_categories_table
Muy bien, ahora completemos el archivo de migración:
class CreateSubCategoriesTable extends Migration
{
public function up()
{
Schema::create('sub_categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->unsignedBigInteger('category_id');
$table->foreign('category_id')
->references('id')
->on('categories')
->onDelete('cascade');
});
}
}
Como puede ver, agregamos aquí una columna separada, llamada category_id
, que almacenará los ID de categories
mesa. No hay premios por adivinar, esto crea una relación de uno a muchos a nivel de base de datos.
Ahora es el turno de los artículos:
$ php artisan make:model Item -m
Model created successfully.
Created Migration: 2021_01_26_141421_create_items_table
Y la migración:
class CreateItemsTable extends Migration
{
public function up()
{
Schema::create('items', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description');
$table->string('type');
$table->unsignedInteger('price');
$table->unsignedInteger('quantity_in_stock');
$table->unsignedBigInteger('sub_category_id');
$table->foreign('sub_category_id')
->references('id')
->on('sub_categories')
->onDelete('cascade');
});
}
}
Si siente que las cosas deben hacerse de manera diferente, está bien. Rara vez dos personas presentarán exactamente el mismo esquema y arquitectura. Tenga en cuenta una cosa, que es una de las mejores prácticas: he almacenado el precio como un número entero.
¿Por qué?
Bueno, la gente se dio cuenta de que manejar las divisiones flotantes y todo era feo y propenso a errores en el lado de la base de datos, por lo que comenzaron a almacenar el precio en términos de la unidad de moneda más pequeña. Por ejemplo, si estuviéramos operando en USD, el campo de precio aquí representaría centavos. En todo el sistema, los valores y cálculos estarán en centavos; solo cuando llegue el momento de mostrárselo al usuario o enviar un PDF por correo electrónico, lo dividiremos por 100 y lo redondearemos. Inteligente, ¿eh?
De todos modos, observe que un elemento está vinculado a una subcategoría en una relación de muchos a uno. También está vinculado a una categoría. . . indirectamente a través de su subcategoría. Veremos demostraciones sólidas de toda esta gimnasia, pero por ahora, necesitamos apreciar los conceptos y asegurarnos de que estamos 100% claros.
El siguiente es el Order
modelo y su migración:
$ php artisan make:model Order -m
Model created successfully.
Created Migration: 2021_01_26_144157_create_orders_table
En aras de la brevedad, incluiré solo algunos de los campos importantes de la migración. Con eso quiero decir, los detalles de una orden pueden contener muchas cosas, pero las restringiremos a unas pocas para que podamos enfocarnos en el concepto de relaciones modelo.
class CreateOrdersTable extends Migration
{
public function up()
{
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->string('status');
$table->unsignedInteger('total_value');
$table->unsignedInteger('taxes');
$table->unsignedInteger('shipping_charges');
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');
});
}
}
Se ve bien, pero, ¡espera un minuto! ¿Dónde están los artículos en este orden? Como establecimos anteriormente, existe una relación de muchos a muchos entre los pedidos y los artículos, por lo que una clave externa simple no funciona. La solución es la denominada mesa de unión o mesa intermedia. En otras palabras, necesitamos una tabla de unión para almacenar el mapeo de muchos a muchos entre pedidos y artículos. Ahora, en el mundo de Laravel, hay una convención incorporada que seguimos para ahorrar tiempo: si creo una nueva tabla usando la forma singular de los dos nombres de tablas, las coloco en el orden del diccionario y las unimos usando un guión bajo, Laravel lo reconocerá automáticamente como la tabla de unión.
En nuestro caso, la mesa de unión se llamará item_order
(la palabra "artículo" viene antes de "orden" en un diccionario). Además, como se explicó anteriormente, esta tabla de unión normalmente contendrá solo dos columnas, claves externas para cada tabla.
Podríamos crear un modelo + migración aquí, pero el modelo nunca se usará ya que es más una meta. Por lo tanto, creamos una nueva migración en Laravel y le decimos qué es qué.
$ php artisan make:migration create_item_order_table --create="item_order"
Created Migration: 2021_01_27_093127_create_item_order_table
Esto da como resultado una nueva migración, que cambiaremos de la siguiente manera:
class CreateItemOrderTable extends Migration
{
public function up()
{
Schema::create('item_order', function (Blueprint $table) {
$table->unsignedBigInteger('order_id');
$table->foreign('order_id')
->references('id')
->on('orders')
->onDelete('cascade');
$table->unsignedBigInteger('item_id');
$table->foreign('item_id')
->references('id')
->on('items')
->onDelete('cascade');
});
}
}
Cómo acceder realmente a estas relaciones a través de las llamadas al método Eloquent es un tema para más adelante, pero tenga en cuenta que primero debemos crear minuciosamente, a mano, estas claves externas. Sin estos, no hay Eloquent y no hay "inteligente" en Laravel. 🙂
¿Ya llegamos? Bueno, casi . . .
Solo tenemos un par de modelos más de los que preocuparnos. La primera es la tabla de facturas, y recordará que decidimos convertirla en una relación uno a uno con los pedidos.
$ php artisan make:model Invoice -m
Model created successfully.
Created Migration: 2021_01_27_101116_create_invoices_table
En las primeras secciones de este artículo, vimos que una forma de hacer cumplir una relación uno a uno es hacer que la clave principal de la tabla secundaria también sea la clave externa. En la práctica, casi nadie adopta este enfoque demasiado cauteloso, y la gente generalmente diseña el esquema como lo haría para una relación de uno a muchos. Mi opinión es que un enfoque intermedio es mejor; simplemente haga que la clave externa sea única y se ha asegurado de que las ID del modelo principal no se puedan repetir:
class CreateInvoicesTable extends Migration
{
public function up()
{
Schema::create('invoices', function (Blueprint $table) {
$table->id();
$table->timestamp('raised_at')->nullable();
$table->string('status');
$table->unsignedInteger('totalAmount');
$table->unsignedBigInteger('order_id')->unique();
$table->foreign('order_id')
->references('id')
->on('orders')
->onDelete('cascade')
->unique();
});
}
}
Y sí, por enésima vez, soy consciente de que a esta tabla de facturas le faltan muchas cosas; sin embargo, nuestro enfoque aquí es ver cómo las relaciones del modelo funcionan y no diseñar una base de datos completa.
Bien, hemos llegado al punto en el que necesitamos crear la migración final de nuestro sistema (¡espero!). La atención se centra ahora en Transaction
modelo, que decidimos anteriormente está vinculado a la Order
modelo. Por cierto, aquí tienes un ejercicio: Transaction
En cambio, el modelo esté vinculado al Invoice
¿modelo? ¿Por qué y por qué no? 🙂
$ php artisan make:model Transaction -m
Model created successfully.
Created Migration: 2021_01_31_145806_create_transactions_table
Y la migración:
class CreateTransactionsTable extends Migration
{
public function up()
{
Schema::create('transactions', function (Blueprint $table) {
$table->id();
$table->timestamp('executed_at');
$table->string('status');
$table->string('payment_mode');
$table->string('transaction_reference')->nullable();
$table->unsignedBigInteger('order_id');
$table->foreign('order_id')
->references('id')
->on('orders')
->onDelete('cascade');
});
}
}
¡Uf! Eso fue un trabajo duro. . . Ejecutemos las migraciones y veamos cómo lo estamos haciendo a los ojos de la base de datos.
$ php artisan migrate:fresh
Dropped all tables successfully.
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (3.45ms)
Migrating: 2021_01_26_093326_create_categories_table
Migrated: 2021_01_26_093326_create_categories_table (2.67ms)
Migrating: 2021_01_26_140845_create_sub_categories_table
Migrated: 2021_01_26_140845_create_sub_categories_table (3.83ms)
Migrating: 2021_01_26_141421_create_items_table
Migrated: 2021_01_26_141421_create_items_table (6.09ms)
Migrating: 2021_01_26_144157_create_orders_table
Migrated: 2021_01_26_144157_create_orders_table (4.60ms)
Migrating: 2021_01_27_093127_create_item_order_table
Migrated: 2021_01_27_093127_create_item_order_table (3.05ms)
Migrating: 2021_01_27_101116_create_invoices_table
Migrated: 2021_01_27_101116_create_invoices_table (3.95ms)
Migrating: 2021_01_31_145806_create_transactions_table
Migrated: 2021_01_31_145806_create_transactions_table (3.54ms)
¡Alabado sea el Señor! 🙏🏻🙏🏻 Parece que hemos sobrevivido al momento del juicio.
¡Y con eso, estamos listos para pasar a la definición de relaciones modelo! Para eso, necesitamos volver a la lista que creamos anteriormente, describiendo el tipo de relaciones directas entre modelos (tablas).
Para empezar, hemos establecido que existe una relación de uno a muchos entre usuarios y pedidos. Podemos confirmar esto yendo al archivo de migración de pedidos y viendo la presencia del campo user_id
Ya está. Este campo es lo que crea la relación, porque cualquier relación que estemos interesados en establecer debe ser respetada por la base de datos primero; el resto (sintaxis elocuente y dónde escribir qué función) es pura formalidad.
En otras palabras, la relación ya existe. Solo necesitamos decirle a Eloquent que lo haga disponible en tiempo de ejecución. Empecemos con el Order
modelo, donde declaramos que pertenece al User
modelo:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Order extends Model
{
use HasFactory;
public function user() {
return $this->belongsTo(User::class);
}
}
La sintaxis debe resultarle familiar; declaramos una función llamada user()
, que sirve para acceder al usuario propietario de este pedido (el nombre de la función puede ser cualquier cosa; lo que importa es lo que devuelve). Piense de nuevo por un momento: si no hubiera una base de datos ni claves externas, una declaración como $this->belongsTo
no tendría sentido. Es solo porque hay una clave externa en el orders
tabla que Laravel puede usar esa user_id
para buscar al usuario con el mismo id
y devuélvelo. Por sí solo, sin la cooperación de la base de datos, Laravel no puede crear relaciones de la nada.
Ahora, también sería bueno poder escribir $user->orders
para acceder a los pedidos de un usuario. Esto significa que tenemos que ir al User
modelar y escribir una función para la parte "muchos" de esta relación de uno a muchos:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
use HasFactory;
public $timestamps = false;
public function orders() {
return $this->hasMany(Order::class);
}
}
Sí, modifiqué mucho el valor predeterminado User
model porque no necesitamos todas las demás funciones para este tutorial. De todos modos, el User
la clase ahora tiene un método llamado orders()
, que dice que un usuario puede asociarse con varios pedidos. En el mundo ORM, decimos que el orders()
La relación aquí es la inversa de la user()
relación que tuvimos en el Order
modelo.
¡Pero espere un minuto! ¿Cómo está funcionando esta relación? Quiero decir, no hay nada en el nivel de la base de datos que tenga múltiples conexiones saliendo del users
mesa a la orders
mesa.
De hecho, hay is una conexión existente, y resulta que es suficiente por sí solo: la referencia de clave externa almacenada en el orders
¡mesa! Esto es, cuando decimos algo como $user->orders
, Laravel golpea el orders()
funciona y sabe al mirarlo que hay una clave externa en el orders
mesa. Entonces, hace un poco SELECT * FROM orders WHERE user_id = 23
y devuelve los resultados de la consulta como una colección. Por supuesto, el objetivo de tener un ORM es olvidarse de SQL, pero no debemos olvidar por completo que la base subyacente es el RDBMS que ejecuta consultas SQL.
A continuación, repasemos los modelos de pedidos y facturas, donde tenemos una relación de uno a uno:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Order extends Model
{
use HasFactory;
public $timestamps = false;
public function user() {
return $this->belongsTo(User::class);
}
public function invoice() {
return $this->hasOne(Invoice::class);
}
}
Y el modelo de factura:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Invoice extends Model
{
use HasFactory;
public $timestamps = false;
public function order() {
return $this->belongsTo(Order::class);
}
}
Tenga en cuenta que en el nivel de la base de datos, así como casi en el nivel Eloquent, es una relación típica de uno a muchos; Acabamos de agregar algunos controles para asegurarnos de que se mantenga uno a uno.
Ahora llegamos a otro tipo de relación: la de muchos a muchos entre pedidos y artículos. Recuerde que ya hemos creado una tabla intermedia llamada item_order
que almacena el mapeo entre las claves primarias. Si todo esto se ha hecho correctamente, definir la relación y trabajar con ella es trivial. Según los documentos de Laravel, para definir una relación de muchos a muchos, sus métodos deben devolver un belongsToMany()
ejemplo.
Entonces, en el Item
modelo:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Item extends Model
{
use HasFactory;
public $timestamps = false;
public function orders() {
return $this->belongsToMany(Order::class);
}
}
Sorprendentemente, la relación inversa es casi idéntica:
class Order extends Model
{
/* ... other code */
public function items() {
return $this->belongsToMany(Item::class);
}
}
¡Y eso es! Siempre que hayamos seguido las convenciones de nomenclatura correctamente, Laravel puede deducir las asignaciones y su ubicación.
Dado que se han cubierto los tres tipos fundamentales de relaciones (uno a uno, uno a muchos, muchos a muchos), dejaré de escribir los métodos para otros modelos, ya que estarán a lo largo del mismas líneas. En su lugar, creemos las fábricas para estos modelos, creemos algunos datos ficticios y veamos estas relaciones en acción.
¿Como hacemos eso? Bueno, tomemos el camino rápido y sucio y coloquemos todo en el archivo sembrador predeterminado. Luego, cuando ejecutemos las migraciones, también ejecutaremos la sembradora. Entonces, esto es lo que mi DatabaseSeeder.php
el archivo se ve así:
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\Category;
use App\Models\SubCategory;
use App\Models\Item;
use App\Models\Order;
use App\Models\Invoice;
use App\Models\User;
use Faker;
class DatabaseSeeder extends Seeder
{
public function run()
{
$faker = Faker\Factory::create();
// Let's make two users
$user1 = User::create(['name' => $faker->name]);
$user2 = User::create(['name' => $faker->name]);
// Create two categories, each having two subcategories
$category1 = Category::create(['name' => $faker->word]);
$category2 = Category::create(['name' => $faker->word]);
$subCategory1 = SubCategory::create(['name' => $faker->word, 'category_id' => $category1->id]);
$subCategory2 = SubCategory::create(['name' => $faker->word, 'category_id' => $category1->id]);
$subCategory3 = SubCategory::create(['name' => $faker->word, 'category_id' => $category2->id]);
$subCategory4 = SubCategory::create(['name' => $faker->word, 'category_id' => $category2->id]);
// After categories, well, we have items
// Let's create two items each for sub-category 2 and 4
$item1 = Item::create([
'sub_category_id' => 2,
'name' => $faker->name,
'description' => $faker->text,
'type' => $faker->word,
'price' => $faker->randomNumber(2),
'quantity_in_stock' => $faker->randomNumber(2),
]);
$item2 = Item::create([
'sub_category_id' => 2,
'name' => $faker->name,
'description' => $faker->text,
'type' => $faker->word,
'price' => $faker->randomNumber(3),
'quantity_in_stock' => $faker->randomNumber(2),
]);
$item3 = Item::create([
'sub_category_id' => 4,
'name' => $faker->name,
'description' => $faker->text,
'type' => $faker->word,
'price' => $faker->randomNumber(4),
'quantity_in_stock' => $faker->randomNumber(2),
]);
$item4 = Item::create([
'sub_category_id' => 4,
'name' => $faker->name,
'description' => $faker->text,
'type' => $faker->word,
'price' => $faker->randomNumber(1),
'quantity_in_stock' => $faker->randomNumber(3),
]);
// Now that we have users and items, let's make user1 place a couple of orders
$order1 = Order::create([
'status' => 'confirmed',
'total_value' => $faker->randomNumber(3),
'taxes' => $faker->randomNumber(1),
'shipping_charges' => $faker->randomNumber(2),
'user_id' => $user1->id
]);
$order2 = Order::create([
'status' => 'waiting',
'total_value' => $faker->randomNumber(3),
'taxes' => $faker->randomNumber(1),
'shipping_charges' => $faker->randomNumber(2),
'user_id' => $user1->id
]);
// now, assigning items to orders
$order1->items()->attach($item1);
$order1->items()->attach($item2);
$order1->items()->attach($item3);
$order2->items()->attach($item1);
$order2->items()->attach($item4);
// and finally, create invoices
$invoice1 = Invoice::create([
'raised_at' => $faker->dateTimeThisMonth(),
'status' => 'settled',
'totalAmount' => $faker->randomNumber(3),
'order_id' => $order1->id,
]);
}
}
Y ahora configuramos la base de datos nuevamente y la iniciamos:
$ php artisan migrate:fresh --seed
Dropped all tables successfully.
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (43.81ms)
Migrating: 2021_01_26_093326_create_categories_table
Migrated: 2021_01_26_093326_create_categories_table (2.20ms)
Migrating: 2021_01_26_140845_create_sub_categories_table
Migrated: 2021_01_26_140845_create_sub_categories_table (4.56ms)
Migrating: 2021_01_26_141421_create_items_table
Migrated: 2021_01_26_141421_create_items_table (5.79ms)
Migrating: 2021_01_26_144157_create_orders_table
Migrated: 2021_01_26_144157_create_orders_table (6.40ms)
Migrating: 2021_01_27_093127_create_item_order_table
Migrated: 2021_01_27_093127_create_item_order_table (4.66ms)
Migrating: 2021_01_27_101116_create_invoices_table
Migrated: 2021_01_27_101116_create_invoices_table (6.70ms)
Migrating: 2021_01_31_145806_create_transactions_table
Migrated: 2021_01_31_145806_create_transactions_table (6.09ms)
Database seeding completed successfully.
¡Todo bien! Ahora es la parte final de este artículo, donde simplemente accedemos a estas relaciones y confirmamos todo lo que hemos aprendido hasta ahora. Estarás encantado de saber (espero) que esta será una sección ligera y divertida.
Y ahora, activemos el componente Laravel más divertido: ¡la consola interactiva Tinker!
$ php artisan tinker
Psy Shell v0.10.6 (PHP 8.0.0 — cli) by Justin Hileman
>>>
Accessing one-to-one model relationships in Laravel Eloquent
Bien, entonces, primero, accedamos a la relación uno a uno que tenemos en nuestros modelos de pedido y factura:
>>> $order = Order::find(1);
[!] Aliasing 'Order' to 'App\Models\Order' for this Tinker session.
=> App\Models\Order {#4108
id: 1,
status: "confirmed",
total_value: 320,
taxes: 5,
shipping_charges: 12,
user_id: 1,
}
>>> $order->invoice
=> App\Models\Invoice {#4004
id: 1,
raised_at: "2021-01-21 19:20:31",
status: "settled",
totalAmount: 314,
order_id: 1,
}
¿Notaste algo? Recuerde que la forma en que se ha hecho a nivel de base de datos, esta relación es de uno a muchos, si no fuera por las restricciones adicionales. Entonces, Laravel podría haber devuelto una colección de objetos (o solo un objeto) como resultado, y eso sería técnicamente preciso. PERO . . . le hemos dicho a Laravel que es una relación uno a uno, por lo que el resultado es una única instancia de Eloquent. Observa cómo ocurre lo mismo al acceder a la relación inversa:
$invoice = Invoice::find(1);
[!] Aliasing 'Invoice' to 'App\Models\Invoice' for this Tinker session.
=> App\Models\Invoice {#3319
id: 1,
raised_at: "2021-01-21 19:20:31",
status: "settled",
totalAmount: 314,
order_id: 1,
}
>>> $invoice->order
=> App\Models\Order {#4042
id: 1,
status: "confirmed",
total_value: 320,
taxes: 5,
shipping_charges: 12,
user_id: 1,
}
Accessing one-to-many model relationships in Laravel Eloquent
Tenemos una relación de uno a muchos entre usuarios y pedidos. "Juguemos" con esto ahora y veamos el resultado:
>>> User::find(1)->orders;
[!] Aliasing 'User' to 'App\Models\User' for this Tinker session.
=> Illuminate\Database\Eloquent\Collection {#4291
all: [
App\Models\Order {#4284
id: 1,
status: "confirmed",
total_value: 320,
taxes: 5,
shipping_charges: 12,
user_id: 1,
},
App\Models\Order {#4280
id: 2,
status: "waiting",
total_value: 713,
taxes: 4,
shipping_charges: 80,
user_id: 1,
},
],
}
>>> Order::find(1)->user
=> App\Models\User {#4281
id: 1,
name: "Dallas Kshlerin",
}
Exactamente como se esperaba, acceder a las órdenes de un usuario da como resultado una colección de registros, mientras que la inversa produce solo una User
objeto. En otras palabras, uno a muchos.
Accessing many-to-many model relationships in Laravel Eloquent
Ahora, exploremos una relación de muchos a muchos. Tenemos una de esas relaciones entre artículos y pedidos:
>>> $item1 = Item::find(1);
[!] Aliasing 'Item' to 'App\Models\Item' for this Tinker session.
=> App\Models\Item {#4253
id: 1,
name: "Russ Kutch",
description: "Deserunt voluptatibus omnis ut cupiditate doloremque. Perspiciatis officiis odio et accusantium alias aut. Voluptatum provident aut ut et.",
type: "adipisci",
price: 26,
quantity_in_stock: 65,
sub_category_id: 2,
}
>>> $order1 = Order::find(1);
=> App\Models\Order {#4198
id: 1,
status: "confirmed",
total_value: 320,
taxes: 5,
shipping_charges: 12,
user_id: 1,
}
>>> $order1->items
=> Illuminate\Database\Eloquent\Collection {#4255
all: [
App\Models\Item {#3636
id: 1,
name: "Russ Kutch",
description: "Deserunt voluptatibus omnis ut cupiditate doloremque. Perspiciatis officiis odio et accusantium alias aut. Voluptatum provident aut ut et.",
type: "adipisci",
price: 26,
quantity_in_stock: 65,
sub_category_id: 2,
pivot: Illuminate\Database\Eloquent\Relations\Pivot {#4264
order_id: 1,
item_id: 1,
},
},
App\Models\Item {#3313
id: 2,
name: "Mr. Green Cole",
description: "Maxime beatae porro commodi fugit hic. Et excepturi natus distinctio qui sit qui. Est non non aut necessitatibus aspernatur et aspernatur et. Voluptatem possimus consequatur exercitationem et.",
type: "pariatur",
price: 381,
quantity_in_stock: 82,
sub_category_id: 2,
pivot: Illuminate\Database\Eloquent\Relations\Pivot {#4260
order_id: 1,
item_id: 2,
},
},
App\Models\Item {#4265
id: 3,
name: "Brianne Weissnat IV",
description: "Delectus ducimus quia voluptas fuga sed eos esse. Rerum repudiandae incidunt laboriosam. Ea eius omnis autem. Cum pariatur aut voluptas sint aliquam.",
type: "non",
price: 3843,
quantity_in_stock: 26,
sub_category_id: 4,
pivot: Illuminate\Database\Eloquent\Relations\Pivot {#4261
order_id: 1,
item_id: 3,
},
},
],
}
>>> $item1->orders
=> Illuminate\Database\Eloquent\Collection {#4197
all: [
App\Models\Order {#4272
id: 1,
status: "confirmed",
total_value: 320,
taxes: 5,
shipping_charges: 12,
user_id: 1,
pivot: Illuminate\Database\Eloquent\Relations\Pivot {#4043
item_id: 1,
order_id: 1,
},
},
App\Models\Order {#4274
id: 2,
status: "waiting",
total_value: 713,
taxes: 4,
shipping_charges: 80,
user_id: 1,
pivot: Illuminate\Database\Eloquent\Relations\Pivot {#4257
item_id: 1,
order_id: 2,
},
},
],
}
Esta salida puede ser un poco mareante de leer, pero observe que item1 es parte de los elementos de order1, y viceversa, que es como configuramos las cosas. También echemos un vistazo a la tabla intermedia que almacena las asignaciones:
>>> use DB;
>>> DB::table('item_order')->select('*')->get();
=> Illuminate\Support\Collection {#4290
all: [
{#4270
+"order_id": 1,
+"item_id": 1,
},
{#4276
+"order_id": 1,
+"item_id": 2,
},
{#4268
+"order_id": 1,
+"item_id": 3,
},
{#4254
+"order_id": 2,
+"item_id": 1,
},
{#4267
+"order_id": 2,
+"item_id": 4,
},
],
}
Conclusión
¡Sí, es así, de verdad! Ha sido un artículo muy largo, pero espero que haya sido útil. ¿Es eso todo lo que uno necesita saber sobre los modelos de Laravel?
Tristemente no. La madriguera del conejo es muy, muy profunda, y hay muchos conceptos más desafiantes como Relaciones polimórficas y la optimización del rendimiento, y todo eso, que encontrará a medida que crezca como desarrollador de Laravel. Por ahora, lo que cubre este artículo es suficiente para el 70% de los desarrolladores el 70% del tiempo, aproximadamente. Pasará mucho tiempo antes de que sienta la necesidad de actualizar sus conocimientos.
Con esa salvedad fuera del camino, quiero que se lleve esta idea más importante: nada es magia oscura o está fuera de alcance en programación. Es solo que no entendemos los cimientos y cómo se construyen las cosas, lo que nos hace luchar y sentirnos frustrados.
Entonces . . . ?
¡Invierte en ti mismo! Cursos, libros, artículos, otras comunidades de programación (Python es mi recomendación número 1): use todos los recursos que pueda encontrar y consúmalos con regularidad, aunque sea lentamente. Muy pronto, la cantidad de casos en los que es probable que arruine todo disminuirá drásticamente.
De acuerdo, suficiente predicación. ¡Que tengas un buen día! 🙂