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)

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
>>>
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! 🙂
-
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