Geekflare wird von unserem Publikum unterstützt. Es kann sein, dass wir durch den Kauf von Links auf dieser Seite Affiliate-Provisionen verdienen.
Unter Entwicklung Zuletzt aktualisiert: September 24, 2023
Weitergeben:
Invicti Web Application Security Scanner - die einzige Lösung, die eine automatische Überprüfung von Schwachstellen mit Proof-Based Scanning™ ermöglicht.

Modelle und ihre Beziehungen sind das Herzstück von Laravel Eloquent. Wenn sie Ihnen Schwierigkeiten bereiten oder Sie keine einfache, freundliche und vollständige Anleitung finden können, fangen Sie hier an!

Wenn Sie auf der anderen Seite ihres Programmierartikels sitzen, ist es für den Autor ein Leichtes, die Aura von Fachwissen/Prestige, die die Plattform bietet, vorzutäuschen oder aufzublasen. Aber ich will ehrlich sein – mir fiel es extrem schwer, Laravel zu lernen, und sei es nur, weil es mein erstes Full-Stack-Framework war. Ein Grund dafür war, dass ich es nicht bei der Arbeit benutzte und es nur aus Neugierde erforschte. Ich versuchte es also, kam zu einem Punkt, war verwirrt, gab auf und vergaß schließlich alles. Ich habe das bestimmt 5-6 Mal gemacht, bevor es für mich einen Sinn ergab (die Dokumentation hilft natürlich nicht weiter)

Aber was immer noch keinen Sinn ergab, war Eloquent. Oder zumindest die Beziehungen zwischen den Modellen (denn Eloquent ist zu umfangreich, um es vollständig zu lernen). Beispiele, die Autoren und Blogbeiträge modellieren, sind ein Witz, denn echte Projekte sind weitaus komplexer; leider werden in den offiziellen Dokumentationen genau dieselben (oder ähnliche) Beispiele verwendet. Oder selbst wenn ich auf einen nützlichen Artikel/eine nützliche Ressource gestoßen bin, war die Erklärung so schlecht oder fehlte so sehr, dass sie einfach nicht zu gebrauchen war

(Übrigens wurde ich schon einmal angegriffen, weil ich die offizielle Dokumentation angegriffen habe. Wenn Sie also ähnliche Ideen haben, hier meine Standardantwort: Schauen Sie sich die Django-Dokumentation an und sprechen Sie dann mit mir)

Schließlich fügte sich alles Stück für Stück zusammen und ergab einen Sinn. Ich war endlich in der Lage, Projekte richtig zu modellieren und die Modelle bequem zu verwenden. Dann stieß ich eines Tages auf einige nette Collections-Tricks, die diese Arbeit angenehmer machen. In diesem Artikel möchte ich alles abdecken, angefangen bei den Grundlagen bis hin zu allen möglichen Anwendungsfällen, denen Sie in echten Projekten begegnen werden

Warum sind Eloquent-Modellbeziehungen schwierig?

Leider treffe ich viel zu viele Laravel-Entwickler, die Modelle nicht richtig verstehen

Aber warum?

Selbst heute, wo es eine Flut von Kursen, Artikeln und Videos über Laravel gibt, ist das Verständnis insgesamt schlecht. Ich denke, das ist ein wichtiger Punkt, über den man nachdenken sollte

Wenn Sie mich fragen, würde ich sagen, dass Beziehungen zwischen Eloquent-Modellen gar nicht so schwer sind. Zumindest aus der Sicht der Definition von “schwierig”. Live-Schema-Migrationen sind schwer; eine neue Template-Engine zu schreiben ist schwer; Code zum Kern von Laravel beizutragen ist schwer. Im Vergleich dazu ist das Erlernen und Verwenden eines ORM … nun, das kann nicht schwer sein! 🤭🤭

Tatsächlich ist es so, dass PHP-Entwickler, die Laravel lernen, Eloquent schwer finden. Das ist das eigentliche Problem, und meiner Meinung nach gibt es mehrere Faktoren, die dazu beitragen (Achtung, unpopuläre Meinung!)

  • Vor Laravel hatten die meisten PHP-Entwickler nur mit CodeIgniter zu tun (das übrigens immer noch existiert, auch wenn es inzwischen mehr an Laravel/CakePHP angelehnt ist). In der älteren CodeIgniter-Gemeinschaft (falls es eine gab) bestand die “beste Praxis” darin, bei Bedarf direkt SQL-Abfragen einzufügen. Und obwohl wir heute ein neues CodeIgniter haben, haben sich die Gewohnheiten fortgesetzt. Wenn Sie Laravel lernen, ist die Idee eines ORM für PHP-Entwickler daher zu 100% neu.
  • Sieht man von dem sehr kleinen Prozentsatz der PHP-Entwickler ab, die mit Frameworks wie Yii, CakePHP usw. zu tun haben, sind die übrigen an die Arbeit mit Core PHP oder in einer Umgebung wie WordPress gewöhnt. Und auch hier gibt es keine OOP-basierte Denkweise, so dass ein Framework, ein Service-Container, ein Design-Pattern, ein ORM … fremde Konzepte sind.
  • In der PHP-Welt gibt es wenig bis gar kein Konzept des kontinuierlichen Lernens. Der durchschnittliche Entwickler arbeitet gerne mit Einzelserver-Konfigurationen, die relationale Datenbanken verwenden und Abfragen in Form von Strings stellen. Asynchrone Programmierung, Websockets, HTTP 2/3, Linux (vergessen Sie Docker), Unit-Tests, Domain-Driven Design – all das sind für einen überwältigenden Teil der PHP-Entwickler fremde Ideen. Das hat zur Folge, dass das Einlesen in etwas Neues und Herausforderndes, so dass man es als angenehm empfindet, bei Eloquent nicht vorkommt.
  • Auch das allgemeine Verständnis von Datenbanken und Modellierung ist mangelhaft. Da das Datenbankdesign direkt und untrennbar mit den Eloquent-Modellen verbunden ist, wird die Schwierigkeitslatte noch höher gelegt.

Ich will nicht zu hart sein und pauschalisieren – es gibt auch hervorragende PHP-Entwickler, und zwar viele, aber ihr Anteil ist insgesamt sehr gering

Wenn Sie dies lesen, bedeutet das, dass Sie all diese Hürden überwunden haben, auf Laravel gestoßen sind und sich mit Eloquent auseinandergesetzt haben

Herzlichen Glückwunsch! 👏

Sie sind fast am Ziel. Alle Bausteine sind vorhanden und wir müssen sie nur noch in der richtigen Reihenfolge und im Detail durchgehen. Beginnen wir also auf der Ebene der Datenbank

Datenbankmodelle: Beziehungen und Kardinalität

Der Einfachheit halber gehen wir davon aus, dass wir in diesem Artikel nur mit relationalen Datenbanken arbeiten. Das liegt zum einen daran, dass ORMs ursprünglich für relationale Datenbanken entwickelt wurden, und zum anderen daran, dass RDBMS immer noch weit verbreitet sind

Datenmodell

