Naar hoofdinhoud gaan
Om vanuit een package relaties toe te voegen aan de modellen van de gebruikende applicatie, kun je met resolveRelationUsing() registreren hoe de relatie wordt aangemaakt. Zonder het modelbestand rechtstreeks te wijzigen, kun je net als bij gewone relaties ophalen, eager loaden en zoeken op het bestaan van relaties. Deze pagina is gecontroleerd aan de hand van de officiële broncode van Laravel Framework v13.40.0. Zie Inleiding tot Eloquent-relaties voor de basis van relaties en Package discovery voor het laden van providers.

Onderscheid met gewone definities

De officiële documentatie raadt voor gewone applicatieontwikkeling normale relatiemethoden aan en geeft aan dat dynamische definities nuttig kunnen zijn bij bijvoorbeeld packageontwikkeling. Dynamische relatieproperties zijn iets anders dan de functie om relatiedefinities van buiten het model te registreren. Bovendien is resolveRelationUsing() geen macro die willekeurige methoden toevoegt. Ontwerp de callback zo dat hij een Eloquent Relation-instantie retourneert.

Registreren op het model dat de gebruiker kiest

Als voorbeeld voegen we een hasMany met de naam acmeNotes toe aan het ordermodel van de gebruikende applicatie. We gaan uit van een opzet waarin de namespace Acme\OrderNotes\ van de package via Composer-PSR-4 aan src/ is gekoppeld en als gewone, niet-uitgestelde service provider wordt geladen.

Configuratie en gerelateerd model voorbereiden

Zet de standaardwaarde in config/acme-order-notes.php op null. Alleen het installeren registreert dan nog geen relatie op de modellen van de applicatie.
src/Models/Note.php aan de kant van de package is het gerelateerde model.
In het voorbeeld gaan we ervan uit dat de ordertabel een integer primaire sleutel id heeft en dat de tabel acme_order_notes de kolommen id, order_id, body en timestamps heeft. Voorzie order_id van een index. Gebruik je bijvoorbeeld UUID’s, zorg dan dat ook de types van de gerelateerde kolommen overeenkomen.
Het registreren van een dynamische relatie maakt geen tabellen of foreign key-constraints aan. Dat is een ander proces dan Migraties van packages publiceren en bijwerken. Ook de provider hieronder bevat geen databasetoegang of het uitvoeren van migraties.

De aanmaakmethode registreren in boot

Merge in src/OrderNotesServiceProvider.php de configuratie en registreer in boot() de callback op het doelmodel.
Publiceer in de gebruikende applicatie de configuratie en wijzig order_model naar het concrete model dat je daadwerkelijk gebruikt, zoals \App\Models\Order::class. Het laden van de provider stel je in via package discovery.
Bij hasMany() worden de foreign key order_id en de sleutel aan de ouderkant expliciet opgegeven. Zo wordt, ook als de klassenaam van het oudermodel Purchase is, niet purchase_id afgeleid en komt het overeen met de tabeldefinitie van de package.
Het doelmodel wordt ingesteld door de beheerder van de applicatie. Ontwerp het niet zo dat klassenamen of relatienamen rechtstreeks uit bijvoorbeeld HTTP-requests worden overgenomen en per request opnieuw worden geregistreerd. Bovendien heeft registreren op de Eloquent-basisklasse Model invloed op alle afgeleide modellen.

Gebruiken zoals een gewone relatie

Op het geconfigureerde ordermodel retourneert een methodeaanroep een HasMany en een property-toegang de collectie met opgehaalde resultaten.
Bij create() stelt hasMany order_id in, dus je hoeft de foreign key niet toe te voegen aan de array die de gebruiker doorgeeft. De omgekeerde relatie wordt niet automatisch toegevoegd; definieer die indien nodig apart.

Voer in de callback geen ophaalactie uit

Als de retourwaarde van de callback het resultaat van get() of een enkel model is, kan deze bijvoorbeeld bij property-toegang niet als geldige relatie worden behandeld. Scheid het opbouwen van de relatie van het ophalen van data. Builder::getRelation() bij eager loading bouwt de relatie op met een nieuwe modelinstantie. Vermijd daarom ontwerpen waarin de callback ervan uitgaat dat er al een opgehaald order-ID is, of afhankelijk van dat ID een andere relatiedefinitie retourneert. Ook met dynamische registratie verdwijnen het N+1-probleem en de beperkingen van lazy loading niet. Laad relaties bij het werken met meerdere modellen zoals gebruikelijk met bijvoorbeeld with().

