Skip to main content
Laravel-applicaties

Laravel 12.51: 3 Functies Die Mijn Bouwwijze Veranderden

Laravel 12.51 brengt drie functies die veranderden hoe ik apps bouw. Prestatieverbeteringen, queryoptimalisaties en de update die je niet mag overslaan.

19 min
Leestijd
3,636
Woorden
Gepubliceerd
Engr Mejba Ahmed

Geschreven door

Engr Mejba Ahmed

Artikel delen

Laravel 12.51: 3 Functies Die Mijn Bouwwijze Veranderden

Vier seconden. Zo lang duurde het laden van een enkele adminpagina in een klantproject dat ik vorig jaar uitvoerde.

De schuldige was geen ontbrekende index. Het was geen N+1-query. Het was een firstOrCreate-aanroep — volkomen normaal uitziende, onopvallende Laravel-code — waarbij de array met aanmaakaAttributen een API-aanroep naar een externe factureringsdienst en een hashoperatie bevatte. Beide werden bij elk verzoek uitgevoerd. Zelfs wanneer de gebruiker al bestond. Zelfs wanneer er niets werd aangemaakt.

Ik vond de bug na bijna een uur dd()-aanroepen en langzaam door de Laravel Debugbar-tijdlijn scrollen. Toen het eindelijk klikte, voelde ik die specifieke mix van opluchting en verlegenheid die alleen ontstaat als je beseft dat de bug niet onzichtbaar was omdat hij slim was — maar omdat ik niet begreep hoe PHP-argumentevaluatie eigenlijk werkt.

De oplossing die ik destijds schreef was een workaround: controleer eerst of het record bestaat, roep dan de juiste methode aan op basis van het resultaat. Het werkte. Het was omslachtig. En elke keer dat ik dat patroon in andere projecten tegenkwam, dacht ik: "Er moet een schonere manier zijn."

Laravel 12.51 levert die schonere manier standaard. En dat is slechts een van de drie dingen in deze release waardoor ik stopte met waar ik mee bezig was en de changelog daadwerkelijk twee keer las.

De andere twee — een native query timeout-methode en koppelbare validator callbacks — pakken pijnpunten aan die ik al jaren oplos met aangepaste code en workarounds. De ene gaat over prestaties die stilletjes productieapps degraderen. De andere gaat over codekwaliteit die stilletjes teams degradeert. Beide zijn belangrijk.

Dit is wat er daadwerkelijk veranderd is, hoe je het gebruikt, en — omdat de meeste changelogs dit deel overslaan — wanneer elke functie jouw tijd waard is en wanneer niet.


Het Probleem Dat Zich Verstopt in Je firstOrCreate-aanroepen

Voordat ik de oplossing uitleg, moet je begrijpen waarom de bug überhaupt bestaat. Want als je de meeste Laravel-ontwikkelaars bent, schrijf je deze code al jaren en heeft het nooit iets duidelijk kapotgemaakt.

De kern van het probleem: PHP evalueert alle functieargumenten voordat de functie wordt aangeroepen.

Die zin klinkt eenvoudig. De implicaties zijn dat niet.

Wanneer je dit schrijft:

$user = User::firstOrCreate(
    ['email' => $email],
    [
        'name' => $name,
        'avatar' => $this->avatarService->generate($email),   // API-aanroep
        'api_key' => $this->keyService->issue($email),         // nog een API-aanroep
        'hash' => bcrypt(Str::random(64)),                     // CPU-gebonden
    ]
);

PHP bouwt de volledige tweede array — roept generate(), issue(), en bcrypt() aan — voordat firstOrCreate ook maar begint. Vervolgens voert firstOrCreate zijn query uit. Als de gebruiker bestaat, geeft het die gebruiker terug en gooit de berekende array volledig weg. Alle berekeningen die je net uitvoerde? Verspild.

In ontwikkeling doet dit geen pijn. Testdatabases hebben weinig records. API-aanroepen raken sandboxes die in milliseconden reageren. Je voert de code uit, het werkt, je shipt het.

In productie wordt dit een belasting die je betaalt bij elk verzoek dat een bestaand record aanraakt — wat in een volwassen applicatie bijna elk verzoek is. Als je avatardienst 300ms kost en je sleuteldienst 200ms, voeg je 500ms van pure verspilling toe aan die verzoeken. Voor altijd. Totdat iemand het trage endpoint traceert en uitzoekt waarom.