Lassen Sie uns zunächst Datenmodelle besser verstehen. Die Idee eines Modells (oder eines Datenmodells, um genau zu sein) stammt aus der Datenbank. Ohne Datenbank keine Daten und somit auch kein Datenmodell. Und was ist ein Datenmodell? Ganz einfach, es ist die Art und Weise, wie Sie Ihre Daten speichern/strukturieren wollen. In einem E-Commerce-Shop könnten Sie beispielsweise alles in einer einzigen riesigen Tabelle speichern (eine schreckliche Praxis, aber leider in der PHP-Welt nicht unüblich); das wäre Ihr Datenmodell. Sie könnten die Daten auch in 20 Haupt- und 16 Verbindungstabellen aufteilen; auch das ist ein Datenmodell

Beachten Sie auch, dass die Art und Weise, wie die Daten in der Datenbank strukturiert sind, nicht 100%ig mit der Anordnung im ORM des Frameworks übereinstimmen muss. Wir bemühen uns jedoch immer, die Dinge so ähnlich wie möglich zu halten, damit wir bei der Entwicklung nicht noch eine weitere Sache beachten müssen

Kardinalität

Lassen Sie uns auch diesen Begriff schnell aus dem Weg räumen: Kardinalität. Er bedeutet einfach nur “Anzahl”, grob gesagt. 1, 2, 3 … können also alle die Kardinalität von etwas sein. Ende der Geschichte. Lassen Sie uns weitermachen!

Beziehungen

Immer, wenn wir Daten in einem beliebigen System speichern, gibt es Möglichkeiten, Datenpunkte miteinander in Beziehung zu setzen. Ich weiß, das klingt abstrakt und langweilig, aber haben Sie ein wenig Geduld mit mir. Die Art und Weise, wie verschiedene Datenpunkte miteinander verbunden sind, wird als Beziehungen bezeichnet. Schauen wir uns zunächst einige Beispiele an, die nichts mit Datenbanken zu tun haben, damit wir sicher sind, dass wir die Idee vollständig verstehen

  • Wenn wir alles in einem Array speichern, ist eine mögliche Beziehung: Das nächste Datenelement befindet sich an einem Index, der um 1 größer ist als der vorherige Index.
  • Wenn wir die Daten in einem binären Baum speichern, besteht eine mögliche Beziehung darin, dass der untergeordnete Baum auf der linken Seite immer kleinere Werte hat als der übergeordnete Knoten (wenn wir den Baum auf diese Weise pflegen wollen).
  • Wenn wir Daten als Array von Arrays gleicher Länge speichern, können wir eine Matrix imitieren, deren Eigenschaften dann zu den Beziehungen für unsere Daten werden.

Wir sehen also, dass das Wort “Beziehung” im Zusammenhang mit Daten keine feste Bedeutung hat. Wenn zwei Personen dieselben Daten betrachten, könnten sie zwei sehr unterschiedliche Datenbeziehungen feststellen (hallo, Statistik!) und beide könnten gültig sein

Relationale Datenbanken

Nach all den Begriffen, die wir bisher besprochen haben, können wir nun endlich über etwas sprechen, das eine direkte Verbindung zu Modellen in einem Web-Framework (Laravel) hat – relationale Datenbanken. Die meisten von uns verwenden als primäre Datenbank MySQL, MariaDB, PostgreSQL, MSSQL, SQL Server, SQLite oder etwas in dieser Richtung. Wir wissen vielleicht auch vage, dass diese Datenbanken RDBMS genannt werden, aber die meisten von uns haben vergessen, was das eigentlich bedeutet und warum es wichtig ist

Das “R” in RDBMS steht natürlich für Relational. Dies ist kein willkürlich gewählter Begriff; damit unterstreichen wir die Tatsache, dass diese Datenbanksysteme darauf ausgelegt sind, effizient mit Beziehungen zwischen den gespeicherten Daten zu arbeiten. In der Tat hat “Beziehung” hier eine streng mathematische Bedeutung, und obwohl sich kein Entwickler damit befassen muss, ist es hilfreich zu wissen, dass diesen Arten von Datenbanken eine strenge mathematische Grundlage zugrunde liegt

Entdecken Sie diese Ressourcen, um SQL und NoSQL zu lernen

Okay, wir wissen also aus Erfahrung, dass die Daten in RDBMS als Tabellen gespeichert werden. Wo sind dann die Beziehungen?

Arten von Beziehungen in RDBMS

Dies ist vielleicht der wichtigste Teil des gesamten Themas Laravel und Modellbeziehungen. Wenn Sie das nicht verstehen, wird Eloquent keinen Sinn ergeben, also passen Sie bitte in den nächsten Minuten gut auf (es ist nicht einmal so schwierig)

Ein RDBMS ermöglicht es uns, Beziehungen zwischen Daten herzustellen – auf Datenbankebene. Das bedeutet, dass diese Beziehungen nicht unpraktisch/imaginär/subjektiv sind und von verschiedenen Personen mit demselben Ergebnis erstellt oder abgeleitet werden können

Gleichzeitig gibt es in einem RDBMS bestimmte Funktionen/Werkzeuge, mit denen wir diese Beziehungen erstellen und durchsetzen können, wie z.B.

  • Primärschlüssel
  • Fremdschlüssel
  • Constraints

Da ich nicht möchte, dass dieser Artikel zu einem Kurs über Datenbanken wird, gehe ich davon aus, dass Sie wissen, was diese Konzepte sind. Falls nicht, oder falls Sie sich unsicher fühlen, empfehle ich Ihnen dieses freundliche Video (Sie können sich gerne die gesamte Serie ansehen)

YouTube Video

Zufälligerweise sind diese RDBMS-ähnlichen Beziehungen auch die häufigsten, die in realen Anwendungen vorkommen (nicht immer, da ein soziales Netzwerk am besten als Graph und nicht als eine Sammlung von Tabellen modelliert wird). Sehen wir uns also eine nach der anderen an und versuchen wir zu verstehen, wo sie nützlich sein könnten

Eins-zu-eins-Beziehung

In fast jeder Webanwendung gibt es Benutzerkonten. Außerdem gilt (im Allgemeinen) für Benutzer und Konten Folgendes

  • Ein Benutzer kann nur ein Konto haben.
  • Ein Konto kann nur von einem Benutzer besessen werden.

Ja, wir können argumentieren, dass sich eine Person mit einer anderen E-Mail-Adresse anmelden und somit zwei Konten erstellen kann, aber aus der Sicht der Webanwendung sind dies zwei verschiedene Personen mit zwei verschiedenen Konten. Die Anwendung wird zum Beispiel nicht die Daten eines Kontos in einem anderen anzeigen

Was diese ganze Haarspalterei bedeutet, ist – wenn Sie eine solche Situation in Ihrer Anwendung haben und eine relationale Datenbank verwenden, müssen Sie sie als Eins-zu-Eins-Beziehung konzipieren. Beachten Sie, dass niemand Sie künstlich dazu zwingt – es gibt eine klare Situation in der Geschäftsdomäne und Sie verwenden zufällig eine relationale Datenbank … nur wenn diese beiden Bedingungen erfüllt sind, greifen Sie zu einer Eins-zu-Eins-Beziehung

Für dieses Beispiel (Benutzer und Konten) können wir diese Beziehung bei der Erstellung des Schemas folgendermaßen implementieren

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)

)

