Geekflare est soutenu par notre public. Nous pouvons gagner des commissions d'affiliation en achetant des liens sur ce site.
Partager sur:

Comprendre les relations de modèle dans Laravel Eloquent

Scanner de sécurité des applications Web Invicti – la seule solution qui offre une vérification automatique des vulnérabilités avec Proof-Based Scanning™.

Les modèles et leurs relations sont au cœur de Laravel Eloquent. S'ils vous donnent du fil à retordre ou si vous ne parvenez pas à trouver un guide simple, convivial et complet, commencez ici!

Assis de l'autre côté de leur article de programmation, il est facile pour l'écrivain de feindre ou de faire exploser l'aura d'expertise / de prestige que fournit la plateforme. Mais je vais être honnête - j'ai eu un extrêmement temps dur apprendre Laravel, ne serait-ce que parce que c'était mon premier framework full-stack. L'une des raisons était que je ne l'utilisais pas au travail et que je l'explorais par curiosité; alors, je ferais une tentative, j'arrivais à un point, je devenais confus, j'abandonnais et j'oublierais finalement tout. J'ai dû le faire 5 à 6 fois avant que cela ne commence à avoir un sens pour moi (bien sûr, la documentation n'aide pas).

Mais ce qui n'avait toujours pas de sens était Eloquent. Ou du moins, les relations entre les modèles (car Eloquent est trop grand pour apprendre complètement). Les exemples de modélisation des auteurs et des articles de blog sont une blague car les vrais projets sont beaucoup plus complexes; malheureusement, les documents officiels utilisent les mêmes exemples (ou similaires). Ou même si je suis tombé sur un article / une ressource utile, l'explication était si mauvaise ou si manquante qu'elle était tout simplement inutile.