De oplossing die Laravel 12.51 introduceert is schoon: geef het tweede argument door als een closure.

$user = User::firstOrCreate(
    ['email' => $email],
    function () use ($name, $email) {
        return [
            'name' => $name,
            'avatar' => $this->avatarService->generate($email),
            'api_key' => $this->keyService->issue($email),
            'hash' => bcrypt(Str::random(64)),
        ];
    }
);

De implementatie in Laravel 12.51 controleert of het tweede argument callable is. Als dat zo is, wordt de closure alleen uitgevoerd wanneer het record niet bestaat. Bestaande records worden onmiddellijk teruggegeven. Het aanmaakwerk wordt nooit uitgevoerd.

Dit is luie evaluatie — een patroon dat al jaren in PHP's gereedschapskist zit via closures en generators, maar dat niet beschikbaar was in de recordaanmaakmethoden van de query builder totdat nu.

De createOrFirst-methode krijgt dezelfde behandeling. Die methode werkt in omgekeerde volgorde: hij probeert eerst in te voegen en valt terug op een select bij schending van een unique constraint. Dezelfde luie evaluatielogica is van toepassing. Geef een closure mee, en de attributen worden alleen berekend als er daadwerkelijk een insert nodig is.

Wanneer dit er daadwerkelijk toe doet voor jouw codebase

Voor alle duidelijkheid: als je aanmaakaAttributen statische waarden zijn — strings, integers, boolean vlaggen — heb je de closure niet nodig. PHP evalueert die in microseconden. De optimalisatie is zinvol wanneer berekening duur is. Vraag jezelf af: doet iets in dat tweede argument een netwerkoproep, voert een cryptografische operatie uit, raakt de database, of voert code uit die meer dan een paar milliseconden duurt?

Als ja, wikkel het in. Elke firstOrCreate- en createOrFirst-aanroep met dure aanmaaaklogica is een kandidaat.

Vind ze nu in je project:

grep -rn "firstOrCreate\|createOrFirst" app/ --include="*.php"

Open elk bestand. Kijk naar het tweede argument. Als het echt werk doet, betaal je de eager evaluatiebelasting.

De cijfers uit mijn eigen tests — met een kunstmatige slaap van twee seconden om twee API-aanroepen te simuleren — waren opvallend: het opzoeken van een bestaand record daalde van 2.003ms naar 8ms. Het aanmaken van een nieuw record bleef op 2.003ms omdat het werk daadwerkelijk nodig is. Dat is het gedrag dat je wilt. Bereken alleen wanneer het moet.

Maar hier wordt het interessant, en hier stoppen de meeste artikelen over deze functie vóór het belangrijke deel.


Lange Queries Stoppen Voordat Ze Je Database Stoppen

De tweede functie is operationeel anders dan luie evaluatie. Luie evaluatie gaat over het vermijden van onnodig werk. Query timeout gaat over het beperken van noodzakelijk werk dat uit de hand gelopen is.

Als je met Laravel hebt gewerkt op grote MySQL-tabellen — ik heb het over honderdduizenden tot miljoenen rijen — heb je dit patroon waarschijnlijk gezien: een query die zes maanden geleden prima werkte, duurt nu 30, 40, 60 seconden. De tabel groeide. Er werden geen nieuwe indexen toegevoegd. Een analyserapport dat vroeger in minder dan een seconde liep, houdt nu een databaseverbinding open lang genoeg dat meerdere HTTP-verzoeken een timeout krijgen.

MySQL heeft hier al jaren een oplossing voor: de MAX_EXECUTION_TIME-optimizer hint. Je kunt hem direct in een SELECT-statement insluiten om een milliseconde-niveau plafond in te stellen voor de queryduur. Als de query dat plafond overschrijdt, beëindigt MySQL hem en geeft een fout terug in plaats van hem onbepaald te laten draaien.

Het probleem was dat het gebruik hiervan in Laravel vereiste dat je buiten de query builder moest gaan:

// Optie 1: Rauwe SQL. Verbreekt de vloeiende interface.
$results = DB::select('SELECT /*+ MAX_EXECUTION_TIME(5000) */ * FROM orders WHERE status = "pending"');