Haben Sie den Trick hier bemerkt? Das ist bei der Erstellung von Anwendungen im Allgemeinen ziemlich ungewöhnlich, aber in der Tabelle accounts haben wir das Feld id sowohl als Primärschlüssel als auch als Fremdschlüssel festgelegt! Die Fremdschlüssel-Eigenschaft verknüpft es mit der Tabelle users (natürlich 🙄), während die Primärschlüssel-Eigenschaft die Spalte id eindeutig macht – eine echte Eins-zu-Eins-Beziehung!

Zugegeben, die Treue dieser Beziehung ist nicht garantiert. Es hält mich beispielsweise nichts davon ab, 200 neue Benutzer hinzuzufügen, ohne auch nur einen einzigen Eintrag in der Tabelle accounts vorzunehmen. Wenn ich das tue, habe ich am Ende eine Eins-zu-Null-Beziehung! 🤭🤭 Aber innerhalb der Grenzen der reinen Struktur ist das das Beste, was wir tun können. Wenn wir das Hinzufügen von Benutzern ohne Konten verhindern wollen, müssen wir auf eine Art Programmierlogik zurückgreifen, entweder in Form von Datenbank-Triggern oder durch Laravel erzwungenen Validierungen

Wenn Sie jetzt anfangen, sich zu stressen, habe ich einen sehr guten Rat für Sie

  • Gehen Sie es langsam an. So langsam, wie Sie es brauchen. Anstatt zu versuchen, diesen Artikel und die 15 anderen, die Sie für heute als Lesezeichen gespeichert haben, zu beenden, halten Sie sich an diesen einen. Lassen Sie sich 3, 4, 5 Tage Zeit, wenn es sein muss – Ihr Ziel sollte es sein, Eloquent model relationships für immer von Ihrer Liste zu streichen. Sie sind schon einmal von Artikel zu Artikel gesprungen und haben dabei mehrere hundert Stunden vergeudet, aber es hat nichts gebracht. Machen Sie also dieses Mal etwas anders. 😇
  • In diesem Artikel geht es zwar um Laravel Eloquent, aber das kommt alles erst viel später. Die Grundlage des Ganzen ist das Datenbankschema, also sollten wir uns zuerst darauf konzentrieren, das richtig hinzubekommen. Wenn Sie nicht auf reiner Datenbankebene arbeiten können (unter der Annahme, dass es keine Frameworks gibt), dann werden Modelle und Beziehungen niemals einen vollen Sinn ergeben. Vergessen Sie also Laravel für den Moment. Vollständig. Wir reden nur über das Datenbankdesign und machen es auch nur so. Ja, ich werde ab und zu auf Laravel verweisen, aber es ist Ihre Aufgabe, sie komplett zu ignorieren, wenn sie das Bild für Sie verkomplizieren.
  • Lesen Sie später ein wenig mehr über Datenbanken und was sie bieten. Indizes, Leistung, Trigger, zugrundeliegende Datenstrukturen und ihr Verhalten, Caching, Beziehungen in MongoDB … was auch immer Sie an tangentialen Themen abdecken können, wird Ihnen als Ingenieur helfen. Denken Sie daran, dass Framework-Modelle nur eine Hülle sind; die eigentliche Funktionalität einer Plattform kommt von den zugrunde liegenden Datenbanken.

One-to-many-Beziehung

Ich bin mir nicht sicher, ob Sie sich dessen bewusst sind, aber dies ist die Art von Beziehung, die wir alle intuitiv bei unserer täglichen Arbeit erstellen. Wenn wir zum Beispiel eine Tabelle Bestellungen (ein hypothetisches Beispiel) erstellen, um einen Fremdschlüssel zur Tabelle Benutzer zu speichern, erstellen wir eine Eins-zu-Viel-Beziehung zwischen Benutzern und Bestellungen. Warum ist das so? Sehen Sie sich das Ganze noch einmal aus der Perspektive an, wer wie viele Bestellungen haben kann: Ein Benutzer darf mehr als eine Bestellung haben, so funktioniert so ziemlich jeder E-Commerce. Und von der anderen Seite aus gesehen besagt die Beziehung, dass eine Bestellung nur zu einem Benutzer gehören kann, was auch sehr sinnvoll ist

In der Datenmodellierung, in RDBMS-Büchern und in der Systemdokumentation wird diese Situation schematisch wie folgt dargestellt

Sehen Sie die drei Linien, die eine Art Dreizack bilden? Dies ist das Symbol für “viele”, und dieses Diagramm besagt, dass ein Benutzer viele Aufträge haben kann

Übrigens sind diese “vielen” und “einen”, die uns immer wieder begegnen, das, was man die Kardinalität einer Beziehung nennt (erinnern Sie sich an dieses Wort aus einem früheren Abschnitt?). Auch für diesen Artikel hat der Begriff keine Bedeutung, aber es ist hilfreich, das Konzept zu kennen, falls es in Interviews oder bei der weiteren Lektüre auftaucht

Einfach, nicht wahr? Und in Bezug auf SQL ist die Erstellung dieser Beziehung ebenfalls einfach. Es ist sogar viel einfacher als bei einer Eins-zu-Eins-Beziehung!

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)

)

Die Tabelle orders speichert die Benutzer-IDs für jede Bestellung. Da es keine Einschränkung gibt, die besagt, dass die Benutzer-IDs in der Tabelle “Bestellungen " eindeutig sein müssen, bedeutet dies, dass wir eine einzelne ID viele Male wiederholen können. Dadurch entsteht die Eins-zu-Viel-Beziehung und nicht durch irgendeinen geheimnisvollen Zauber, der darunter verborgen ist. Die Benutzer-IDs werden in der Tabelle Bestellungen auf eine dumme Art und Weise gespeichert, und SQL hat kein Konzept für eine Eins-zu-Viel-Beziehung, eine Eins-zu-Eins-Beziehung usw. Aber wenn wir die Daten auf diese Weise speichern, können wir uns vorstellen, dass es eine Eins-zu-Viel-Beziehung gibt

Hoffentlich ergibt das jetzt einen Sinn. Oder zumindest mehr Sinn als vorher. 😅 Denken Sie daran, dass dies, wie alles andere auch, reine Übungssache ist, und sobald Sie dies 4-5 Mal in realen Situationen getan haben, werden Sie nicht mehr darüber nachdenken

Many-to-many-Beziehungen

Die nächste Art von Beziehungen, die in der Praxis vorkommt, ist die so genannte Many-to-many-Beziehung. Bevor Sie sich über Frameworks Gedanken machen oder gar in Datenbanken eintauchen, lassen Sie uns noch einmal ein Analogon aus der realen Welt betrachten: Bücher und Autoren. Denken Sie an Ihren Lieblingsautor; er hat mehr als ein Buch geschrieben, nicht wahr? Gleichzeitig ist es ziemlich üblich, dass mehrere Autoren an einem Buch mitarbeiten (zumindest im Sachbuchbereich). Ein Autor kann also viele Bücher schreiben, und viele Autoren können ein Buch schreiben. Zwischen den beiden Entitäten (Buch und Autor) entsteht so eine Beziehung von vielen zu vielen