(Au fait, j'ai déjà été attaqué pour avoir attaqué la documentation officielle, donc si vous avez des idées similaires, voici ma réponse standard: allez consulter la documentation de Django puis parlez-moi.)

Finalement, petit à petit, cela s'est mis en place et a eu du sens. J'ai enfin pu modéliser correctement les projets et utiliser les modèles confortablement. Puis un jour, je suis tombé sur quelques astuces de Collections qui rendent ce travail plus agréable. Dans cet article, j'ai l'intention de couvrir tout cela, en commençant par les bases, puis en couvrant tous les cas d'utilisation possibles que vous rencontrerez dans de vrais projets.

Pourquoi les relations avec les modèles éloquents sont-elles difficiles?

Malheureusement, je rencontre beaucoup trop de développeurs Laravel qui ne comprennent pas correctement les modèles.

Mais pourquoi?

Même aujourd'hui, quand il y a une explosion de cours, d'articles et de vidéos sur Laravel, la compréhension globale est mauvaise. Je pense que c'est un point important et mérite réflexion.

Si vous me demandez, je dirai que les relations de modèle éloquentes ne sont pas du tout difficiles. Du moins vu du point de vue de la définition de «dur». Les migrations de schémas en direct sont difficiles; écrire un nouveau moteur de création de modèles est difficile; contribuer au code au cœur de Laravel est difficile. Par rapport à ceux-ci, apprendre et utiliser un ORM. . . eh bien, ça ne peut pas être dur! 🤭🤭

Ce qui se passe réellement, c'est que Développeurs PHP apprenant Laravel trouver Eloquent dur. C'est le vrai problème sous-jacent, et à mon avis, plusieurs facteurs y contribuent (alerte d'opinion sévère et impopulaire!):

  • Avant Laravel, l'exposition à un framework pour la plupart des développeurs PHP était CodeIgniter (c'est toujours vivante, au fait, même si c'est devenu plus Laravel / CakePHP). Dans l'ancienne communauté CodeIgniter (s'il y en avait une), la «meilleure pratique» était de coller directement les requêtes SQL là où c'était nécessaire. Et bien que nous ayons un nouveau CodeIgniter aujourd'hui, les habitudes ont perduré. En conséquence, quand apprendre Laravel, l'idée d'un ORM est 100% nouvelle pour les développeurs PHP.
  • En abandonnant le très petit pourcentage de PHP exposé à des frameworks tels que Yii, CakePHP, etc., les autres sont habitués à travailler dans le cœur de PHP ou dans un environnement tel que WordPress. Et là encore, un état d'esprit basé sur la POO n'existe pas, donc un framework, un conteneur de service, un modèle de conception, un ORM. . . ce sont des concepts étrangers.
  • Il y a peu ou pas de concept d'apprentissage continu dans le monde PHP. Le développeur moyen est heureux de travailler avec des configurations à serveur unique utilisant des bases de données relationnelles et émettant des requêtes écrites sous forme de chaînes. Programmation asynchrone, sockets Web, HTTP 2/3, Linux (oubliez Docker), tests unitaires, conception pilotée par domaine - ce sont toutes des idées étrangères pour une proportion écrasante de développeurs PHP. En conséquence, lire quelque chose de nouveau et de stimulant, au point que l'on le trouve confortable, ne se produit pas lorsque Eloquent est rencontré.
  • La compréhension globale des bases de données et de la modélisation est également médiocre. Étant donné que la conception de bases de données est directement, inséparablement liée aux modèles Eloquent, elle élève la barre de difficulté plus haut.

Je ne veux pas être dur et généraliser à l'échelle mondiale - il y a excellent Les développeurs PHP aussi, et beaucoup d'entre eux, mais leur pourcentage global est très faible.

Si vous lisez ceci, cela signifie que vous avez franchi toutes ces barrières, rencontré Laravel et joué avec Eloquent.

Toutes nos félicitations! 👏

Tu y es presque. Tous les éléments de base sont en place et il suffit de les parcourir dans le bon ordre et dans les détails. En d'autres termes, commençons au niveau de la base de données.

Modèles de base de données: relations et cardinalité

Pour simplifier les choses, supposons que nous travaillons avec bases de données relationnelles seulement tout au long de cet article. L'une des raisons est que les ORM ont été initialement développés pour les bases de données relationnelles; l'autre raison est que les SGBDR sont toujours extrêmement populaires.

Modèle de données

Tout d'abord, comprenons mieux les modèles de données. L'idée d'un modèle (ou d'un données modèle, pour être plus précis), provient de la base de données. Pas de base de données, pas de données et donc pas de modèle de données. Et qu'est-ce qu'un modèle de données? C'est tout simplement la façon dont vous décidez de stocker / structurer vos données. Par exemple, dans un magasin de commerce électronique, vous pouvez tout stocker dans une seule table géante (pratique HORRIBLE, mais malheureusement, pas rare dans le monde PHP); ce serait votre modèle de données. Vous pouvez également diviser les données en 20 tables principales et 16 tables de connexion; c'est aussi un modèle de données.

Notez également que la manière dont les données sont structurées dans la base de données ne doit pas nécessairement correspondre à 100% à la manière dont elles sont organisées dans l'ORM du framework. Cependant, l'effort est toujours de garder les choses aussi proches que possible afin que nous n'ayons plus une chose à garder à l'esprit lors du développement.

Cardinalité

Sortons aussi rapidement ce terme: la cardinalité. Il se réfère simplement à «compter», en gros. Donc, 1, 2, 3. . . peut tout être la cardinalité de quelque chose. Fin de l'histoire. Continuons d'avancer!

Les relations

Désormais, chaque fois que nous stockons des données dans n'importe quel type de système, il existe des façons dont les points de données peuvent être liés les uns aux autres. Je sais que cela semble abstrait et ennuyeux, mais supportez-moi un peu. Les modes de connexion des différents éléments de données sont appelés des relations. Voyons d'abord quelques exemples non liés à la base de données afin d'être convaincus que nous comprenons parfaitement l'idée.

  • Si nous stockons tout dans un tableau, un possible relation amoureuse is: l'élément de données suivant est à un index supérieur à l'index précédent de 1.
  • Si nous stockons des données dans un arbre binaire, une relation possible est que l'arbre enfant à gauche a toujours des valeurs plus petites que celles du nœud parent (si nous choisissons de maintenir l'arbre de cette façon).
  • Si nous stockons des données sous la forme d'un tableau de tableaux de longueur égale, nous pouvons imiter une matrice, puis ses propriétés deviennent les relations pour nos données.

On voit donc que le mot «relation», dans le contexte des données, n'a pas de sens fixe. En fait, si deux personnes regardaient les mêmes données, elles pourraient identifier deux relations de données très différentes (bonjour, statistiques!) Et les deux pourraient être valides.

Relationnel bases de données

Sur la base de tous les termes dont nous avons discuté jusqu'à présent, nous pouvons enfin parler de quelque chose qui a un lien direct vers des modèles dans un cadre Web (Laravel) - des bases de données relationnelles. Pour la plupart d'entre nous, la base de données principale utilisée est MySQL, MariaDB, PostgreSQL, MSSQL, SQL Server, SQLite ou quelque chose du genre. Nous pourrions également savoir vaguement que ceux-ci sont appelés SGBDR, mais la plupart d'entre nous ont oublié ce que cela signifie réellement et pourquoi est-ce important.

Le «R» dans SGBDR signifie relationnel, bien sûr. Ce n'est pas un terme choisi arbitrairement; par cela, nous soulignons le fait que ces systèmes de bases de données sont conçus pour fonctionner efficacement avec les relations entre les données stockées. En fait, «relation» a ici une signification mathématique stricte, et même si aucun développeur n'a besoin de s'en soucier, il est utile de savoir qu'il existe une mathématique rigoureuse fondation sous ces types de bases de données.

Explorez ces ressources pour apprendre SQL et NoSQL.

D'accord, nous savons donc par expérience que les données du SGBDR sont stockées sous forme de tables. Où sont donc les relations?

Types de relations dans le SGBDR

C'est peut-être la partie la plus importante de tout le sujet de Laravel et des relations modèles. Si vous ne comprenez pas cela, Eloquent n'aura jamais de sens, alors faites attention pendant les prochaines minutes (ce n'est même pas si difficile).

Un SGBDR permet nous pour avoir des relations entre les données - au niveau de la base de données. Cela signifie que ces relations ne sont pas irréalisables / imaginaires / subjectives et peuvent être créées ou inférées par différentes personnes avec le même résultat.

Dans le même temps, il existe certaines capacités / outils dans un SGBDR qui nous permettent de créer et d'appliquer ces relations, telles que:

  • Clé primaire
  • Clé étrangère
  • contraintes

Je ne veux pas que cet article devienne un cours sur les bases de données, je suppose donc que vous connaissez ces concepts. Sinon, ou au cas où vous vous sentirez fragile dans votre confiance, je vous recommande cette vidéo conviviale (n'hésitez pas à explorer toute la série):

YouTube vidéo

Il se trouve que ces relations de type SGBDR sont également les plus courantes qui se produisent dans les applications du monde réel (pas toujours, car un réseau social est mieux modélisé sous forme de graphique et non comme une collection de tableaux). Alors, regardons-les un par un et essayons également de comprendre où ils pourraient être utiles.

Relation individuelle

Dans presque toutes les applications Web, il existe des comptes d'utilisateurs. En outre, ce qui suit est vrai (en général) à propos des utilisateurs et des comptes:

  • Un utilisateur ne peut avoir qu'un seul compte.
  • Un compte ne peut appartenir qu'à un seul utilisateur.

Oui, nous pouvons affirmer qu'une personne peut s'inscrire avec un autre e-mail et ainsi créer deux comptes, mais du point de vue de l'application Web, ce sont deux personnes différentes avec deux comptes différents. L'application n'affichera pas, par exemple, les données d'un compte dans un autre.

Ce que tout cela signifie, c'est que si vous avez une situation comme celle-ci dans votre application et que vous utilisez une base de données relationnelle, vous devez la concevoir comme une relation un-à-un. Notez que personne ne vous force artificiellement - il y a une situation claire dans le domaine de l'entreprise et vous utilisez une base de données relationnelle. . . ce n'est que lorsque ces deux conditions sont remplies que vous parvenez à une relation individuelle.

Pour cet exemple (utilisateurs et comptes), voici comment nous pouvons implémenter cette relation lors de la création du schéma:

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)
);

Remarquez le truc ici? C'est assez rare lors de la création d'applications en général, mais dans le accounts table, nous avons le terrain id défini à la fois comme clé primaire et clé étrangère! La propriété de clé étrangère la relie au users table (bien sûr 🙄) alors que la propriété de clé primaire rend le id colonne unique - une vraie relation un-à-un!

Certes, la fidélité de cette relation n'est pas garantie. Par exemple, rien ne m'empêche d'ajouter 200 nouveaux utilisateurs sans ajouter une seule entrée au accounts table. Si je fais ça, je me retrouve avec un one-to-zéro relation! 🤭🤭 Mais dans les limites de la structure pure, c'est le mieux que nous puissions faire. Si nous voulons empêcher l'ajout d'utilisateurs sans comptes, nous devons prendre l'aide d'une sorte de logique de programmation, soit sous la forme de déclencheurs de base de données ou de validations appliquées par Laravel.

Si vous commencez à vous stresser, j'ai de très bons conseils:

  • Vas-y doucement. Aussi lent que nécessaire. Au lieu d'essayer de terminer cet article et les 15 autres que vous avez mis en signet pour aujourd'hui, tenez-vous-en à celui-ci. Laissez-le prendre 3, 4, 5 jours si c'est ce qu'il faut - votre objectif devrait être de supprimer définitivement les relations modèles Eloquent de votre liste. Vous avez déjà sauté d'article en article, perdant plusieurs centaines d'heures et pourtant cela n'a pas aidé. Alors, faites quelque chose de différent cette fois. 😇
  • Bien que cet article parle de Laravel Eloquent, tout cela vient beaucoup plus tard. Le fondement de tout cela est le schéma de base de données, nous devons donc nous concentrer d'abord sur ce qu'il faut. Si vous ne pouvez pas travailler uniquement au niveau de la base de données (en supposant qu'il n'y a pas de framework dans le monde), alors les modèles et les relations n'auront jamais de sens. Alors, oubliez Laravel pour le moment. Complètement. Nous ne parlons et faisons que la conception de bases de données pour l'instant. Oui, je vais faire des références à Laravel de temps en temps, mais votre travail est de les ignorer complètement si elles compliquent l'image pour vous.
  • Plus tard, lisez un peu plus sur les bases de données et ce qu'elles offrent. Index, performances, déclencheurs, structures de données sous-jacentes et leur comportement, mise en cache, relations dans MongoDB. . . les sujets tangentiels que vous pouvez couvrir vous aideront en tant qu'ingénieur. N'oubliez pas que les modèles de cadre ne sont que des coquilles fantômes; la fonctionnalité réelle d'une plateforme provient de ses bases de données sous-jacentes.

Relation un-à-plusieurs

Je ne sais pas si vous avez réalisé cela, mais c'est le type de relation que nous créons tous intuitivement dans notre travail quotidien. Lorsque nous créons un orders table (un exemple hypothétique), par exemple, pour stocker une clé étrangère dans le users table, nous créons une relation un-à-plusieurs entre les utilisateurs et les commandes. Pourquoi donc? Eh bien, regardez à nouveau du point de vue de qui peut en avoir combien: un utilisateur est autorisé à avoir plus d'une commande, ce qui est à peu près le fonctionnement de tout le commerce électronique. Et vu du côté opposé, la relation dit qu'une commande ne peut appartenir qu'à un seul utilisateur, ce qui a également beaucoup de sens.

Dans la modélisation de données, les livres de SGBDR et la documentation système, cette situation est représentée schématiquement comme ceci:

Remarquez les trois lignes faisant une sorte de trident? C'est le symbole pour «plusieurs», et donc ce diagramme indique qu'un utilisateur peut avoir plusieurs commandes.

À propos, ces comptes «plusieurs» et «un» que nous rencontrons à plusieurs reprises sont ce qu'on appelle la cardinalité d'une relation (rappelez-vous ce mot d'une section précédente?). Encore une fois, pour cet article, le terme n'a aucune utilité, mais il permet de connaître le concept au cas où il apparaîtrait lors d'entretiens ou de lectures supplémentaires.

Simple, non? Et en termes de SQL réel, la création de cette relation est également simple. En fait, c'est beaucoup plus simple que le cas d'une relation en tête-à-tête!

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 solution orders table stocke les ID utilisateur pour chaque commande. Puisqu'il n'y a aucune contrainte (restriction) que les ID utilisateur dans le orders la table doit être unique, cela signifie que nous pouvons répéter un seul identifiant plusieurs fois. C'est ce qui crée la relation un-à-plusieurs, et non une magie obscure qui se cache en dessous. Les ID utilisateur sont stockés de manière stupide dans le orders table, et SQL n'a aucun concept de un-à-plusieurs, un-à-un, etc. Mais une fois que nous stockons les données de cette façon, nous pouvons penser de l'existence d'une relation un-à-plusieurs.

Espérons que cela a du sens maintenant. Ou du moins, plus de sens qu'avant. 😅 N'oubliez pas que, comme toute autre chose, c'est une simple question de pratique, et une fois que vous avez fait cela 4 à 5 fois dans des situations réelles, vous n'y penserez même pas.

Relations plusieurs-à-plusieurs

Le prochain type de relation qui survient dans la pratique est la soi-disant relation plusieurs-à-plusieurs. Encore une fois, avant de se soucier des frameworks ou même de plonger dans les bases de données, pensons à un analogue du monde réel: les livres et les auteurs. Pensez à votre auteur préféré; ils ont écrit plus d'un livre, non? Dans le même temps, il est assez courant de voir plusieurs auteurs collaborer sur un livre (du moins dans le genre non fictionnel). Ainsi, un auteur peut écrire plusieurs livres, et plusieurs auteurs peuvent écrire un livre. Entre les deux entités (livre et auteur), cela forme une relation plusieurs-à-plusieurs.

Maintenant, étant donné qu'il est très peu probable que vous créiez une application du monde réel impliquant des bibliothèques ou des livres et des auteurs, pensons donc à quelques exemples supplémentaires. Dans un contexte B2B, un fabricant commande des articles à un fournisseur et reçoit à son tour une facture. La facture contiendra plusieurs articles, chacun indiquant la quantité et l'article fournis; par exemple, des morceaux de tuyau de 5 pouces x 200, etc. Dans cette situation, les articles et les factures ont une relation plusieurs-à-plusieurs (pensez-y et laissez-vous convaincre). Dans un système de gestion de flotte, les véhicules et les conducteurs auront une relation similaire. Dans un site de commerce électronique, les utilisateurs et les produits peuvent avoir une relation plusieurs-à-plusieurs si nous considérons des fonctionnalités telles que les favoris ou les listes de souhaits.

Assez juste, maintenant comment créer cette relation plusieurs-à-plusieurs en SQL? Sur la base de notre connaissance du fonctionnement de la relation un-à-plusieurs, il pourrait être tentant de penser que nous devrions stocker les clés étrangères de l'autre table dans les deux tables. Cependant, nous rencontrons des problèmes majeurs si nous essayons de le faire. Jetez un œil à cet exemple où les livres sont des auteurs censés avoir une relation plusieurs-à-plusieurs:

À première vue, tout va bien - les livres sont mappés aux auteurs exactement de manière plusieurs-à-plusieurs. Mais regardez attentivement le authors données de table: les identifiants 12 et 13 du livre sont tous deux écrits par Peter M. (identifiant auteur 2), à cause de quoi nous n'avons d'autre choix que de répéter les entrées. Non seulement le authors table a maintenant des problèmes d'intégrité des données ( normalisation et tout ça), les valeurs dans le id colonne se répètent maintenant. Cela signifie que dans la conception que nous avons choisie, il ne peut y avoir de colonne de clé primaire (car les clés primaires ne peuvent pas avoir de valeurs en double) et tout s'effondre.

De toute évidence, nous avons besoin d'une nouvelle façon de faire cela et, heureusement, ce problème a déjà été résolu. Étant donné que le stockage des clés étrangères directement dans les deux tables complique les choses, la bonne façon de créer des relations plusieurs-à-plusieurs dans le SGBDR est de créer une soi-disant «table de jointure». L'idée est essentiellement de laisser les deux tables originales ne pas être dérangées et de créer une troisième table pour illustrer le mappage plusieurs-à-plusieurs.

Refaisons l'exemple qui a échoué pour contenir une table de jointure:

Notez qu'il y a eu des changements drastiques:

  • Le nombre de colonnes dans le authors la table est réduite.
  • Le nombre de colonnes dans le books la table est réduite.
  • Le nombre de lignes dans le authors table est réduite car il n'y a plus besoin de répétition.
  • Une nouvelle table appelée authors_books est apparu, contenant des informations sur l'ID d'auteur associé à l'ID de livre. Nous aurions pu nommer n'importe quoi à la table de jointure, mais par convention, c'est le résultat de simplement joindre les deux tables qu'elle représente, en utilisant un trait de soulignement.

La table de jointure n'a pas de clé primaire et, dans la plupart des cas, ne contient que deux colonnes - ID des deux tables. C'est presque comme si nous supprimions les colonnes de clé étrangère de notre exemple précédent et les collions dans cette nouvelle table. Puisqu'il n'y a pas de clé primaire, il peut y avoir autant de répétitions que nécessaire pour enregistrer toutes les relations.

Maintenant, nous pouvons voir de nos yeux comment la table de jointure affiche clairement les relations, mais comment y accéder dans nos applications? Le secret est lié au nom - joindre table. Ce n'est pas un cours sur les requêtes SQL, donc je ne vais pas y plonger mais l'idée est que si vous voulez tous les livres d'un auteur particulier dans une seule requête efficace, vous joignez les tables en SQL dans le même ordre -> authors, authors_books et booksL’ authors et authors_books les tables sont jointes sur id et author_id colonnes, respectivement, tandis que le authors_books et books les tables sont jointes sur le book_id et id colonnes, respectivement.

Épuisant, oui. Mais regardez le bon côté - nous avons fini tous la théorie / le travail de base nécessaires avant de s'attaquer aux modèles éloquents. Et laissez-moi vous rappeler que tout cela n'est pas facultatif! Ne pas connaître la conception de la base de données vous laissera pour toujours dans le pays de la confusion éloquente. De plus, tout ce qu'Eloquent fait ou essaie de faire reflète parfaitement ces détails au niveau de la base de données, il est donc facile de comprendre pourquoi essayer d'apprendre Eloquent tout en fuyant le SGBDR est un exercice futile.

Creating model relationships in Laravel Eloquent

Enfin, après un détour qui a duré environ 70,000 miles, nous sommes arrivés au point où nous pouvons parler d'Eloquent, de ses modèles, et comment les créer / les utiliser. Maintenant, nous avons appris dans la partie précédente de l'article que tout commence par la base de données et comment vous modélisez vos données. Cela m'a fait réaliser que je devrais utiliser un seul exemple complet où je démarre un nouveau projet. En même temps, je veux que cet exemple soit du monde réel, et non pas des blogs et des auteurs ou des livres et des étagères (qui sont également du monde réel, mais qui ont été faits à mort).

Imaginons un magasin qui vend des peluches. Supposons également que nous ayons reçu le document des exigences, à partir duquel nous pouvons identifier ces quatre entités dans le système: utilisateurs, commandes, factures, articles, catégories, sous-catégories et transactions. Oui, il y aura probablement plus de complications, mais mettons cela de côté et concentrons-nous sur la façon dont nous passons d'un document à une application.

Une fois que les principales entités du système ont été identifiées, nous devons réfléchir à la manière dont elles sont liées les unes aux autres, en termes de relations de base de données dont nous avons discuté jusqu'à présent. Voici ceux auxquels je peux penser:

  • Utilisateurs et commandes: un à plusieurs.
  • Commandes et factures: une à une. Je me rends compte que celui-ci n'est pas coupé et séché, et selon votre domaine d'activité, il peut y avoir une relation un à plusieurs, plusieurs à un ou plusieurs à plusieurs. Mais en ce qui concerne votre petit magasin de commerce électronique moyen, une commande ne donnera lieu qu'à une seule facture et vice versa.
  • Commandes et articles: plusieurs à plusieurs.
  •  Articles et catégories: plusieurs à un. Encore une fois, ce n'est pas le cas dans les grands sites de commerce électronique, mais nous avons une petite opération.
  • Catégories et sous-catégories: une à plusieurs. Encore une fois, vous trouverez la plupart des exemples du monde réel qui contredisent cela, mais bon, Eloquent est déjà assez difficile comme ça, alors ne compliquons pas la modélisation des données!
  • Commandes et transactions: une à plusieurs. J'aimerais également ajouter ces deux points pour justifier mon choix: 1) Nous aurions pu également ajouter une relation entre les transactions et les factures. C'est juste une décision de modélisation des données. 2) Pourquoi un à plusieurs ici? Eh bien, il est courant qu'un paiement de commande échoue pour une raison quelconque et réussisse la prochaine fois. Dans ce cas, nous avons deux transactions créées pour cette commande. Que nous souhaitons montrer ces transactions échouées ou non est une décision commerciale, mais c'est toujours une bonne idée de capturer des données précieuses.

Y a-t-il d'autres relations? Eh bien, beaucoup plus de relations sont possibles, mais elles ne sont pas pratiques. Par exemple, nous pouvons dire qu'un utilisateur a de nombreuses transactions, il devrait donc y avoir une relation entre elles. La chose à réaliser ici est qu'il existe déjà une relation indirecte: utilisateurs -> commandes -> transactions, et d'une manière générale, c'est assez bon car les SGBDR sont des bêtes pour joindre des tables. Deuxièmement, créer cette relation signifierait ajouter un user_id colonne à la transactions table. Si nous faisions cela pour chaque relation directe possible, nous ajouterions beaucoup plus de charge à la base de données (sous la forme de plus de stockage, en particulier si les UUID sont utilisés et de la maintenance des index), en chaînant le système global. Bien sûr, si l'entreprise dit qu'elle a besoin de données de transactions et qu'elle en a besoin dans les 1.5 seconde, nous pourrions décider d'ajouter cette relation et d'accélérer les choses (compromis, compromis ...).

Et maintenant, Mesdames et Messieurs, le moment est venu d'écrire le code proprement dit!

Laravel model relationships — real example with code

La phase suivante de cet article consiste à se salir les mains - mais d'une manière utile. Nous choisirons les mêmes entités de base de données que dans l'exemple de commerce électronique précédent, et nous verrons comment les modèles dans Laravel sont créés et connectés, dès l'installation de Laravel!

Naturellement, je suppose que votre environnement de développement est configuré et que vous savez comment installer et utiliser Composer pour gérer les dépendances.

$ composer global require laravel/installer -W
$ laravel new model-relationships-study

Ces deux commandes de console installent le programme d'installation de Laravel (le -W une partie est utilisée pour la mise à niveau car j'avais déjà une ancienne version installée). Et au cas où vous seriez curieux, au moment de l'écriture, la version de Laravel qui a été installée est la 8.5.9. Devez-vous paniquer et mettre à niveau également? Je vous le déconseille, car je ne m'attends pas à des changements majeurs entre Laravel 5 et Laravel 8 dans le cadre de notre application. Certaines choses ont changé et auront un impact sur cet article (comme les usines de modèles), mais je pense que vous pourrez porter le code.