// Optie 2: Sessieniveau-instelling. Beïnvloedt meer dan alleen deze query.
DB::statement('SET SESSION MAX_EXECUTION_TIME = 5000');
$orders = Order::where('status', 'pending')->get();
DB::statement('SET SESSION MAX_EXECUTION_TIME = 0'); // moet worden gereset anders is alles begrensd

// Optie 3: Aangepaste macro. Werkt, maar vereist onderhoud en projectspecifieke opzet.
Builder::macro('maxExecutionTime', function (int $ms) {
    return $this->beforeQuery(function ($q) use ($ms) {
        $q->addSelectExpression("/*+ MAX_EXECUTION_TIME($ms) */");
    });
});

Ik gebruikte Optie 3 in meerdere projecten. Het werkt, maar je draagt aangepaste code mee die nieuwe teamleden niet kennen, die niet verschijnt in IDE-autocompletion, en die je moet onthouden toe te voegen aan elk nieuw Laravel-project dat je start.

Laravel 12.51 levert dit standaard:

$orders = Order::where('status', 'pending')
    ->timeout(5)
    ->get();

De timeout()-methode accepteert seconden (niet milliseconden — let op het verschil met de rauwe MySQL-hint, die milliseconden gebruikt). Onder de motorkap converteert Laravel dit en injecteert de optimizer hint in de query. Bereik het limiet en je krijgt een QueryException met MySQL's bericht over een onderbroken query.

Hier is het productieklare patroon voor het inpakken van timeoutqueries:

use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Log;

public function generateSalesReport(array $filters): array
{
    try {
        $results = Order::query()
            ->where('created_at', '>=', $filters['start_date'])
            ->where('created_at', '<=', $filters['end_date'])
            ->with(['items', 'customer', 'discounts'])
            ->when($filters['status'] ?? null, fn($q, $s) => $q->where('status', $s))
            ->timeout(15)
            ->get();

        return $this->formatReport($results);

    } catch (QueryException $e) {
        if (str_contains($e->getMessage(), 'Query execution was interrupted')) {
            Log::warning('Sales report query timed out', [
                'filters' => $filters,
                'timeout_seconds' => 15,
            ]);

            // Graceful degradation: geef gecachede resultaten of een beperkte dataset terug
            return $this->getCachedReport($filters) ?? [];
        }

        throw $e; // Gooi onverwachte databasefouten opnieuw
    }
}

Dit patroon — timeout plus expliciete afhandeling van uitzonderingen — is wat "timeout toegevoegd" scheidt van "timeout correct afgehandeld." De timeout alleen vervangt een oneindige hang door een uitzondering. De afhandeling van de uitzondering is waar je je gebruikers daadwerkelijk beschermt.

Het deel dat de meeste tutorials overslaan: dit is MySQL-specifiek

Het gedrag van de timeout()-methode is driverspecifiek. Voor MySQL en MariaDB gebruikt het de MAX_EXECUTION_TIME-optimizer hint. Voor PostgreSQL is de implementatie anders — PostgreSQL gebruikt statement-niveau timeouts die anders worden geconfigureerd, en het cross-driver gedrag is mogelijk niet wat je verwacht.

Als je een multi-tenant SaaS bouwt waarbij verschillende clients op verschillende databasedrivers kunnen zitten, of als je lokaal op SQLite ontwikkelt maar op MySQL in productie deployt (een verrassend veelvoorkomende opstelling), test dan je timeoutgedrag op de daadwerkelijke productiedriver voordat je erop vertrouwt.

Ook de moeite waard om te weten: MAX_EXECUTION_TIME is van toepassing op SELECT-statements in MySQL. Het is niet van toepassing op INSERT-, UPDATE- of DELETE-operaties — die vereisen andere technieken voor een timeout. De timeout()-methode in Laravel is voor leesqueries.

Gebruik het op je rapportageeindpunten. Gebruik het in wachtrijtaken die database-intensieve verwerking uitvoeren. Gebruik het overal waar een query legitiem een tijdje mag draaien maar nooit voor altijd.

Op dit punt heb je luie evaluatie en querytimeouts toegepast. De derde wijziging in 12.51 is anders van karakter — minder over ruwe prestaties, meer over het soort codekwaliteit dat zich over een heel team in de loop van de tijd opstapelt.