Zugegeben, es ist sehr unwahrscheinlich, dass Sie eine reale Anwendung mit Bibliotheken oder Büchern und Autoren erstellen, also lassen Sie uns ein paar weitere Beispiele finden. In einer B2B-Umgebung bestellt ein Hersteller Artikel bei einem Lieferanten und erhält im Gegenzug eine Rechnung. Die Rechnung enthält mehrere Einzelposten, in denen jeweils die Menge und der gelieferte Artikel aufgeführt sind, z.B. 5-Zoll-Rohrstücke x 200 usw. In dieser Situation haben Artikel und Rechnungen eine Many-to-Many-Beziehung (denken Sie darüber nach und überzeugen Sie sich selbst). In einem Flottenmanagementsystem haben Fahrzeuge und Fahrer eine ähnliche Beziehung. Auf einer E-Commerce-Website können Benutzer und Produkte eine Many-to-Many-Beziehung haben, wenn wir Funktionen wie Favoriten oder Wunschlisten berücksichtigen

Nun gut, wie kann man diese Many-to-Many-Beziehung in SQL erstellen? Ausgehend von unserem Wissen über die Funktionsweise der Eins-zu-Viel-Beziehung ist es vielleicht verlockend zu denken, dass wir in beiden Tabellen Fremdschlüssel zur anderen Tabelle speichern sollten. Allerdings stoßen wir auf große Probleme, wenn wir dies versuchen. Schauen Sie sich dieses Beispiel an, in dem Bücher und Autoren in einer Many-to-Many-Beziehung stehen sollen

Auf den ersten Blick sieht alles gut aus – die Bücher werden den Autoren genau in einer Many-to-Many-Beziehung zugeordnet. Aber sehen Sie sich die Daten in der Tabelle der Autoren genau an: Die Buch-IDs 12 und 13 wurden beide von Peter M. (Autor-ID 2) geschrieben, so dass wir keine andere Wahl haben, als die Einträge zu wiederholen. Nicht nur, dass die Tabelle der Autoren jetzt Probleme mit der Datenintegrität hat (korrekte Normalisierung und all das), die Werte in der id-Spalte wiederholen sich jetzt auch noch. Das bedeutet, dass es in dem von uns gewählten Design keine Primärschlüsselspalte geben kann (weil Primärschlüssel keine doppelten Werte haben dürfen), und alles fällt auseinander

Es ist klar, dass wir einen neuen Weg brauchen, um dies zu tun, und zum Glück wurde dieses Problem bereits gelöst. Da die Speicherung von Fremdschlüsseln direkt in den beiden Tabellen alles vermasselt, ist der richtige Weg zur Erstellung von Many-to-Many-Beziehungen in RDBMS die Erstellung einer so genannten “Verbindungstabelle”. Die Idee ist im Grunde, die beiden ursprünglichen Tabellen ungestört zu lassen und eine dritte Tabelle zu erstellen, um die Many-to-Many-Zuordnung zu demonstrieren

Lassen Sie uns das fehlgeschlagene Beispiel wiederholen, um eine Join-Tabelle zu erstellen

Sie werden feststellen, dass es drastische Änderungen gegeben hat

  • Die Anzahl der Spalten in der Tabelle authors wurde reduziert.
  • Die Anzahl der Spalten in der Tabelle Bücher wurde reduziert.
  • Die Anzahl der Zeilen in der Tabelle authors wurde reduziert, da es keine Notwendigkeit mehr für Wiederholungen gibt.
  • Es ist eine neue Tabelle namens authors_books entstanden, die Informationen darüber enthält, welche Autorenkennung mit welcher Buchkennung verbunden ist. Wir hätten die Join-Tabelle beliebig benennen können, aber der Konvention nach ist sie das Ergebnis einer einfachen Verknüpfung der beiden Tabellen, die sie repräsentiert, unter Verwendung eines Unterstrichs.

Die Verknüpfungstabelle hat keinen Primärschlüssel und enthält in den meisten Fällen nur zwei Spalten – die IDs der beiden Tabellen. Es ist fast so, als hätten wir die Fremdschlüsselspalten aus unserem früheren Beispiel entfernt und sie in diese neue Tabelle eingefügt. Da es keinen Primärschlüssel gibt, können Sie so viele Wiederholungen vornehmen, wie nötig sind, um alle Beziehungen zu erfassen

Jetzt können wir mit den Augen sehen, wie die Verknüpfungstabelle die Beziehungen klar anzeigt, aber wie greifen wir in unseren Anwendungen auf sie zu? Das Geheimnis ist mit dem Namen verknüpft – Verknüpfungstabelle. Dies ist kein Kurs über SQL-Abfragen, also werde ich nicht näher darauf eingehen, aber die Idee ist, dass Sie, wenn Sie alle Bücher eines bestimmten Autors in einer einzigen, effizienten Abfrage haben möchten, die Tabellen in der gleichen Reihenfolge zusammenfügen –>
authors, authors_books, und books. Die Tabellen authors und authors_books werden über die Spalten id bzw. author_id verbunden, während die Tabellen authors_books und books über die Spalten book_id bzw. id verbunden werden

Anstrengend, ja. Aber sehen Sie es von der positiven Seite – wir haben alle notwendigen theoretischen Grundlagen geschaffen, bevor wir uns an die Eloquent-Modelle wagen. Und lassen Sie mich Sie daran erinnern, dass all diese Dinge nicht optional sind! Wenn Sie das Datenbankdesign nicht kennen, werden Sie für immer im Eloquent-Wirrwarr stecken bleiben. Außerdem spiegelt alles, was Eloquent tut oder zu tun versucht, diese Details auf Datenbankebene perfekt wider. Es ist also leicht zu verstehen, warum der Versuch, Eloquent zu lernen, während Sie vor RDBMS davonlaufen, eine Übung in Vergeblichkeit ist

Erstellen von Modellbeziehungen in Laravel Eloquent

Endlich, nach einem Umweg von etwa 70.000 Meilen, sind wir an dem Punkt angelangt, an dem wir über Eloquent, seine Modelle und deren Erstellung/Verwendung sprechen können. Im vorigen Teil des Artikels haben wir gelernt, dass alles mit der Datenbank beginnt und damit, wie Sie Ihre Daten modellieren. Dadurch wurde mir klar, dass ich ein einziges, vollständiges Beispiel verwenden sollte, bei dem ich ein neues Projekt beginne. Gleichzeitig möchte ich, dass dieses Beispiel realistisch ist und nicht von Blogs und Autoren oder Büchern und Regalen handelt (die auch realistisch sind, aber schon zu Tode gemacht wurden)

Stellen wir uns ein Geschäft vor, das Plüschtiere verkauft. Nehmen wir außerdem an, dass wir das Anforderungsdokument erhalten haben, aus dem wir diese vier Entitäten im System identifizieren können: Benutzer, Bestellungen, Rechnungen, Artikel, Kategorien, Unterkategorien und Transaktionen. Ja, es gibt wahrscheinlich noch mehr Komplikationen, aber lassen wir das beiseite und konzentrieren wir uns darauf, wie wir von einem Dokument zu einer App kommen