Puisque nous avons déjà réfléchi au modèle de données et à leurs relations, la partie de la création des modèles sera triviale. Et vous verrez aussi (je sonne comme un disque rayé maintenant!) Comment il reflète le schéma de la base de données car il en dépend à 100%!

En d'autres termes, nous devons d'abord créer les migrations (et les fichiers de modèle) pour tous les modèles, qui seront appliqués à la base de données. Plus tard, nous pouvons travailler sur les modèles et aborder les relations.

Alors, par quel modèle commençons-nous? Le plus simple et le moins connecté, bien sûr. Dans notre cas, cela signifie que User modèle. Puisque Laravel est livré avec ce modèle (et ne peut pas fonctionner sans lui 🤣), modifions le fichier de migration et nettoyons également le modèle pour répondre à nos besoins simples.

Voici la classe de migration:

class CreateUsersTable extends Migration
{
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
        });
    }
}

Puisque nous ne construisons pas réellement un projet, nous n'avons pas besoin d'entrer dans les mots de passe, is_active, et tout ça. Notre users table n'aura que deux colonnes: l'id et le nom de l'utilisateur.

Créons la migration pour Category Suivant. Puisque Laravel nous permet de générer également le modèle en une seule commande, nous en profiterons, même si nous ne toucherons pas au fichier de modèle pour le moment.

