Naar hoofdinhoud gaan
Wil je de opgeslagen data van polymorfe relaties blijven lezen nadat je een model van je package hebt verplaatst of hernoemd, scheid dan de klassenaam van het model van de typenaam die in de database wordt opgeslagen. Een morph map-alias is minder een afkorting dan een contract over het opslagformaat dat je over releases heen onderhoudt. Deze pagina is gebaseerd op de officiële implementatie van Laravel Framework v13.42.0. De basis van relaties vind je in de Introductie tot Eloquent-relaties. Hoe je van buitenaf relaties toevoegt aan de modellen van de gebruikende app, lees je in Dynamische relaties registreren vanuit packages.

Typenamen en relatienamen scheiden

Een polymorfe relatie combineert de primaire sleutel van het gerelateerde model met een typenaam om naar meerdere soorten modellen te verwijzen. Als reacties bijvoorbeeld bij artikelen of video’s kunnen horen, heb je naast commentable_id ook commentable_type nodig. Zonder registratie wordt normaal gesproken een volledig gekwalificeerde klassenaam zoals Acme\Publishing\Models\Article als type gebruikt. Na het registreren van een alias wordt acme-publishing.article gebruikt, bijvoorbeeld bij het aanmaken van nieuwe relaties. Registratie alleen herschrijft de waarden in bestaande rijen niet. Een morph map maakt geen relatiemethoden of tabellen aan. Modellen, kolommen en indexen maak je zoals gewoonlijk aan.

Alleen package-specifieke aliassen registreren

In het voorbeeld is Acme\Publishing\ via Composer PSR-4 gekoppeld aan src/. Definieer eerst src/Models/Article.php.
In src/Models/Comment.php definieer je de omgekeerde relatie.
acme_articles krijgt id, title en timestamps, acme_comments krijgt id, body, commentable_type, commentable_id en timestamps. De primaire sleutels in dit voorbeeld zijn integers. Je kunt de kolommen en de samengestelde index van $table->morphs('commentable') gebruiken, maar stem bij UUID’s en vergelijkbare opzetten de kolomtypes af op het gerelateerde model. Registreer daarna de alias in boot() van een gewone, niet-uitgestelde service provider, src/PublishingServiceProvider.php.
Het laden van de provider configureer je met package discovery. Tijdens de registratie hoef je geen modellen op te slaan of typenamen in de database bij te werken. Het migreren van bestaande data is een aparte taak.
$alias is acme-publishing.article en $class is Article::class. Zolang Comment zelf niet het gerelateerde model van een andere polymorfe relatie wordt, hoef je voor dit voorbeeld geen alias voor Comment te registreren.

De regels voor samenvoegen en naamconflicten begrijpen

Relation::morphMap() werkt op een statische map. Bij het standaard samenvoegen worden de nieuwe en de bestaande array gecombineerd met $map + static::$morphMap. Geeft een package false mee als tweede argument, dan wist het de registraties van andere packages en van de gebruikende app. Behoud bij normale registratie de bestaande map en kies een alias die de vendor en het package herkenbaar maakt, in plaats van een generieke naam zoals article. Gebruik dezelfde alias niet opnieuw voor een andere betekenis. Geef je alleen een lijst klassenamen mee, dan instantieert Laravel elk model en gebruikt het de tabelnaam als sleutel. Packages die het opslagformaat ook los willen koppelen van wijzigingen in de tabelnaam, declareren de koppeling tussen alias en klasse expliciet, zoals in het voorbeeld hierboven.
Voegt de gebruikende app een andere alias toe voor dezelfde klasse, dan kan de registratievolgorde bepalen welke waarden voortaan worden opgeslagen en welke voorwaarden relatiequery’s gebruiken. Dat beide namen geregistreerd zijn, betekent niet automatisch dat de compatibiliteit behouden blijft. Ook als je het vervangen van de klasse toestaat, controleer je als onderdeel van het publieke contract dat opgeslagen data met dezelfde alias leesbaar blijft.
De standaard getMorphClass() zoekt naar de concrete modelklasse. Een subklasse van Article erft niet automatisch de alias van de bovenliggende klasse. Packages die het vervangen van modellen ondersteunen, hebben een ontwerp nodig dat de daadwerkelijk gebruikte klassen in de map registreert.

Maak de strikte modus een beleid voor de hele app

enforceMorphMap() uit de officiële documentatie registreert aliassen en roept requireMorphMap(true) in één stap aan. Het is geen verplichting die alleen voor het package geldt. In de standaardimplementatie van v13.42.0 werkt de strikte modus in beide richtingen.
  • Van model naar typenaam: getMorphClass() gooit een ClassMorphViolationException voor niet-geregistreerde modellen. De ingebouwde generieke klasse Pivot heeft een uitzondering, maar gewone modellen en eigen pivots krijgen die vrijstelling niet.
  • Van opgeslagen typenaam naar klasse: getActualClassNameForMorph() gooit dezelfde exception voor waarden die geen sleutel in de map zijn. Dit wordt ook gebruikt bij het gewone ophalen via morphTo() en bij eager loading. Ook als de klasse als “waarde” in de map staat, kan een oude volledig gekwalificeerde klassenaam die geen “sleutel” is niet worden geladen.