Let bij belongsTo ook op het afleiden van de relatienaam

Als je de relatienaam weglaat, leidt belongsTo() de aanroeper af via een backtrace. Bij definities vanuit een closure kun je, in plaats van te vertrouwen op dezelfde afleiding als bij een gewone modelmethode, naast de foreign key en de owner key ook de relatienaam als vierde argument expliciet opgeven. Hieronder een losstaand voorbeeld waarbij de orderkant customer_id heeft en de klantkant id.

De regels voor naamconflicten en overerving begrijpen

HasRelationships slaat de closure op in een static property met de klassenaam van het model en de relatienaam als sleutel. Model::__call(), dat de aanroep van een niet-bestaande methode ontvangt, zoekt de resolver op en voert die uit met de betreffende modelinstantie. Het diagram toont het pad wanneer een resolver wordt gevonden. Let op dat conflicten met bestaande methoden of attributen een ander pad volgen. Gebruik een unieke relatienaam zoals acmeNotes en controleer op conflicten met methoden en attributen van de gebruiker en met andere packages. Met alleen method_exists() kun je het bestaan van een dynamische registratie niet controleren. Eloquents isRelation() controleert ook resolvers, maar garandeert niet dat er geen conflict met attributen is.

De levensduur van registratie en opgehaalde resultaten scheiden

De registratie van resolvers en de geladen relatiedata die elk model vasthoudt, zijn twee verschillende dingen. Als je een resolver overschrijft nadat $order->acmeNotes al is geladen, worden de resultaten die dat model vasthoudt niet automatisch bijgewerkt. Moet je opnieuw ophalen, verwijder dan eerst de relatieresultaten en gebruik daarna bijvoorbeeld load('acmeNotes').
De registratie wordt niet in de service container opgeslagen, maar in een static property. Als je requests, de geauthenticeerde gebruiker, tenants en dergelijke in de closure opneemt, kunnen deze in langlopende processen worden vastgehouden. Beperk registraties tot stabiele definities bij het opstarten en werk bij wijzigingen in configuratie of code ook processen zoals queue workers en Octane bij. Zie ook De levensduur van services ontwerpen.
Ga er evenmin van uit dat resolvers verdwijnen door de applicatie in Testbench opnieuw op te bouwen. Gebruik voor tests die de ingeschakelde en uitgeschakelde situatie vergelijken een aparte model-fixture of de procesisolatie van PHPUnit, zodat je niet afhankelijk bent van eerdere registraties.

Zowel de registratie als de SQL testen

Controleer met Orchestra Testbench niet alleen de registratie door de provider, maar ook de foreign key en het daadwerkelijk ophalen van de relatie. Hieronder een voorbeeld van tests/DynamicRelationsTest.php. Zorg dat de klassen van de package en de testnamespace via Composer geladen kunnen worden, en voorzie in PDO en de SQLite-driver.
De naam van het testmodel is OrderFixture, maar de foreign key blijft order_id in plaats van order_fixture_id. Zo kun je controleren dat er niet wordt vertrouwd op afleiding uit de klassenaam. Controleer in de CI van de package daarnaast de volgende grensgevallen.
  • Bij een aparte, niet-geregistreerde fixture wordt geen relatie toegevoegd als order_model null is.
  • Overerving naar kindmodellen, en de prioriteit wanneer alleen op het kind een resolver met dezelfde naam is geregistreerd.
  • Bij een conflict met bestaande methoden of attributen is de beoogde API bruikbaar.
  • Het verschil na het opnieuw registreren van de resolver tussen een model waarop de relatie al eens is geladen en een nieuw model.
  • Kolomtypes, indexen en foreign key-constraints op de ondersteunde databases, en primaire sleutels die door de gebruiker zijn gewijzigd.

Geraadpleegde primaire bronnen

Laatst gewijzigd op 4 november 2026