$ php artisan make:model Category -m
Model created successfully.
Created Migration: 2021_01_26_093326_create_categories_table

Et voici la classe de migration:

class CreateCategoriesTable extends Migration
{
    public function up()
    {
        Schema::create('categories', function (Blueprint $table) {
            $table->id();
            $table->string('name');
        });
    }
}

Si vous êtes surpris de l'absence du down() fonction, ne sois pas; en pratique, vous finissez rarement par l'utiliser pour supprimer une colonne ou une table ou changer un type de colonne entraîne une perte de données qui ne peut pas être récupérée. En développement, vous vous retrouverez à supprimer la base de données entière, puis à réexécuter les migrations. Mais nous nous écartons, alors revenons en arrière et abordons la prochaine entité. Étant donné que les sous-catégories sont directement liées aux catégories, je pense que c'est une bonne idée de le faire ensuite.

$ php artisan make:model SubCategory -m
Model created successfully.
Created Migration: 2021_01_26_140845_create_sub_categories_table

Très bien, maintenant remplissons le fichier de migration:

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');
        });
    }
}

Comme vous pouvez le voir, nous ajoutons ici une colonne distincte, appelée category_id, qui stockera les identifiants du categories table. Pas de prix à deviner, cela crée une relation un à plusieurs au niveau de la base de données.

