v13.42.0. Die Grundlagen von Beziehungen finden Sie in der Einführung in Eloquent-Relationen. Wie Sie den Modellen der nutzenden Anwendung von außen Beziehungen hinzufügen, erklärt Dynamische Relationen in Paketen registrieren.
Typnamen und Beziehungsnamen trennen
Eine polymorphe Beziehung kombiniert den Primärschlüssel des verknüpften Modells mit einem Typnamen und kann so auf mehrere Modellarten verweisen. Wenn Kommentare etwa zu Artikeln oder Videos gehören können, brauchen Sie nebencommentable_id auch commentable_type.
Ohne Registrierung wird normalerweise ein vollqualifizierter Klassenname wie
Acme\Publishing\Models\Article als Typ verwendet. Nach der Registrierung eines Alias wird acme-publishing.article verwendet, etwa beim Anlegen neuer Beziehungen. Die Werte bestehender Zeilen werden durch die Registrierung allein nicht umgeschrieben.
Eine Morph Map erstellt weder Beziehungsmethoden noch Tabellen. Modelle, Spalten und Indizes legen Sie wie gewohnt an.
Nur paketspezifische Aliase registrieren
Im Beispiel istAcme\Publishing\ per Composer-PSR-4 auf src/ abgebildet. Definieren Sie zuerst src/Models/Article.php.
src/Models/Comment.php definieren Sie die umgekehrte Beziehung.
acme_articles erhält id, title und Zeitstempel, acme_comments erhält id, body, commentable_type, commentable_id und Zeitstempel. Die Primärschlüssel in diesem Beispiel sind Ganzzahlen. Sie können die Spalten und den zusammengesetzten Index aus $table->morphs('commentable') verwenden. Bei UUIDs oder ähnlichen Setups passen Sie die Spaltentypen an das verknüpfte Modell an.
Registrieren Sie anschließend den Alias in boot() eines normalen, nicht verzögerten Service-Providers src/PublishingServiceProvider.php.
$alias ist acme-publishing.article, $class ist Article::class. Solange Comment selbst nicht das verknüpfte Modell einer anderen polymorphen Beziehung wird, müssen Sie für dieses Beispiel keinen Alias für Comment registrieren.
Regeln für Zusammenführung und Namenskonflikte verstehen
Relation::morphMap() arbeitet auf einer statischen Zuordnungstabelle. Bei der standardmäßigen Zusammenführung werden das neue und das bestehende Array mit $map + static::$morphMap kombiniert.
Übergibt ein Paket
false als zweites Argument, löscht es die Registrierungen anderer Pakete und der nutzenden Anwendung. Behalten Sie bei normaler Registrierung die bestehende Zuordnung bei und wählen Sie einen Alias, der Herausgeber und Paket erkennen lässt, statt eines generischen Namens wie article. Verwenden Sie denselben Alias nicht für eine andere Bedeutung.
Wenn Sie nur eine Liste von Klassennamen übergeben, instanziiert Laravel jedes Modell und verwendet den Tabellennamen als Schlüssel. Pakete, die das Speicherformat auch von Änderungen des Tabellennamens entkoppeln möchten, geben die Zuordnung von Alias und Klasse explizit an, wie im obigen Beispiel.
Das Standard-getMorphClass() sucht nach der konkreten Modellklasse. Wenn Sie eine Unterklasse von Article verwenden, wird der Alias der Elternklasse nicht automatisch geerbt. Pakete, die das Ersetzen von Modellen unterstützen, brauchen ein Design, das die tatsächlich verwendeten Klassen in der Zuordnung registriert.
Den strikten Modus zur Richtlinie der gesamten Anwendung machen
enforceMorphMap() aus der offiziellen Dokumentation registriert Aliase und ruft requireMorphMap(true) in einem Schritt auf. Diese Erzwingung ist nicht auf das Paket beschränkt.
In der Standardimplementierung von
v13.42.0 wirkt der strikte Modus in beide Richtungen.
- Vom Modell zum Typnamen:
getMorphClass()wirft für nicht registrierte Modelle eineClassMorphViolationException. Für die eingebaute generische KlassePivotgibt es eine Ausnahme, für normale Modelle und eigene Pivots jedoch nicht. - Vom gespeicherten Typnamen zur Klasse:
getActualClassNameForMorph()wirft dieselbe Ausnahme für Werte, die kein Schlüssel der Zuordnung sind. Diese Methode wird auch beim normalen Abruf übermorphTo()und beim Eager Loading verwendet. Selbst wenn die Klasse als „Wert“ in der Zuordnung steht, kann ein alter vollqualifizierter Klassenname, der kein „Schlüssel“ ist, nicht geladen werden.
morphMap() und überlassen es der nutzenden Anwendung, ob die Zuordnung anwendungsweit erzwungen wird. Erzwingt die nutzende Anwendung sie bereits, setzen Sie sie vom Paket aus nicht mit requireMorphMap(false) zurück.
Bestehende Daten migrieren, bevor Sie die Kompatibilität beurteilen
Für Neuinstallationen und für Upgrades bestehender Anwendungen gelten bei der Einführung der Alias-Registrierung unterschiedliche Voraussetzungen. Auch die offizielle Dokumentation weist darauf hin, dass bei der Einführung bestehende vollqualifizierte Klassennamen in Aliase umgewandelt werden müssen.Die Prüfung der umgekehrten Abfrage allein reicht nicht
Ist der strikte Modus deaktiviert, können Sie den Artikel eines Kommentars möglicherweise weiterhin permorphTo() abrufen, solange die Klasse im alten commentable_type existiert. Das morphMany() auf der Artikelseite filtert jedoch nach dem Wert von getMorphClass(). Nach der Registrierung des Alias sucht es nach acme-publishing.article, daher fallen Zeilen mit dem alten Klassennamen aus der Kommentarliste des Artikels heraus.
MorphOneOrMany hält den Typnamen bei der Erzeugung fest und verwendet ihn sowohl für Abfragebedingungen als auch beim Anlegen neuer Datensätze. Auch wenn alte und neue Typnamen gemischt vorkommen, sucht es nicht automatisch per OR nach allen Werten.
Das Upgrade-Verfahren in das Release aufnehmen
Das folgende Fragment wandelt den alten Klassennamen in den Alias um. Implementieren Sie es als Datenmigration für die Zielanwendung und führen Sie es nicht automatisch aus einem Service-Provider aus.- Untersuchen Sie die betroffenen
*_type-Spalten und gespeicherten Werte und bereiten Sie vor dem Update ein Backup, eine Zeilenzählung und ein Wiederherstellungsverfahren vor. Wenn andere Tabellen auf dasselbe Modell verweisen, gehören sie ebenfalls dazu. - Legen Sie für die erste Einführung ein Umstellungsverfahren fest, einschließlich Schreibstopp oder Wartungsmodus, damit Prozesse, die den alten Klassennamen schreiben, und Prozesse, die den Alias schreiben, nicht gleichzeitig laufen. Ziehen Sie bei großen Tabellen auch Update-Sperren und Batch-Verarbeitung in Betracht.
- Stellen Sie Zuordnung und Daten um und wandeln Sie die Typnamen um, ohne die Werte der Primärschlüssel zu ändern. Legen Sie den alten Klassennamen im Migrationscode als die Zeichenkette fest, die tatsächlich gespeichert wurde, nicht als
Article::class, dessen Bedeutung sich bei künftigen Umbenennungen ändert. - Prüfen Sie die Anzahl verbleibender alter Werte, neue Speichervorgänge, Beziehungsabfragen in beide Richtungen und Eager Loading. Aktualisieren Sie langlebige Worker, bevor Sie Schreibvorgänge wieder zulassen.
- Prüfen Sie für ein Rollback, ob der zurückgerollte Code den gespeicherten Alias auflösen kann. Das Zurücksetzen der Composer-Version allein setzt die Typnamen in der Datenbank nicht zurück.
Gespeicherte Werte und Abfragen in beide Richtungen testen
Prüfen Sie mit Orchestra Testbench nicht nur die Zuordnung, sondern auch die in der Datenbank gespeicherten Werte und Abfragen in beide Richtungen. Das folgende Beispiel setzt voraus, dass das Paket über Composer automatisch geladen wird und der PDO-SQLite-Treiber verfügbar ist. Der Provider wird im Test explizit registriert, getrennt von der Prüfung der Auto-Discovery.- Die Registrierung des Providers deaktiviert den strikten Modus nicht, wenn die nutzende Anwendung ihn bereits aktiviert hat.
- Bei Konflikten um denselben Alias, mehreren Aliasen für dieselbe Klasse und einem ersetzten konkreten Modell bleibt das veröffentlichte Speicherformat unverändert.
- Mit Fixtures, die mit dem Klassennamen vor dem Upgrade gespeichert wurden, sind nach der Migration keine alten Werte mehr vorhanden, und die Abfragen in beide Richtungen sowie die Anzahl stimmen überein.
- Das tatsächliche Upgrade-Verfahren einschließlich Queue-Workern lässt sich mit der Kombination aus zurückgerolltem Code und zurückgerollten Daten wiederherstellen.
Herangezogene Primärquellen
- Offizielle Laravel-Dokumentation: Custom Polymorphic Types
- Offizielle Laravel-Dokumentation: Package Development
- Relation: Zusammenführen der Zuordnung und strikter Modus
- HasRelationships: Typnamen ermitteln und Klassen auflösen
- MorphOneOrMany: Typnamen festhalten, Abfragebedingungen und Speichern
- MorphTo: Typauflösung beim Eager Loading
- Offizielle Laravel-Tests: Registrieren und Auflösen von Morph Maps
- Offizielle Laravel-Tests: Lesen und Schreiben im strikten Modus