afterQuery() gebruiken. Naast het toevoegen van SQL-voorwaarden kun je hiermee ook een Closure in de query builder registreren die op de opgehaalde resultaten wordt uitgevoerd.
Het is echter geen “hook die bij elke SQL-uitvoering een resultaat van hetzelfde type ontvangt”. Afhankelijk van de ophaalmethode verschillen de inhoud van de doorgegeven Collection en het aantal aanroepen.
Deze pagina is gericht op Laravel 13. Uitgaande van After Query Callbacks in de officiële documentatie controleert ze de grenzen van het gebruik aan de hand van de Eloquent builder en de officiële tests.
Resultaatverwerking bundelen in een lokale scope
Als je herhaaldelijk modellen op ID opzoekt, kun je een scope maken die de sleutels van de resultaten gelijktrekt. Definieer het volgende voorbeeld inapp/Models/Post.php.
$posts blijft een Eloquent Collection en de elementen blijven Post. get(42) geeft het model met sleutel 42 terug, of null als het niet bestaat. Als je de opgehaalde kolommen beperkt met select(), neem dan ook de id op die als sleutel dient.
Als je de resultaten maar op één plek verwerkt, is get()->keyBy('id') eenvoudiger. Kies afterQuery() wanneer je de verwerking na het ophalen wilt opnemen in een scope of een hergebruikte query. Hoe je scopes zelf definieert, lees je in Eloquent-scopes.
De uitvoeringsvolgorde van get lezen
Eloquent\Builder::get() past globale scopes toe, maakt de modellen aan, laadt de opgegeven relaties via eager loading en voert als laatste de afterQuery-callbacks uit.
Bij een gewone get() kunnen callbacks de via eager loading geladen relaties gebruiken. Toegang tot relaties die je niet hebt opgegeven kan extra SQL veroorzaken, dus ontwerp de scope samen met eager loading.
Het moment verschilt van dat van het model-event retrieved. retrieved wordt afgevuurd wanneer elk model in Model::newFromBuilder() wordt aangemaakt, dus vóór de eager loading van get(). afterQuery(), dat de verzameling opgehaalde modellen verwerkt, en de events per model hebben niet dezelfde rol.
Retourwaarden worden doorgegeven aan de volgende callback
afterQuery() voegt Closures toe in registratievolgorde. Het uitvoerende deel, applyAfterQueryCallbacks(), is als volgt geïmplementeerd.
?? gebruikt, maar ?:, dat steunt op de truthy-evaluatie van PHP.
Je kunt ook een
void-callback gebruiken die de Collection direct wijzigt en niets teruggeeft. In dat geval blijft hetzelfde gewijzigde object behouden. Maak je daarentegen een nieuwe Collection, dan moet je die teruggeven.
Intern zijn argument en retourwaarde mixed, maar maak willekeurige typeconversies geen onderdeel van je publieke API. Als je bijvoorbeeld het resultaat van get() vervangt door een string of array, breek je de ->first()-aanroep die first() daarna doet. Beperk je in scopes tot verwerking die de modellen, het Collection-type, het aantal en de volgorde behoudt.
Verschillen per ophaalmethode
De volgende tabel laat zien hoe een in de Eloquent builder geregistreerdeafterQuery() wordt behandeld.
first() roept intern limit(1)->get()->first() aan. Het gebruik van first() betekent niet dat je de parameter van de Closure naar Post moet wijzigen.
Heb je alleen waarden nodig, gebruik dan geen scope en schrijf Post::query()->pluck('id'). Om waarden op te halen na verwerking van de modellen, kun je Post::keyedById()->get()->pluck('id') schrijven. De pluck() in het tweede geval is een bewerking op de opgehaalde Collection, niet op de query builder.
Het aantal per pagina en de volgende ophaalpositie niet wijzigen
paginate() telt het totaal in SQL en haalt daarna de modellen van de pagina op. Als je de Collection na het ophalen filtert, wordt het totaal niet opnieuw berekend.
Daarnaast gebruiken simplePaginate() en cursorPaginate() het verwerkte resultaat om te bepalen of er een volgende pagina is. chunkById() gebruikt de ID aan het einde van een chunk als volgende ophaalpositie. Als je in de callback het aantal of de volgorde wijzigt, kunnen paginadetectie en vervolgpositie niet meer overeenkomen met de oorspronkelijke query.
Geef filtering, sortering en kolommen op in SQL, en beperk afterQuery() tot verwerking die de betekenis van de opgehaalde resultaten niet verandert. Wil je alleen het uitvoerformaat van een API wijzigen, overweeg dan ook een transformatie met API-resources.
De officiële documentatie bevat een voorbeeld dat een attribuut met een fragment uit zoekresultaten aan modellen toevoegt. Toewijzen aan modelattributen kan ook wijzigen wat er wordt opgeslagen. Vermijd ontwerpen die save() rechtstreeks aanroepen op modellen met weergave-attributen die niet in de database bestaan.
Registraties blijven in de query builder
Elke aanroep vanafterQuery() voegt een Closure toe, en uitvoeren verwijdert de registratie niet. Opnieuw ophalen met dezelfde builder voert hem opnieuw uit, en als je dezelfde scope twee keer aanroept, wordt ook de callback twee keer geregistreerd. Ook bij het clonen van de builder worden de geregistreerde callbacks overgenomen.
In code die meerdere keren ophaalt of in langlopende workers maak je voor elk doel een nieuwe query, en beperk je callbacks tot resultaatverwerking zonder bijwerkingen. Als je daar e-mailverzending of persistentie plaatst, verwar je meervoudige uitvoeringen door chunks of cursors snel met één zakelijke handeling.
Bied je herbruikbare scopes aan in een package, leg dan ook de ondersteunde ophaalmethoden vast als publieke API en test het contract voor types en aantal aanroepen.
De verschillen tussen ophaalpaden testen
Controleer in de Laravel 13-applicatie die de scope gebruikt niet alleen de sleutelverwerking, maar ook de aanroepen bijfirst(), cursor() en lege pagina’s.
Het volgende voorbeeld gaat uit van het eerdere Post-model en een migratie voor een posts-tabel met id, title, body, created_at en updated_at. RefreshDatabase isoleert de records tussen tests.
Maak de test aan in tests/Feature/AfterQueryTest.php.
Checklist voor onderhoud
- Leg de ophaalmethoden vast die de scope ondersteunt, en de parameter- en retourtypes van de Closure.
- Controleer een leeg resultaat van
get()en eenpaginate()met totaal 0 afzonderlijk. - Bevestig bij chunks en cursors dat het aantal aanroepen en de ophaalvolgorde niet veranderen.
- Ontwerp de code zo dat dubbel geregistreerde callbacks of hergebruikte builders geen zakelijke bijwerkingen veroorzaken.
- Controleer bij Laravel-updates opnieuw de aanroeppaden van
get(),pluck(),cursor()enBuildsQueries.
Gerelateerde pagina’s
Eloquent-scopes
De basis om SQL-beperkingen te bundelen in lokale en globale scopes.
Eloquent-collecties
Het verschil tussen Collections met modellen en Support Collections.
Eloquent-observers en model-events
Events per model, die verschillen van het verwerken van opgehaalde resultaten.
API-resources
Scheid de transformatie van door de API teruggegeven weergavegegevens van het opslaan van modellen.