C'est maintenant au tour des objets:

$ php artisan make:model Item -m
Model created successfully.
Created Migration: 2021_01_26_141421_create_items_table

Et la migration:

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 vous pensez que les choses devraient être faites différemment, c'est très bien. Deux personnes auront rarement le même schéma et la même architecture. Notez une chose, qui est une bonne pratique en quelque sorte: j'ai stocké le prix sous forme d'entier.

Pourquoi ?

Eh bien, les gens ont réalisé que gérer les divisions flottantes et tout était laid et sujet aux erreurs du côté de la base de données, alors ils ont commencé à stocker le prix en termes de la plus petite unité monétaire. Par exemple, si nous opérions en USD, le champ de prix ici représenterait des cents. Dans tout le système, les valeurs et les calculs seront en centimes; ce n'est que lorsqu'il est temps d'afficher à l'utilisateur ou d'envoyer un PDF par e-mail, nous diviserons par 100 et arrondirons. Intelligent, hein?

Quoi qu'il en soit, notez qu'un élément est lié à une sous-catégorie dans une relation plusieurs-à-un. Il est également lié à une catégorie. . . indirectement via sa sous-catégorie. Nous verrons des démonstrations solides de toutes ces gymnastiques, mais pour l'instant, nous devons apprécier les concepts et nous assurer que nous sommes clairs à 100%.