Validator Callbacks: Handmatige Validatie Laten Aanvoelen als Laravel

HTTP-verzoekvalidatie in Laravel is uitstekend. Je definieert een Form Request-klasse, injecteert die in je controller, en het framework handelt alles automatisch af. Validatie slaagt, de controller wordt uitgevoerd. Validatie mislukt, de gebruiker krijgt fouten terug. Schoon, automatisch, nul boilerplate.

Maar niet alle validatie vindt plaats in HTTP-verzoeken.

Artisan-commando's moeten argumenten valideren. Serviceklassen moeten invoer van wachtrijtaken valideren. Domeinclassen moeten gegevens van externe API's valideren. In al deze scenario's doe je handmatige validatie — maak je zelf een Validator-instantie en controleer je het resultaat.

Het klassieke patroon:

public function handle(): int
{
    $validator = Validator::make($this->arguments(), [
        'email' => 'required|email|max:255',
        'role'  => 'required|in:admin,editor,viewer',
    ]);

    if ($validator->fails()) {
        $this->error('Validatie mislukt:');
        foreach ($validator->errors()->all() as $error) {
            $this->line("  — {$error}");
        }
        return self::FAILURE;
    }

    $this->info('Gebruiker aanmaken...');
    $this->userService->create($validator->validated());
    return self::SUCCESS;
}

Deze code is correct. Maar lees hem nogmaals en let op het ritme: je bouwt de validator, dan verbreek je die stroom om fails() te controleren, het foutpad te behandelen, en dan — pas dan — ga je verder met het succespad. Twee afzonderlijke controlestromen voor één validatieoperatie.

Laravel 12.51 voegt whenFails() en whenPasses() direct toe op de Validator-instantie:

public function handle(): int
{
    return Validator::make($this->arguments(), [
        'email' => 'required|email|max:255',
        'role'  => 'required|in:admin,editor,viewer',
    ])
    ->whenFails(function (Validator $validator) {
        $this->error('Validatie mislukt:');
        foreach ($validator->errors()->all() as $error) {
            $this->line("  — {$error}");
        }
        return self::FAILURE;
    })
    ->whenPasses(function (Validator $validator) {
        $this->info('Gebruiker aanmaken...');
        $this->userService->create($validator->validated());
        return self::SUCCESS;
    });
}

Beide callbacks ontvangen de validatorinstantie als parameter. De terugkeerwaarde van welke callback ook wordt uitgevoerd, wordt de terugkeerwaarde van de ketting. Als validatie mislukt, wordt whenFails uitgevoerd en wordt zijn terugkeerwaarde geretourneerd. Als validatie slaagt, wordt whenPasses uitgevoerd.

Je hoeft niet beide te gebruiken. In een serviceklasse die bij mislukking een uitzondering moet gooien:

public function processWebhook(array $payload): void
{
    Validator::make($payload, [
        'event'   => 'required|string',
        'data'    => 'required|array',
        'user_id' => 'required|integer|exists:users,id',
    ])
    ->whenFails(fn($v) => throw new InvalidPayloadException($v->errors()->toJson()))
    ->whenPasses(function ($v) {
        $this->dispatchWebhookEvent($v->validated());
    });
}

Of je kunt alleen whenFails gebruiken om het foutgeval te behandelen en de uitvoering op succes natuurlijk te laten doorgaan:

Validator::make($data, $rules)
    ->whenFails(function ($validator) {
        Log::error('Gegevensvalidatie mislukt', ['errors' => $validator->errors()->toArray()]);
        return false;
    });

// code hier wordt uitgevoerd ongeacht of validatie slaagde of mislukte (als whenFails niet teruggaf/gooide)

De API is flexibel genoeg om de meeste praktijkscenario's te dekken zonder je in een specifiek patroon te dwingen.


Alle Drie Functies Toepassen: Een Realistisch Voorbeeld

Laat me je tonen hoe deze drie functies er samen uitzien in een stuk echte code — een commando dat gebruikers importeert uit een CSV-bestand.

Voor 12.51:

<?php

namespace App\Console\Commands;

use App\Models\User;
use App\Services\BillingService;
use App\Services\AvatarService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;

class ImportUsers extends Command
{
    protected $signature = 'users:import {file}';