Is de strikte modus uitgeschakeld, dan worden typenamen die niet in de map staan als klassenamen behandeld. Dat garandeert echter niet dat de klasse bestaat of dat het om het verwachte gerelateerde model gaat. Een package registreert meestal alleen zijn eigen modellen met morphMap() en laat de keuze om de map app-breed af te dwingen aan de gebruikende app over. Dwingt de gebruikende app die al af, zet hem dan niet vanuit het package terug met requireMorphMap(false).

Migreer bestaande data voordat je de compatibiliteit beoordeelt

Voor nieuwe installaties en voor upgrades van bestaande apps gelden andere voorwaarden bij het invoeren van aliasregistratie. Ook de officiële documentatie waarschuwt dat je bij de invoering bestaande volledig gekwalificeerde klassenamen naar aliassen moet omzetten.

Alleen de omgekeerde opvraging controleren is niet genoeg

Met de strikte modus uitgeschakeld kun je het artikel van een reactie mogelijk nog steeds ophalen met morphTo(), zolang de klasse in de oude commentable_type bestaat. Maar morphMany() aan de kant van het artikel filtert op de waarde van getMorphClass(). Na het registreren van de alias zoekt het naar acme-publishing.article, dus rijen met de oude klassenaam verdwijnen uit de lijst met reacties van het artikel. MorphOneOrMany legt de typenaam vast bij het aanmaken en gebruikt die zowel in de queryvoorwaarden als bij het aanmaken van nieuwe records. Ook als oude en nieuwe typenamen door elkaar voorkomen, zoekt het niet automatisch met OR naar alle waarden.

Neem de upgradeprocedure op in de release

Het volgende fragment zet de oude klassenaam om naar de alias. Implementeer het als datamigratie voor de betreffende app en voer het niet automatisch uit vanuit een service provider.
Met deze stap alleen is de deployment nog niet klaar.
  1. Breng de betreffende *_type-kolommen en opgeslagen waarden in kaart en zorg vóór de update voor een back-up, een telling van de rijen en een herstelprocedure. Verwijzen andere tabellen naar hetzelfde model, neem die dan ook mee.
  2. Leg voor de eerste invoering een overstapprocedure vast, inclusief het stoppen van schrijfacties of onderhoud, zodat processen die de oude klassenaam schrijven en processen die de alias schrijven niet tegelijk draaien. Overweeg bij grote tabellen ook update-locks en verwerking in batches.
  3. Schakel de map en de data om en zet de typenamen om zonder de waarden van de primaire sleutels te wijzigen. Leg de oude klassenaam in de migratiecode vast als de string die in het verleden daadwerkelijk is opgeslagen, niet als Article::class, waarvan de betekenis verandert bij toekomstige hernoemingen.
  4. Controleer het aantal resterende oude waarden, nieuwe opslagacties, opvragingen in beide richtingen en eager loading, en werk langlopende workers bij voordat je de schrijfacties hervat.
  5. Controleer bij een rollback of de teruggezette code de opgeslagen alias kan oplossen. Alleen de Composer-versie terugzetten zet de typenamen in de database niet terug.
Behoud je een alias die al vastligt en wijzig je alleen de namespace van het model, pas dan de waarde in de map aan naar de nieuwe klasse. De typenamen blijven dan gewoon werken. Het wijzigen van de alias zelf behandel je daarentegen als een wijziging die een datamigratie vereist. Stem de upgradeprocedure ook af op Versiecompatibiliteit van packages beheren en Migrations van packages publiceren en bijwerken.

Opgeslagen waarden en opvragingen in beide richtingen testen

Controleer met Orchestra Testbench niet alleen de map, maar ook de in de database opgeslagen waarden en opvragingen in beide richtingen. Het volgende voorbeeld gaat ervan uit dat het package via Composer wordt geautoload en dat de PDO SQLite-driver beschikbaar is. De provider wordt expliciet in de test geregistreerd, los van het controleren van package discovery.
De map en de vlag voor afdwingen zijn aparte statische toestanden, dus ga er niet van uit dat het opnieuw opbouwen van de applicatie ze reset. Dit voorbeeld bewaart en herstelt beide. Herschrijf ze ook in productie niet per request of per tenant, en beperk je tot een stabiele registratie bij het opstarten. Relaties die al zijn aangemaakt houden hun typenaam vast, dus een tussentijdse wijziging van de registratie wordt ook niet uniform doorgevoerd in bestaande instanties. Voeg daarnaast de volgende regressiegevallen toe aan de CI van het package.
  • Het registreren van de provider schakelt de strikte modus niet uit wanneer de gebruikende app die al heeft ingeschakeld.
  • Het gepubliceerde opslagformaat verandert niet bij conflicten rond dezelfde alias, meerdere aliassen voor dezelfde klasse en een vervangen concreet model.
  • Met fixtures die zijn opgeslagen met de klassenaam van vóór de upgrade blijven na de migratie geen oude waarden over, en kloppen de opvragingen in beide richtingen en de aantallen.
  • De echte upgradeprocedure, inclusief queue workers, kan worden hersteld met de combinatie van teruggezette code en data.

Geraadpleegde primaire bronnen

Laatst gewijzigd op 11 november 2026