La prochaine étape est la Order modèle et sa migration:

$ php artisan make:model Order -m
Model created successfully.
Created Migration: 2021_01_26_144157_create_orders_table

Par souci de concision, je n'inclurai que certains des champs importants de la migration. Je veux dire par là que les détails d'une commande peuvent contenir un grand nombre de choses, mais nous les limiterons à quelques-uns afin que nous puissions nous concentrer sur le concept de relations de modèle.

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');
        });
    }
}

Ça a l'air bien, mais attendez une minute! Où sont les articles dans cet ordre? Comme nous l'avons établi précédemment, il existe une relation plusieurs-à-plusieurs entre les commandes et les articles, donc une simple clé étrangère ne fonctionne pas. La solution est une table dite de jonction ou table intermédiaire. En d'autres termes, nous avons besoin d'une table de jointure pour stocker le mappage plusieurs-à-plusieurs entre les commandes et les articles. Maintenant, dans le monde de Laravel, il existe une convention intégrée que nous suivons pour gagner du temps: si je crée une nouvelle table en utilisant la forme singulière des deux noms de table, placez-les dans l'ordre du dictionnaire et joignez-les à l'aide d'un trait de soulignement, Laravel la reconnaîtra automatiquement comme table de jonction.

Dans notre cas, la table de jonction sera appelée item_order (le mot «élément» vient avant «ordre» dans un dictionnaire). De plus, comme expliqué précédemment, cette table de jointure ne contiendra normalement que deux colonnes, des clés étrangères pour chaque table.

Nous pourrions créer un modèle + migration ici, mais le modèle ne sera jamais utilisé car il s'agit plutôt d'une méta-chose. Ainsi, nous créons une nouvelle migration dans Laravel et lui disons quoi.

$ php artisan make:migration create_item_order_table --create="item_order"
Created Migration: 2021_01_27_093127_create_item_order_table

Cela se traduit par une nouvelle migration, que nous modifierons comme suit:

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');    
        });
    }
}