    public function handle(BillingService $billing, AvatarService $avatars): int
    {
        $validator = Validator::make(['file' => $this->argument('file')], [
            'file' => 'required|string',
        ]);

        if ($validator->fails()) {
            $this->error($validator->errors()->first());
            return self::FAILURE;
        }

        $rows = array_map('str_getcsv', file($this->argument('file')));

        foreach ($rows as $row) {
            [$email, $name, $plan] = $row;

            // Eager evaluatie: avatar en plan worden berekend zelfs voor bestaande gebruikers
            $user = User::firstOrCreate(
                ['email' => $email],
                [
                    'name'    => $name,
                    'avatar'  => $avatars->generate($email),      // API-aanroep, altijd uitgevoerd
                    'plan_id' => $billing->getDefaultPlan()->id,  // DB-query, altijd uitgevoerd
                ]
            );

            // Langlopende query zonder timeout
            $exists = DB::table('user_activities')
                ->where('user_id', $user->id)
                ->where('created_at', '>=', now()->subYear())
                ->count();

            $this->line("Verwerkt: {$email} (activiteiten: {$exists})");
        }

        return self::SUCCESS;
    }
}

Na 12.51:

<?php

namespace App\Console\Commands;

use App\Models\User;
use App\Services\BillingService;
use App\Services\AvatarService;
use Illuminate\Console\Command;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;

class ImportUsers extends Command
{
    protected $signature = 'users:import {file}';

    public function handle(BillingService $billing, AvatarService $avatars): int
    {
        return Validator::make(['file' => $this->argument('file')], [
            'file' => 'required|string',
        ])
        ->whenFails(function ($v) {
            $this->error($v->errors()->first());
            return self::FAILURE;
        })
        ->whenPasses(function () use ($billing, $avatars) {
            $rows = array_map('str_getcsv', file($this->argument('file')));

            foreach ($rows as $row) {
                [$email, $name] = $row;

                // Luie evaluatie: closure wordt alleen uitgevoerd als de gebruiker niet bestaat
                $user = User::firstOrCreate(
                    ['email' => $email],
                    function () use ($email, $name, $billing, $avatars) {
                        return [
                            'name'    => $name,
                            'avatar'  => $avatars->generate($email),
                            'plan_id' => $billing->getDefaultPlan()->id,
                        ];
                    }
                );

                // Timeout: bescherm tegen langlopende activiteitsqueries
                try {
                    $activityCount = DB::table('user_activities')
                        ->where('user_id', $user->id)
                        ->where('created_at', '>=', now()->subYear())
                        ->timeout(3)
                        ->count();

                    $this->line("Verwerkt: {$email} (activiteiten: {$activityCount})");

                } catch (QueryException $e) {
                    if (str_contains($e->getMessage(), 'Query execution was interrupted')) {
                        Log::warning("Activiteitsquery timed out voor gebruiker {$email}");
                        $this->line("Verwerkt: {$email} (activiteitsaantal niet beschikbaar)");
                        continue;
                    }
                    throw $e;
                }
            }

            return self::SUCCESS;
        });
    }
}

De versie na is iets langer omdat hij faalgevallen expliciet behandelt — maar de bedoeling van elke sectie is duidelijker. Validatielogica leeft in één gekoppeld blok. Aanmaaaklogica is gescheiden van opzoeklogica. Lange queries hebben grenzen.

Dit is hoe upgraden naar 12.51 er in de praktijk eigenlijk uit zou moeten zien: gerichte verbeteringen, geen herschrijvingen.


Wat Ik Echt Denk Over Deze Wijzigingen

Goed — eerlijk gezegd, want de meeste artikelen zeggen dit niet.

De luie evaluatiefunctie pakt een ontwerpkeuze aan waarvan ik denk dat die van meet af aan fout was. "Geef aanmaakaAttributen door als een array" is de intuïtieve API, en het is wat elk tutorial- en documentatievoorbeeld toont. Maar het zet ontwikkelaars stilletjes op voor een prestatieval. De aanname die de meeste mensen maken is dat PHP slim is over het niet doen van werk tenzij het nodig is. Die aanname is onjuist voor functieargumenten.

