Zum Hauptinhalt springen
Wenn Sie ein Paketmodell verschieben oder umbenennen und die gespeicherten Daten polymorpher Beziehungen weiterhin lesen möchten, trennen Sie den Klassennamen des Modells vom Typnamen, der in der Datenbank gespeichert wird. Ein Morph-Map-Alias ist weniger eine Kurzschreibweise als ein Vertrag über das Speicherformat, den Sie über Releases hinweg einhalten. Diese Seite bezieht sich auf die offizielle Implementierung von Laravel Framework 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 neben commentable_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 ist Acme\Publishing\ per Composer-PSR-4 auf src/ abgebildet. Definieren Sie zuerst src/Models/Article.php.
In 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.
Das Laden des Providers konfigurieren Sie über die Paket-Auto-Discovery. Während der Registrierung müssen Sie weder Modelle speichern noch Typnamen in der Datenbank aktualisieren. Die Migration bestehender Daten ist eine separate Aufgabe.
$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.
Fügt die nutzende Anwendung für dieselbe Klasse einen anderen Alias hinzu, kann die Registrierungsreihenfolge die künftig gespeicherten Werte und die Bedingungen von Beziehungsabfragen ändern. Dass beide Namen registriert sind, bedeutet nicht zwingend, dass die Kompatibilität erhalten bleibt. Auch wenn Sie das Ersetzen der Klasse erlauben, stellen Sie als Teil des öffentlichen Vertrags sicher, dass gespeicherte Daten mit demselben Alias lesbar bleiben.
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 eine ClassMorphViolationException. Für die eingebaute generische Klasse Pivot gibt 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 über morphTo() 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.
Ist der strikte Modus deaktiviert, werden Typnamen, die nicht in der Zuordnung stehen, als Klassennamen behandelt. Das garantiert jedoch weder, dass die Klasse existiert, noch, dass es sich um das erwartete verknüpfte Modell handelt. Pakete registrieren in der Regel nur ihre eigenen Modelle mit 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 per morphTo() 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.
Mit diesem Schritt allein ist das Deployment nicht abgeschlossen.
  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
Wenn Sie einen bereits festgelegten Alias beibehalten und nur den Namespace des Modells ändern, ändern Sie den Wert in der Zuordnung auf die neue Klasse. Die Typnamen funktionieren dann unverändert weiter. Eine Änderung des Alias selbst behandeln Sie dagegen als Änderung, die eine Datenmigration erfordert. Stimmen Sie das Upgrade-Verfahren auch mit Versionskompatibilität von Paketen verwalten und Paket-Migrationen veröffentlichen und aktualisieren ab.

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.
Zuordnung und Erzwingungs-Flag sind getrennte statische Zustände. Gehen Sie daher nicht davon aus, dass ein Neuaufbau der Anwendung sie zurücksetzt. Dieses Beispiel sichert und stellt beide wieder her. Schreiben Sie sie auch in Produktion nicht pro Request oder Mandant um, sondern beschränken Sie sich auf eine stabile Registrierung beim Start. Bereits erzeugte Beziehungen halten ihren Typnamen fest, daher wirkt sich eine zwischenzeitliche Änderung der Registrierung auch nicht einheitlich auf bestehende Instanzen aus. Ergänzen Sie die CI des Pakets außerdem um folgende Regressionsfälle.
  • 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

Zuletzt geändert am 11. November 2026