Comment accéder à ces relations via des appels de méthode Eloquent est un sujet pour plus tard, mais notez que nous devons d'abord créer minutieusement, à la main, ces clés étrangères. Sans cela, il n'y a pas d'éloquent et il n'y a pas de «intelligent» à Laravel. 🙂

Sommes-nous déjà là? Enfin presque. . .

Nous n'avons plus que quelques modèles à craindre. Le premier est le tableau des factures, et vous vous souviendrez que nous avons décidé d'en faire une relation individuelle avec les commandes.

$ php artisan make:model Invoice -m
Model created successfully.
Created Migration: 2021_01_27_101116_create_invoices_table

Dans les toutes premières sections de cet article, nous avons vu qu'une façon d'appliquer une relation un-à-un consiste à faire de la clé primaire de la table enfant la clé étrangère également. En pratique, presque personne n'adopte cette approche trop prudente et les gens conçoivent généralement le schéma comme ils le feraient pour une relation un-à-plusieurs. Mon point de vue est qu'une approche intermédiaire est meilleure; rendez simplement la clé étrangère unique et vous vous êtes assuré que les ID du modèle parent ne peuvent pas être répétés:

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();
        });
    }
}

Et oui, pour la énième fois, je suis conscient qu'il manque beaucoup à ce tableau des factures ; cependant, notre objectif ici est de voir comment les relations de modèle fonctionnent et non pour concevoir une base de données entière.

Bon, nous avons atteint le point où nous devons créer la migration finale de notre système (j'espère!). L'accent est désormais mis sur la Transaction modèle, que nous avons décidé plus tôt est lié au Order modèle. En passant, voici un exercice pour vous: si le Transaction le modèle soit plutôt lié au Invoice modèle? Pourquoi et pourquoi pas? 🙂

$ php artisan make:model Transaction -m
Model created successfully.
Created Migration: 2021_01_31_145806_create_transactions_table

Et la migration:

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');
        });
    }
}

Phew! C'était un travail difficile. . . exécutons les migrations et voyons comment nous nous en sortons aux yeux de la base de données.

$ 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)

Louange au Seigneur! 🙏🏻🙏🏻 On dirait que nous avons survécu au moment du procès.

Et avec cela, nous sommes prêts à passer à la définition des relations de modèle! Pour cela, nous devons revenir à la liste que nous avons créée précédemment, en soulignant le type de relations directes entre les modèles (tables).

Pour commencer, nous avons établi qu'il existe une relation un-à-plusieurs entre les utilisateurs et les commandes. Nous pouvons le confirmer en allant dans le fichier de migration des commandes et en voyant la présence du champ user_id Là. Ce champ est ce qui crée la relation, car toute relation que nous souhaitons établir doit d'abord être honorée par la base de données; le reste (syntaxe éloquente et où écrire quelle fonction) n'est que pure formalité.

En d'autres termes, la relation est déjà là. Nous devons simplement dire à Eloquent de le rendre disponible au moment de l'exécution. Commençons par le Order modèle, où nous déclarons qu'il appartient au User modèle:

<?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 syntaxe doit vous être familière; nous déclarons une fonction nommée user(), qui sert à accéder à l'utilisateur qui possède cette commande (le nom de la fonction peut être n'importe quoi; c'est ce qu'elle renvoie qui compte). Repensez-y un instant - s'il n'y avait ni base de données ni clé étrangère, une instruction comme $this->belongsTo n'aurait aucun sens. C'est uniquement parce qu'il y a une clé étrangère sur le orders table que Laravel est capable d'utiliser user_id rechercher l'utilisateur avec le même id et renvoyez-le. À lui seul, sans la coopération de la base de données, Laravel ne peut pas créer de relations à partir de rien.

Maintenant, ce serait aussi bien de pouvoir écrire $user->orders pour accéder aux commandes d'un utilisateur. Cela signifie que nous devons aller au User modélisez et écrivez une fonction pour la partie «plusieurs» de cette relation un-à-plusieurs:

<?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);
    }
}

Oui, j'ai fortement modifié la valeur par défaut User model car nous n'avons pas besoin de toutes les autres fonctionnalités pour ce didacticiel. Quoi qu'il en soit, le User class a maintenant une méthode appelée orders(), qui dit qu'un utilisateur peut être associé à plusieurs commandes. Dans le monde ORM, on dit que le orders() relation ici est l'inverse de la user() relation que nous avions sur le Order .

Mais attendez une minute! Comment fonctionne cette relation? Je veux dire, il n'y a rien au niveau de la base de données qui ait plusieurs connexions sortant du users table à la orders tableau.

En fait, là is une connexion existante, et il s'avère que cela suffit à lui seul - la référence de clé étrangère stockée dans le orders table! C'est, quand nous disons quelque chose comme $user->orders, Laravel frappe le orders() fonction et sait en le regardant qu'il y a une clé étrangère sur le orders table. Ensuite, ça fait un peu SELECT * FROM orders WHERE user_id = 23 et renvoie les résultats de la requête sous forme de collection. Bien sûr, tout l'intérêt d'avoir un ORM est d'oublier SQL, mais nous ne devons pas oublier complètement que la base sous-jacente est le SGBDR qui exécute les requêtes SQL.

Ensuite, passons en revue les modèles de commandes et de factures, dans lesquels nous entretenons une relation individuelle:

<?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);
    }
}

Et le modèle de facture:

<?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);
    }
}

Notez qu'au niveau de la base de données, ainsi que presque au niveau éloquent, c'est une relation un-à-plusieurs typique; nous venons d'ajouter quelques vérifications pour nous assurer qu'il reste un à un.