Ik ben blij dat dit is opgelost. Maar ik denk ook dat het de moeite waard is eerlijk te zijn dat dit een klasse van bug is die Laravel jaren geleden onmogelijk had kunnen maken door het eager evaluation-gedrag prominenter te documenteren in de firstOrCreate-docs. Ik heb deze exacte bug in drie verschillende klantcodebases gezien. Ik heb het waarschijnlijk in sommige van mijn eigen projecten die nog niet groot genoeg zijn geworden om de pijn te voelen.

Controleer je code.

De querytimeoutfunctie is uitstekend en ik gebruik hem onmiddellijk — maar met één voorbehoud waarop ik zou willen terugduwen: de methodenaam timeout() geeft geen indicatie dat hij MySQL-specifiek is. Als je werkt in een team waar niet iedereen de internals kent, zal iemand timeout() toevoegen aan een PostgreSQL-query in de verwachting van MySQL's gedrag en verward zijn wanneer dingen niet werken zoals verwacht. Betere documentatie en mogelijk een driverspecifieke waarschuwing in de uitzondering zouden welkom zijn.

De whenFails- en whenPasses-methoden zijn fijn. Ik vind ze goed. Maar ik heb al mensen online gezien die ze gebruiken in contexten waar ze de code minder duidelijk maken, niet meer — met name wanneer de callbacklogica complex genoeg is dat een gewone if/else leesbaarder zou zijn geweest. Deze methoden schitteren voor korte, gerichte validatieafhandeling: gooi een uitzondering bij mislukking, verzend een taak bij succes. Als je callbacks elk 15 regels zijn, vraag jezelf af of een traditioneel if-blok eigenlijk schoner zou zijn.

Laravel's incrementeel releasemodel betekent dat "minor versie"-verbeteringen zoals deze de neiging hebben over het hoofd gezien te worden. Dat is een fout. De luie evaluatieoplossing in firstOrCreate alleen al kan de responstijden met seconden verbeteren voor applicaties die onbewust met dit probleem hebben geleefd. Dat is in de praktijk niet minor — het is een significante winst vermomd in een saaie changelogvermelding.


De Prestatiewiskunde: Wat Je Daadwerkelijk Kunt Verwachten

Laat me specifiek zijn over wat deze wijzigingen wel en niet zullen doen voor je applicatiemetrieken.

Luie firstOrCreate — de cijfers:

De verbetering hangt volledig af van wat er in je aanmaaakclosure zit. Hier is een ruw kader:

Kosten aanmaakaAttributen Verzoeken die bestaande records aanraken Verwachte besparing per verzoek
Alleen statische waarden Elke ~0ms (gebruik geen closure)
Enkele bcrypt / hash 80%+ 50–200ms
Één externe API-aanroep 80%+ 200–1.500ms
Twee API-aanroepen + DB-query 80%+ 500–3.000ms

Als 80% van je verzoeken bestaande records aanraken (typisch voor ingelogde gebruikerssessies), en je aanmaakaAttributen twee API-aanroepen bevatten die gemiddeld 500ms duren, kijk je naar een verlaging van 1.000ms per verzoek op het 80e percentiel. Voor druk bezochte eindpunten is dat transformatief.

Voor het klantproject dat ik aan het begin van dit artikel noemde — de een met de vier seconden durende adminpagina's — was de gemeten verbetering na het overschakelen naar closuregebaseerde aanmaakaAttributen 3,8 seconden per verzoek op het p95-niveau. Dat is niet theoretisch. Dat is een applicatie die van traag en frustrerend naar snel ging in één commit.

Querytimeout — het operationele beeld:

Timeout maakt queries niet sneller. Het maakt de faalmode beheerst in plaats van onbegrensd. De waarde zit in je foutbudget en systeembetrouwbaarheid:

  • Voor timeout: langlopende query houdt databaseverbindingen vast → verbindingspool uitgeput → andere queries in de rij → cascadefout
  • Na timeout: langlopende query bereikt plafond → QueryException → je behandelt dit netjes → andere queries gaan normaal verder

Voor een productieapplicatie die echt verkeer verwerkt, is dat verschil de grens tussen een trage pagina en een volledige uitval.

Validator koppelbaarheid — het ontwikkelaarservaringsperspectief:

Geen runtime-prestatie-impact. Het rendement hier wordt gemeten in code-reviewtijd, onboardingsnelheid en onderhoudskosten over maanden en jaren. Teams die schonere code schrijven, maken minder fouten. Minder fouten betekent minder incidenten. Het samengestelde effect is echt, ook als het niet meetbaar is op een stopwatch.