Sobald die wichtigsten Entitäten im System identifiziert sind, müssen wir uns überlegen, wie sie zueinander in Beziehung stehen, und zwar im Sinne der Datenbankbeziehungen, die wir bisher besprochen haben. Hier sind die Beziehungen, die ich mir vorstellen kann

  • Benutzer und Aufträge: Einer zu vielen.
  • Aufträge und Rechnungen: Eins zu eins. Mir ist klar, dass dies nicht ganz einfach ist und dass es je nach Geschäftsfeld eine eins zu viele, eine viele zu eins oder eine viele zu viele Beziehung geben kann. Aber wenn es um Ihren durchschnittlichen, kleinen E-Commerce-Shop geht, wird eine Bestellung nur eine Rechnung nach sich ziehen und umgekehrt.
  • Bestellungen und Artikel: Viele zu vielen.
  • Artikel und Kategorien: Viele zu eins. Auch dies ist bei großen E-Commerce-Websites nicht der Fall, aber wir haben einen kleinen Betrieb.
  • Kategorien und Unterkategorien: Eins zu viele. Auch hier werden Sie die meisten Beispiele aus der Praxis finden, die dem widersprechen, aber hey, Eloquent ist schon schwer genug, also lassen Sie uns die Datenmodellierung nicht noch schwieriger machen!
  • Bestellungen und Transaktionen: Aus einem mach viele. Ich möchte noch diese beiden Punkte als Begründung für meine Entscheidung anführen: 1) Wir hätten auch eine Beziehung zwischen Transaktionen und Rechnungen hinzufügen können. Das ist nur eine Entscheidung der Datenmodellierung. 2) Warum hier eins zu viel? Nun, es kommt häufig vor, dass eine Auftragszahlung aus irgendeinem Grund fehlschlägt und beim nächsten Mal erfolgreich ist. In diesem Fall haben wir zwei Transaktionen für diese Bestellung erstellt. Ob wir diese fehlgeschlagenen Transaktionen anzeigen möchten oder nicht, ist eine geschäftliche Entscheidung, aber es ist immer eine gute Idee, wertvolle Daten zu erfassen.

Gibt es noch andere Beziehungen? Nun, es sind noch viele weitere Beziehungen möglich, aber sie sind nicht praktisch. Wir können zum Beispiel sagen, dass ein Benutzer viele Transaktionen hat, also sollte es eine Beziehung zwischen ihnen geben. Hier müssen Sie sich darüber im Klaren sein, dass es bereits eine indirekte Beziehung gibt: Benutzer -> Bestellungen -> Transaktionen, und im Allgemeinen ist das gut genug, denn RDBMS sind Biester, wenn es darum geht, Tabellen zu verbinden. Zweitens würde die Erstellung dieser Beziehung bedeuten, dass wir der Tabelle Transaktionen eine Spalte user_id hinzufügen müssten. Wenn wir dies für jede mögliche direkte Beziehung tun würden, würden wir die Datenbank erheblich mehr belasten (in Form von mehr Speicherplatz, insbesondere wenn UUIDs verwendet werden, und durch die Pflege von Indizes), was das gesamte System beeinträchtigen würde. Sicher, wenn das Unternehmen sagt, dass es Transaktionsdaten innerhalb von 1,5 Sekunden benötigt, könnten wir uns entscheiden, diese Beziehung hinzuzufügen und die Dinge zu beschleunigen (Kompromisse, Kompromisse . . .)

Und jetzt, meine Damen und Herren, ist die Zeit gekommen, den eigentlichen Code zu schreiben!

Laravel Modellbeziehungen – echtes Beispiel mit Code

In der nächsten Phase dieses Artikels geht es darum, sich die Hände schmutzig zu machen – aber auf eine nützliche Art und Weise. Wir nehmen dieselben Datenbankentitäten wie im früheren E-Commerce-Beispiel und sehen uns an, wie Modelle in Laravel erstellt und verbunden werden, und zwar direkt bei der Installation von Laravel!

Ich gehe natürlich davon aus, dass Sie Ihre Entwicklungsumgebung eingerichtet haben und wissen, wie Sie Composer für die Verwaltung von Abhängigkeiten installieren und verwenden können

$ composer global require laravel/installer -W

$

 laravel new model-relationships-study

Diese beiden Konsolenbefehle installieren den Laravel-Installer (der -W-Teil wird für das Upgrade verwendet, da ich bereits eine ältere Version installiert hatte). Und falls Sie neugierig sind, zum Zeitpunkt der Erstellung dieses Artikels ist die installierte Laravel-Version 8.5.9. Sollten Sie in Panik geraten und ebenfalls ein Upgrade durchführen? Ich würde Ihnen davon abraten, da ich im Zusammenhang mit unserer Anwendung keine großen Änderungen zwischen Laravel 5 und Laravel 8 erwarte. Einige Dinge haben sich geändert und werden sich auf diesen Artikel auswirken (z. B. Model Factories), aber ich denke, Sie werden den Code portieren können

Da wir das Datenmodell und seine Beziehungen bereits durchdacht haben, wird der Teil der Erstellung der Modelle trivial sein. Und Sie werden auch sehen (ich höre mich jetzt an wie eine kaputte Schallplatte!), wie es das Datenbankschema widerspiegelt, da es zu 100% davon abhängig ist!

Mit anderen Worten, wir müssen zunächst die Migrationen (und Modelldateien) für alle Modelle erstellen, die dann auf die Datenbank angewendet werden. Später können wir dann an den Modellen arbeiten und die Beziehungen hinzufügen

Mit welchem Modell sollen wir also beginnen? Natürlich mit dem einfachsten und dem am wenigsten verknüpften Modell. In unserem Fall ist das das Benutzermodell. Da Laravel mit diesem Modell ausgeliefert wird (und ohne dieses Modell nicht funktioniert 🤣), ändern wir die Migrationsdatei und bereinigen auch das Modell, um es an unsere einfachen Bedürfnisse anzupassen

Hier ist die Migrationsklasse

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

}
Da wir eigentlich kein Projekt erstellen, brauchen wir uns nicht mit Passwörtern, is_active und all dem zu befassen. Unsere Benutzertabelle wird nur zwei Spalten haben: die ID und den Namen des Benutzers

Als nächstes erstellen wir die Migration für die Kategorie. Da Laravel uns die Möglichkeit bietet, auch das Modell mit einem einzigen Befehl zu erzeugen, werden wir dies nutzen, obwohl wir die Modelldatei vorerst nicht anfassen werden

$ php artisan make:model Category -m
Modell erfolgreich erstellt.
Erstellt Migration: 2021_01_26_093326_create_categories_table

Und hier ist die Migrationsklasse

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

Wenn Sie sich über das Fehlen der Funktion down() wundern, brauchen Sie sich nicht zu wundern. In der Praxis werden Sie sie nur selten verwenden, da das Löschen einer Spalte oder Tabelle oder die Änderung eines Spaltentyps zu einem Datenverlust führt, der nicht wiederhergestellt werden kann. Bei der Entwicklung werden Sie die gesamte Datenbank löschen und dann die Migrationen erneut ausführen. Aber wir schweifen ab, also lassen Sie uns zurückkehren und die nächste Entität in Angriff nehmen. Da Unterkategorien in direktem Zusammenhang mit Kategorien stehen, halte ich es für eine gute Idee, dies als nächstes zu tun

$ php artisan make:model SubCategory -m
Modell erfolgreich erstellt.
Erstellt Migration: 2021_01_26_140845_create_sub_categories_table

Alles klar, füllen wir nun die Migrationsdatei

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

}
Wie Sie sehen können, fügen wir hier eine separate Spalte namens category_id hinzu, in der die IDs aus der Tabelle categories gespeichert werden. Sie werden es nicht erraten, aber dadurch wird eine eins-zu-viele-Beziehung auf Datenbankebene erstellt