Nous arrivons maintenant à un autre type de relation - le plusieurs-à-plusieurs entre les commandes et les articles. Rappelons que nous avons déjà créé une table intermédiaire appelée item_order qui stocke le mappage entre les clés primaires. Si tout a été fait correctement, définir la relation et travailler avec elle est trivial. Selon la documentation Laravel, pour définir une relation plusieurs-à-plusieurs, vos méthodes doivent renvoyer un belongsToMany() exemple.

Donc, dans le Item modèle:

<?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);
    }
}

Étonnamment, la relation inverse est presque identique:

class Order extends Model
{
    /* ... other code */
    
    public function items() {
        return $this->belongsToMany(Item::class);
    }
}

Et c'est tout! Tant que nous avons suivi correctement les conventions de dénomination, Laravel est capable de déduire les mappages ainsi que leur emplacement.

Puisque les trois types fondamentaux de relations ont été couverts (un-à-un, un-à-plusieurs, plusieurs-à-plusieurs), je vais arrêter d'écrire les méthodes pour d'autres modèles, car elles le seront mêmes lignes. Créons plutôt les usines pour ces modèles, créons des données factices et voyons ces relations en action!

Comment fait-on cela? Eh bien, prenons le chemin rapide et sale et jetons tout dans le fichier de semoir par défaut. Ensuite, lorsque nous exécuterons les migrations, nous exécuterons également le semeur. Alors, voici ce que mon DatabaseSeeder.php le fichier ressemble à:

<?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,
        ]);
    }
}

Et maintenant, nous configurons à nouveau la base de données et la semons:

$ 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.

D'accord! Voici la dernière partie de cet article, où nous accédons simplement à ces relations et confirmons tout ce que nous avons appris jusqu'à présent. Vous serez ravi de savoir (j'espère) que ce sera une section légère et amusante.

Et maintenant, lançons le composant le plus amusant de Laravel - la console interactive 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

Bon, alors, tout d'abord, accédons à la relation individuelle que nous avons dans nos modèles de commande et de facture:

>>> $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,
   }

Remarquez quelque chose? N'oubliez pas que la façon dont cela a été fait au niveau de la base de données, cette relation est un-à-plusieurs, si ce n'est pour les contraintes supplémentaires. Ainsi, Laravel aurait pu renvoyer une collection d'objets (ou un seul objet) comme résultat, et ce serait techniquement exact. MAIS . . . nous avons dit à Laravel que c'était une relation un à un, donc, le résultat est une seule instance Eloquent. Remarquez comment la même chose se produit lors de l'accès à la relation inverse:

$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

Nous avons une relation un-à-plusieurs entre les utilisateurs et les commandes. «Bricolons» maintenant et voyons le résultat:

>>> 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",
   }

Exactement comme prévu, l'accès aux commandes d'un utilisateur aboutit à une collection d'enregistrements, tandis que l'inverse ne produit qu'un seul User objet. En d'autres termes, un-à-plusieurs.

Accessing many-to-many model relationships in Laravel Eloquent

Maintenant, explorons une relation plusieurs-à-plusieurs. Nous avons une telle relation entre les articles et les commandes:

>>> $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,
         },
       },
     ],
   }

Cette sortie peut être un peu étourdissante à lire, mais notez que item1 fait partie des articles de order1, et vice versa, c'est ainsi que nous configurons les choses. Jetons également un coup d'œil dans la table intermédiaire qui stocke les mappages:

>>> 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,
       },
     ],
   }

Conclusion

Oui, c'est ça, vraiment! Ce fut un très long article, mais j'espère qu'il a été utile. Est-ce tout ce qu'il faut savoir sur les modèles Laravel?

Malheureusement non. Le terrier du lapin est vraiment, vraiment profond, et il existe de nombreux concepts plus difficiles tels que les relations polymorphes et l'optimisation des performances, et ainsi de suite, que vous rencontrerez en grandissant en tant que développeur Laravel. Pour l'instant, ce que cet article couvre est suffisant pour 70% des développeurs 70% du temps, grosso modo. Il vous faudra beaucoup de temps avant que vous ressentiez le besoin de mettre à niveau vos connaissances.

Avec cette mise en garde, je veux que vous reteniez cette idée la plus importante: rien n'est de la magie noire ou hors de portée programmation. C'est seulement que nous ne comprenons pas les fondations et comment les choses sont construites, ce qui nous fait lutter et nous sentir frustrés.

Donc . . . ?

Investissez en vous! Cours, livres, articles, autres communautés de programmation (Python est ma recommandation n ° 1) - utilisez toutes les ressources que vous pouvez trouver et consommez-les régulièrement si lentement. Bientôt, le nombre de cas où vous risquez de tout gâcher diminuera considérablement.

D'accord, assez de prédication. Bonne journée! 🙂

Merci à nos commanditaires
Plus de bonnes lectures sur le développement
Alimentez votre entreprise
Certains des outils et services pour aider votre entreprise à se développer.
  • Invicti utilise Proof-Based Scanning™ pour vérifier automatiquement les vulnérabilités identifiées et générer des résultats exploitables en quelques heures seulement.
    Essayez Invicti
  • Web scraping, proxy résidentiel, proxy manager, web unlocker, moteur de recherche et tout ce dont vous avez besoin pour collecter des données Web.
    Essayez Brightdata
  • Semrush est une solution de marketing numérique tout-en-un avec plus de 50 outils de référencement, de médias sociaux et de marketing de contenu.
    Essayez Semrush
  • Intruder est un scanner de vulnérabilités en ligne qui détecte les failles de cybersécurité de votre infrastructure, afin d'éviter des violations de données coûteuses.
    Essayez Intruder