Upgraden en Deze Functies Daadwerkelijk Gebruiken

Hier is het praktische pad vooruit.

Upgrade eerst. Als je op Laravel 12.x zit, is dit een Composer-update:

composer require laravel/framework:^12.51

Voer je testsuite uit. Dit zijn achterwaarts compatibele wijzigingen — als je tests slagen, ben je klaar.

Controleer vervolgens je firstOrCreate- en createOrFirst-aanroepen. Voer de grep uit die ik eerder noemde:

grep -rn "firstOrCreate\|createOrFirst" app/ --include="*.php"

Controleer voor elk resultaat het tweede argument. Als het echt werk doet, converteer het naar een closure en meet het voor en na op je stagingomgeving.

Vind dan je zwaarste queries — analysetoepassingseindpunten, rapportage routes, wachtrijtaken die complexe aggregaties uitvoeren. Voeg ->timeout() toe met een redelijk plafond. Pak ze in in try/catch met graceful degradation. Zet ze in op staging, test het timeoutpad expliciet (je kunt de timeout tijdelijk verlagen om het te triggeren), en bevestig dat je foutafhandeling werkt zoals verwacht.

Neem tot slot één Artisan-commando of serviceklasse die handmatige validatie gebruikt en refactor deze om whenFails/whenPasses te gebruiken. Bekijk hoe het leest. Als het schoner is, pas het patroon elders toe. Als je team de voorkeur geeft aan de traditionele if/else-structuur, is dat ook prima — gebruik het gereedschap dat je code duidelijker maakt.

Ik houd een lijst bij van functies die ik al een tijdje standaard in Laravel wilde hebben. Luie firstOrCreate stond op die lijst. Querytimeout stond op die lijst. En elke keer dat een release iets van die lijst levert — zonder iets anders te breken — is het een goede herinnering dat het framework wordt gebouwd door mensen die het daadwerkelijk in productie gebruiken.

Die vier seconden durende paginalading in het adminpaneel van de klant? Het is nu 90ms. Zelfde database. Zelfde infrastructuur. Eén wijziging in hoe aanmaakaAttributen worden doorgegeven aan een enkele methodeaanroep.

Ga je code controleren.


🤝 Laten We Samenwerken

Op zoek naar het bouwen van AI-systemen, het automatiseren van workflows, of het opschalen van je technische infrastructuur? Ik help graag.

Coffee cup

Vond u dit artikel leuk?

Uw steun helpt mij meer diepgaande technische content, open-source tools en gratis bronnen voor de ontwikkelaarsgemeenschap te maken.

Gerelateerde onderwerpen

Engr Mejba Ahmed

Engr Mejba Ahmed

Engr. Mejba Ahmed builds AI-powered applications and secure cloud systems for businesses worldwide. With 8+ years shipping production software in Laravel, Python, and AWS, he's helped companies automate workflows, reduce infrastructure costs, and scale without security headaches. He writes about practical AI integration, cloud architecture, and developer productivity.

Gerelateerde artikelen

Alles bekijken

Comments

Leave a Comment

Comments are moderated before appearing.

Learning Resources

Expand Your Knowledge

Accelerate your growth with structured courses, verified certificates, interactive flashcards, and production-ready AI agent skills.

Sample Certificate of Completion

Sample certificate — complete any course to earn yours

Engr Mejba Ahmed

Engr Mejba Ahmed

AI assistant · trained on my work

👋

Hey there!

Quick Actions

WhatsApp Direct line to me

Chat on WhatsApp

+880 1723 741224 · Replies within the hour on working days

Popular Questions

Engr Mejba Ahmed is connected
Engr Mejba Ahmed is typing...
Engr Mejba Ahmed avatar

✉ Want me to follow up? Drop your email

Engr Mejba Ahmed avatar

📞 Connect Directly

Choose how you'd like to reach me

WhatsApp

+880 1723 741224

Email

mejba.13@gmail.com

✓ Details sent! I'll get back to you shortly.

Powered by OpenAI

335+

Blog Posts

25

AI Courses

63

Projects

Services & Expertise

Pricing & Process

Learning & Resources

Connect & Support