Jetzt sind die Artikel an der Reihe

$ php artisan make:model Item -m
Modell erfolgreich erstellt.
Erstellt Migration: 2021_01_26_141421_create_items_table

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

}
Wenn Sie der Meinung sind, dass die Dinge anders gemacht werden sollten, ist das in Ordnung. Es kommt selten vor, dass zwei Personen genau dasselbe Schema und dieselbe Architektur entwickeln. Beachten Sie jedoch eine Sache, die eine Art Best Practice darstellt: Ich habe die Preisangabe als Ganzzahl gespeichert

Und warum?

Nun, die Leute haben erkannt, dass die Handhabung von Float-Divisionen und all dem auf der Datenbankseite hässlich und fehleranfällig ist, also haben sie angefangen, den Preis in der kleinsten Währungseinheit zu speichern. Wenn wir zum Beispiel in USD arbeiten, würde das Preisfeld hier für Cents stehen. Im gesamten System werden die Werte und Berechnungen in Cents angegeben. Nur wenn es an der Zeit ist, sie dem Benutzer anzuzeigen oder eine PDF-Datei per E-Mail zu versenden, wird durch 100 geteilt und abgerundet. Clever, nicht wahr?

Wie dem auch sei, beachten Sie, dass ein Artikel mit einer Unterkategorie in einer eindeutigen Beziehung verknüpft ist. Er ist auch mit einer Kategorie verknüpft … indirekt über seine Unterkategorie. Wir werden all diese Kunststücke noch sehen, aber jetzt müssen wir uns erst einmal mit den Konzepten vertraut machen und sicherstellen, dass wir uns 100%ig im Klaren sind

Als nächstes sehen wir uns das Order-Modell und seine Migration an

$ php artisan make:model Order -m
Modell erfolgreich erstellt.
Erstellt Migration: 2021_01_26_144157_create_orders_table

Der Kürze halber werde ich nur einige der wichtigen Felder in die Migration aufnehmen. Damit meine ich, dass die Details einer Bestellung sehr viele Dinge enthalten können, aber wir beschränken sie auf einige wenige, damit wir uns auf das Konzept der Modellbeziehungen konzentrieren können

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

}
Sieht gut aus, aber, Moment mal! Wo sind die Elemente in dieser Reihenfolge? Wie wir bereits festgestellt haben, besteht zwischen den Bestellungen und den Artikeln eine Many-to-many-Beziehung, so dass ein einfacher Fremdschlüssel nicht funktioniert. Die Lösung ist eine so genannte Verbindungstabelle oder Zwischentabelle. Mit anderen Worten, wir brauchen eine Verbindungstabelle, um die Many-to-Many-Zuordnung zwischen Bestellungen und Artikeln zu speichern. In der Laravel-Welt gibt es eine eingebaute Konvention, die wir befolgen, um Zeit zu sparen: Wenn ich eine neue Tabelle erstelle, indem ich die Singularform der beiden Tabellennamen verwende, sie in Wörterbuchreihenfolge anordne und sie mit einem Unterstrich verbinde, wird Laravel sie automatisch als die verbindende Tabelle erkennen

In unserem Fall wird die Tabelle item_order heißen (das Wort “item” steht in einem Wörterbuch vor “order”). Wie bereits erwähnt, enthält diese zusammenführende Tabelle normalerweise nur zwei Spalten, die Fremdschlüssel zu jeder Tabelle

Wir könnten hier eine Modellmigration erstellen, aber das Modell wird nie verwendet, da es sich eher um eine Metasache handelt. Also erstellen wir eine neue Migration in Laravel und teilen ihr mit, was was ist

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

Dies führt zu einer neuen Migration, die wir wie folgt ändern werden

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

}
Wie man auf diese Beziehungen über Eloquent-Methodenaufrufe zugreift, ist ein Thema für später, aber beachten Sie, dass wir diese Fremdschlüssel zunächst mühsam von Hand erstellen müssen. Ohne diese gibt es kein Eloquent und kein “smart” in Laravel 🙂

Sind wir schon so weit? Nun, fast..

Wir müssen uns nur noch um ein paar Modelle kümmern. Das erste ist die Rechnungstabelle, und Sie werden sich erinnern, dass wir beschlossen haben, sie in eine Eins-zu-eins-Beziehung zu den Bestellungen zu setzen

$ php artisan make:model Invoice -m
Das Modell wurde erfolgreich erstellt.
Created Migration: 2021_01_27_101116_create_invoices_table

In den ersten Abschnitten dieses Artikels haben wir gesehen, dass eine Möglichkeit, eine Eins-zu-Eins-Beziehung zu erzwingen, darin besteht, den Primärschlüssel der untergeordneten Tabelle auch zum Fremdschlüssel zu machen. In der Praxis wählt kaum jemand diesen übermäßig vorsichtigen Ansatz, und die Leute entwerfen das Schema im Allgemeinen so, wie sie es für eine Eins-zu-Viel-Beziehung tun würden. Meiner Meinung nach ist ein mittlerer Ansatz besser. Machen Sie einfach den Fremdschlüssel eindeutig und Sie haben sichergestellt, dass die IDs des übergeordneten Modells nicht wiederholt werden können

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

}
Und ja, zum x-ten Mal, ich bin mir bewusst, dass in dieser Rechnungstabelle eine Menge fehlt, aber wir wollen hier nur sehen, wie Modellbeziehungen funktionieren und nicht eine ganze Datenbank entwerfen

Okay, wir sind also an dem Punkt angelangt, an dem wir die endgültige Migration unseres Systems erstellen müssen (hoffe ich!). Der Schwerpunkt liegt jetzt auf dem Transaktionsmodell, das, wie wir vorhin beschlossen haben, mit dem Auftragsmodell verknüpft ist. Übrigens, hier ist eine Übung für Sie: Sollte das Transaktionsmodell stattdessen mit dem Rechnungsmodell verknüpft werden? Warum und warum nicht? 🙂

$ php artisan make:model Transaction -m
Modell erfolgreich erstellt.
Erstellt Migration: 2021_01_31_145806_create_transactions_table

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

}
Puh! Das war harte Arbeit … Lassen Sie uns die Migrationen ausführen und sehen, wie wir in den Augen der Datenbank abschneiden

$ php artisan migrate:fresh
Alle Tabellen erfolgreich gelöscht.
Migrationstabelle erfolgreich erstellt.
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)

Gelobt sei der Herr! 🙏🏻🙏🏻 Sieht so aus, als hätten wir den Moment der Prüfung überstanden

Und damit sind wir bereit, zur Definition von Modellbeziehungen überzugehen! Dazu müssen wir auf die Liste zurückgreifen, die wir zuvor erstellt haben und in der die Art der direkten Beziehungen zwischen Modellen (Tabellen) aufgeführt ist

Zunächst einmal haben wir festgestellt, dass es eine eins-zu-viele-Beziehung zwischen Benutzern und Bestellungen gibt. Wir können dies bestätigen, indem wir die Migrationsdatei der Bestellungen aufrufen und dort das Feld user_id sehen. Dieses Feld stellt die Beziehung her, denn jede Beziehung, die wir herstellen möchten, muss zuerst von der Datenbank anerkannt werden. Der Rest (Eloquent-Syntax und wo wir welche Funktion schreiben) ist reine Formsache

Mit anderen Worten: Die Beziehung ist bereits vorhanden. Wir müssen Eloquent nur sagen, dass es sie zur Laufzeit verfügbar machen soll. Beginnen wir mit dem Modell Order, für das wir erklären, dass es zum Modell User gehört

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

}
Die Syntax dürfte Ihnen bekannt vorkommen; wir deklarieren eine Funktion namens user(), die dazu dient, auf den Benutzer zuzugreifen, dem diese Bestellung gehört (der Funktionsname kann beliebig sein; es kommt darauf an, was sie zurückgibt). Denken Sie noch einmal einen Moment zurück – wenn es keine Datenbank und keine Fremdschlüssel gäbe, wäre eine Anweisung wie $this->gehörtZu sinnlos. Nur weil es einen Fremdschlüssel in der Tabelle orders gibt, kann Laravel die user_id verwenden, um den Benutzer mit derselben ID zu suchen und zurückzugeben. Allein, ohne die Zusammenarbeit mit der Datenbank, kann Laravel keine Beziehungen aus dem Nichts erstellen

Nun wäre es auch schön, wenn wir $user->orders schreiben könnten, um auf die Bestellungen eines Benutzers zuzugreifen. Das bedeutet, dass wir zum User-Modell gehen und eine Funktion für den “Many”-Teil dieser One-to-Many-Beziehung schreiben müssen

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

}


Ja, ich habe das Standard-Benutzermodell stark verändert, weil wir die anderen Funktionen für dieses Tutorial nicht brauchen. Jedenfalls verfügt die Klasse User jetzt über eine Methode namens orders(), die besagt, dass ein Benutzer mit mehreren Bestellungen verknüpft werden kann. In der ORM-Welt sagen wir, dass die Beziehung orders() hier die Umkehrung der Beziehung user() ist, die wir im Modell Order hatten

Aber, Moment mal! Wie funktioniert diese Beziehung? Ich meine, auf der Datenbankebene gibt es nichts, was mehrere Verbindungen von der Tabelle Benutzer zur Tabelle Bestellungen herstellt

Tatsächlich gibt es eine bestehende Verbindung, und es stellt sich heraus, dass sie allein ausreicht – die Fremdschlüsselreferenz, die in der Tabelle Bestellungen gespeichert ist! Das heißt, wenn wir etwas wie $user->orders sagen, greift Laravel auf die Funktion orders() zu und weiß durch einen Blick darauf, dass es einen Fremdschlüssel in der Tabelle orders gibt. Dann macht es eine Art SELECT * FROM orders WHERE user_id = 23 und gibt die Abfrageergebnisse als Sammlung zurück. Natürlich geht es bei einem ORM darum, SQL zu vergessen, aber wir sollten nicht völlig vergessen, dass die zugrunde liegende Basis das RDBMS ist, das SQL-Abfragen ausführt

Lassen Sie uns als Nächstes die Modelle für Bestellungen und Rechnungen durchgehen, wo wir eine Eins-zu-Eins-Beziehung haben

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

}
Und das Rechnungsmodell

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

}
Beachten Sie, dass es sich sowohl auf Datenbankebene als auch auf Eloquent-Ebene um eine typische Eins-zu-Viel-Beziehung handelt; wir haben lediglich einige Prüfungen hinzugefügt, um sicherzustellen, dass es bei einer Eins-zu-Eins-Beziehung bleibt

Wir kommen nun zu einer anderen Art von Beziehung – der Many-to-Many-Beziehung zwischen Bestellungen und Artikeln. Sie erinnern sich, dass wir bereits eine Zwischentabelle namens item_order erstellt haben, in der die Zuordnung zwischen den Primärschlüsseln gespeichert wird. Wenn das alles richtig gemacht wurde, ist die Definition der Beziehung und die Arbeit mit ihr trivial. Wie in den Laravel-Dokumenten beschrieben, müssen Ihre Methoden eine belongsToMany() -Instanz zurückgeben, um eine Many-to-Many-Beziehung zu definieren

Also, im Item-Modell

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

}


Überraschenderweise ist die umgekehrte Beziehung fast identisch

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

}
Und das war’s! Solange wir die Namenskonventionen korrekt befolgt haben, ist Laravel in der Lage, sowohl die Zuordnungen als auch deren Position abzuleiten

Da alle drei grundlegenden Arten von Beziehungen abgedeckt sind (eins-zu-eins, eins-zu-viele, viele-zu-viele), verzichte ich darauf, die Methoden für andere Modelle zu schreiben, da sie in dieselbe Richtung gehen werden. Lassen Sie uns stattdessen die Fabriken für diese Modelle erstellen, einige Dummy-Daten erzeugen und diese Beziehungen in Aktion sehen!

Wie machen wir das? Nun, lassen Sie uns den einfachen Weg gehen und alles in die Standard-Seeder-Datei packen. Wenn wir dann die Migrationen ausführen, wird auch der Seeder ausgeführt. So sieht also meine Datei DatabaseSeeder.php aus

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

 // Erzeugen wir zwei Benutzer
 $user1 = User::create(['name' => $faker->name]);
 $user2 = User::create(['name' => $faker->name]);

 // Erstellen Sie zwei Kategorien mit jeweils zwei Unterkategorien
 $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]);

 // Nach den Kategorien haben wir nun die Artikel
 // Lassen Sie uns je zwei Artikel für die Unterkategorie 2 und 4 erstellen
 $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 = Artikel::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 = Artikel::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 = Artikel::create([
 'sub_category_id' => 4,
 'name' => $faker->name,
 'description' => $faker->text,
 'type' => $faker->word,
 'price' => $faker->randomNumber(1),
 'quantity_in_stock' => $faker->randomNumber(3),
 ]);

 // Da wir nun Benutzer und Artikel haben, lassen wir user1 ein paar Bestellungen aufgeben
 $order1 = Order::create([
 'status' => 'confirmed',
 'total_value' => $faker->randomNumber(3),
 'taxes' => $faker->randomNumber(1),
 'shipping_charges' => $faker->randomNumber(2),
 'user_id' => $user1->id
 ]);

 $order2 = Bestellung::create([
 'status' => 'waiting',
 'total_value' => $faker->randomNumber(3),
 'taxes' => $faker->randomNumber(1),
 'shipping_charges' => $faker->randomNumber(2),
 'user_id' => $user1->id
 ]);

 // jetzt, Zuweisung von Artikeln zu Bestellungen
 $order1->items()->attach($item1);
 $order1->items()->attach($item2);
 $order1->items()->attach($item3);
        
 $order2->items()->attach($item1);
 $order2->items()->attach($item4);

 // und schließlich, Rechnungen erstellen
 $invoice1 = Invoice::create([
 'raised_at' => $faker->dateTimeThisMonth(),
 'status' => 'settled',
 'totalAmount' => $faker->randomNumber(3),
 'order_id' => $order1->id,
 ]);
 }

}


Und jetzt richten wir die Datenbank wieder ein und setzen sie auf

$ php artisan migrate:fresh --seed
Alle Tabellen erfolgreich gelöscht.
Migrationstabelle erfolgreich erstellt.
Migrieren: 2014_10_12_000000_create_users_table
Migriert:  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)
Das Seeding der Datenbank wurde erfolgreich abgeschlossen

So, das war’s! Jetzt kommt der letzte Teil dieses Artikels, in dem wir einfach auf diese Beziehungen zugreifen und alles bestätigen, was wir bisher gelernt haben. Sie werden erfreut sein zu hören, dass dies ein leichtgewichtiger und unterhaltsamer Abschnitt sein wird (hoffe ich)

Und nun lassen Sie uns die lustigste Laravel-Komponente starten – die interaktive Konsole Tinker!

$ php artisan tinker

Psy

 Shell v0.10.6 (PHP 8.0.0 - cli) von Justin Hileman
>>&gt

Zugriff auf Eins-zu-Eins-Modellbeziehungen in Laravel Eloquent

Okay, lassen Sie uns zunächst auf die Eins-zu-Eins-Beziehung zugreifen, die wir in unseren Modellen von Bestellung und Rechnung haben

>>> $order = Order::find(1);
[!] Verlagerung von 'Order' zu 'App\Models\Order' für diese Tinker-Sitzung.
=> App\Models\Order {#4108
 id: 1,
 status: "bestätigt",
 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,
 }

Fällt Ihnen etwas auf? Denken Sie daran, dass es sich bei dieser Beziehung auf Datenbankebene um eine eins-zu-viele-Beziehung handelt, wenn die zusätzlichen Einschränkungen nicht wären. Laravel hätte also eine Sammlung von Objekten (oder nur ein Objekt) als Ergebnis zurückgeben können, und das wäre technisch korrekt gewesen. ABER … wir haben Laravel gesagt, dass es sich um eine Eins-zu-Eins-Beziehung handelt, also ist das Ergebnis eine einzelne Eloquent-Instanz. Beachten Sie, dass das Gleiche passiert, wenn Sie auf die umgekehrte Beziehung zugreifen

$invoice = Invoice::find(1);
[!] Aliasing 'Invoice' zu 'App\Models\Invoice' für diese Tinker-Sitzung.
=> 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: "bestätigt",
 total_value: 320,
 taxes: 5,
 shipping_charges: 12,
 user_id: 1,
 }

Zugriff auf Eins-zu-Viel-Modellbeziehungen in Laravel Eloquent

Wir haben eine One-to-many-Beziehung zwischen Benutzern und Bestellungen. Lassen Sie uns jetzt damit “basteln” und die Ausgabe sehen

>>> Benutzer::find(1)->Bestellungen;
[!] Aliasing von 'Benutzer' auf 'App\Models\User' für diese Tinker-Sitzung.
=> Illuminate\Database\Eloquent\Collection {#4291
 all: [
 App\Models\Order {#4284
 id: 1,
 status: "bestätigt",
 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,
 },
 ],
 }
>>> Auftrag::find(1)->Benutzer
=> App\Modelle\Benutzer {#4281
 id: 1,
 name: "Dallas Kshlerin",
 }

Genau wie erwartet führt der Zugriff auf die Bestellungen eines Benutzers zu einer Sammlung von Datensätzen, während die Umkehrung nur ein einziges Benutzerobjekt ergibt. Mit anderen Worten: One-to-many

Zugriff auf Many-to-Many-Modellbeziehungen in Laravel Eloquent

Lassen Sie uns nun eine Beziehung untersuchen, die viele-zu-viele ist. Wir haben eine solche Beziehung zwischen Artikeln und Bestellungen

>>> $item1 = Item::find(1);
[!] Verlagerung von 'Item' zu 'App\Models\Item' für diese Tinker-Sitzung.
=> 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: "bestätigt",
 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->bestellungen
=> Illuminate\Database\Eloquent\Collection {#4197
 all: [
 App\Models\Order {#4272
 id: 1,
 status: "bestätigt",
 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,
 },
 },
 ],
 }

Diese Ausgabe kann etwas schwindelerregend sein, aber Sie werden feststellen, dass item1 zu den items von order1 gehört und umgekehrt, so wie wir es eingerichtet haben. Werfen wir auch einen Blick auf die Zwischentabelle, in der die Zuordnungen gespeichert sind

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

Fazit

Ja, das war’s, wirklich! Das war ein wirklich langer Artikel, aber ich hoffe, er war nützlich. Ist das alles, was man über Laravel-Modelle wissen muss?

Leider nein. Der Kaninchenbau ist sehr, sehr tief, und es gibt noch viele weitere anspruchsvolle Konzepte wie polymorphe Beziehungen, Leistungsoptimierung und so weiter, die Sie auf Ihrem Weg als Laravel-Entwickler kennenlernen werden. Im Moment reicht das, was dieser Artikel abdeckt, für 70% der Entwickler in 70% der Zeit aus, grob gesagt. Es wird sehr lange dauern, bis Sie das Bedürfnis verspüren werden, Ihr Wissen zu erweitern

Nach dieser Warnung möchte ich Ihnen die wichtigste Erkenntnis mit auf den Weg geben: Nichts ist in der Programmierung schwarze Magie oder unerreichbar. Es ist nur so, dass wir die Grundlagen nicht verstehen und nicht wissen, wie die Dinge aufgebaut sind, weshalb wir uns abmühen und frustriert sind

Also … ?

Investieren Sie in sich selbst! Kurse, Bücher, Artikel, andere Programmier-Communities(Python ist meine Empfehlung Nr. 1) – nutzen Sie alle Ressourcen, die Sie finden können, und konsumieren Sie sie regelmäßig, wenn auch langsam. Schon bald wird die Zahl der Fälle, in denen Sie die ganze Sache verpfuschen, drastisch abnehmen

Okay, genug gepredigt. Einen schönen Tag noch! 🙂

  • Ankush
    Autor
    Ich schreibe über, um und für das Ökosystem der Entwickler. Empfehlungen, Anleitungen, technische Diskussionen - was auch immer ich veröffentliche, ich versuche mein Bestes, um Verwirrung und Fluff zu vermeiden und umsetzbare Antworten auf der Grundlage persönlicher Erfahrungen zu geben... mehr lesen
Dank an unsere Sponsoren
Weitere gute Lektüre zum Thema Entwicklung
Energie für Ihr Unternehmen
Einige der Tools und Dienste, die Ihr Unternehmen beim Wachstum unterstützen.
  • Invicti nutzt das Proof-Based Scanning™, um die identifizierten Schwachstellen automatisch zu überprüfen und innerhalb weniger Stunden verwertbare Ergebnisse zu erzielen.
    Versuchen Sie Invicti
  • Web Scraping, Residential Proxy, Proxy Manager, Web Unlocker, Search Engine Crawler und alles, was Sie zum Sammeln von Webdaten benötigen.
    Versuchen Sie Brightdata
  • Monday.com ist ein All-in-One-Betriebssystem, mit dem Sie Projekte, Aufgaben, Arbeit, Vertrieb, CRM, Arbeitsabläufe und vieles mehr verwalten können.
    Versuch Montag
  • Intruder ist ein Online-Schwachstellen-Scanner, der Schwachstellen in Ihrer Infrastruktur aufspürt, um kostspielige Datenschutzverletzungen zu vermeiden.
    Versuchen Sie Intruder