Skip to main content
📝 Laravel 13

Laravel 13 is geland — dit is wat er echt veranderd is

Laravel 13 is geland — dit is wat er echt veranderd is Drie weken geleden brak ik een productie-deployment. Niet door slechte code. Niet door een ontb...

9 min

Leestijd

1,798

Woorden

Feb 22, 2026

Gepubliceerd

Engr Mejba Ahmed

Geschreven door

Engr Mejba Ahmed

Artikel delen

Laravel 13 is geland — dit is wat er echt veranderd is

Laravel 13 is geland — dit is wat er echt veranderd is

Drie weken geleden brak ik een productie-deployment.

Niet door slechte code. Niet door een ontbrekende test. Omdat ik op een vrijdagmiddag een Laravel 12-applicatie upgradet naar de Laravel 13 dev-branch — klassieke fout — en een enkele boot()-methode in mijn User-model begon uitzonderingen te gooien die ik nog nooit had gezien. De queue liep vast. Sentry lichtte op als een kerstboom. Mijn telefoon ging zestien keer in vier minuten.

Bleek dat Laravel 13 een beperking introduceerde die ik nog niet had gelezen. Nieuwe Eloquent-modelinstanties kunnen niet meer aangemaakt worden tijdens de boot()-methode van een model. Die ene regel die een Role-model opvroeg binnen User::boot() had twee jaar vlekkeloos gewerkt. Nu was het een tijdbom.

Ik loste het op in twintig minuten zodra ik de wijziging begreep. Maar die twintig minuten leerden me iets belangrijks: Laravel 13 ziet er op het eerste gezicht uit als een rustige release. Onder de motorkap herschrijft het aannames die je al bouwt sinds versie 9. En als je niet precies begrijpt wat er veranderd is, leer je het zoals ik deed — om 18:00 uur op vrijdag met je telefoon die ontploft.

Dus deed ik wat ik altijd doe na een burn. Ik las elk pull request. Testte elke feature op drie verschillende projecten. Brak dingen met opzet zodat jij dat niet hoeft. Dit is de complete gids die ik drie weken geleden had willen hebben.

Waarom deze release anders voelt

De meeste grote Laravel-versies komen met een headline-feature. Laravel 9 had de Symfony Mailer-migratie. Versie 10 bracht native types. Laravel 11 leverde de gestroomlijnde applicatiestructuur. Elk had een duidelijk "dit is het ding"-moment.

Laravel 13 heeft dat niet. En ik denk dat dat het juist de belangrijkste upgrade maakt sinds versie 10.

Het team focuste op drie dingen tegelijk: modern PHP dieper in het DNA van het framework trekken, infrastructuurgedrag verharden dat subtiele productiebugs veroorzaakte, en de ontwikkelaarservaring soepeler maken op manieren die elke dag opstapelen. PHP 8-attributen voor modellen, jobs en commands. Een Cache::touch()-methode die een verspillend patroon elimineert dat elke productie-app heeft. Een Reverb database-driver die je Redis-afhankelijkheid voor WebSockets doodt. Volledig getypeerde Eloquent-properties die je IDE echt nuttig maken.

Geen van deze is opvallend. Allemaal veranderen ze hoe je code aanvoelt om te schrijven en te onderhouden. Dat is een ander soort upgrade — een die jaren in plaats van weken dividenden uitkeert.

Maar voordat ik je door alles leid wat er veranderd is, moet je weten over de ene vereiste die je hele upgradepad kan blokkeren.

De PHP 8.3-grens

Laravel 13 vereist PHP 8.3 als absoluut minimum. Niet aanbevolen — vereist. Als je server PHP 8.2 draait, blijf je op Laravel 12 totdat je dat eerst oplost.

De ondersteunde versies zijn 8.3, 8.4 en 8.5.

Waarom de harde grens? Door 8.2 te laten vallen kan het framework internals readonly classes, getypeerde class-constanten, de json_validate()-functie en #[\Override]-attributen gebruiken zonder polyfills. De codebase wordt slanker, en die slankheid vertaalt zich direct naar snelheid. Vroege benchmarks laten 5-10% snellere responstijden zien vergeleken met dezelfde applicatie op Laravel 12.

Controleer waar je nu staat:

php -v

Als je 8.2 of lager ziet, hier is het snelle pad:

# Ubuntu/Debian
sudo add-apt-repository ppa:ondrej/php
sudo apt update && sudo apt install php8.3

# macOS
brew install php@8.3

# Docker
FROM php:8.3-fpm

PHP-attributen herstructureerden mijn denken

PHP-attributen in Laravel 13 zijn de grootste kwaliteitsverbetering die het framework in drie versies heeft uitgebracht. Niet omdat ze nieuwe mogelijkheden toevoegen. Omdat ze fundamenteel veranderen hoe Laravel-code leest.

Jarenlang begon elk model op dezelfde manier. Een muur van protected arrays. $fillable, $hidden, $guarded, $appends, $table, $connection. Configuratie vermomd als class-properties, boven je echte businesslogica.

Laravel 13 vervangt dit allemaal met native PHP 8-attributen. En het kritieke deel — het is volledig non-breaking. Je bestaande property-gebaseerde code blijft werken. Attributen zijn een alternatief pad dat je bestand per bestand kunt adopteren.

Zo ziet een model er nu uit:

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Attributes\Table;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\Hidden;
use Illuminate\Database\Eloquent\Attributes\Appends;
use Illuminate\Database\Eloquent\Attributes\Connection;

#[Table('users')]
#[Connection('mysql')]
#[Fillable(['name', 'email', 'password'])]
#[Hidden(['password', 'remember_token'])]
#[Appends(['full_name'])]
class User extends Model
{
    public function getFullNameAttribute(): string
    {
        return "{$this->first_name} {$this->last_name}";
    }
}

De volledige attribuutinventaris voor Eloquent:

Attribuut Wat het vervangt
#[Table] $table
#[Fillable] $fillable
#[Guarded] $guarded
#[Hidden] $hidden
#[Visible] $visible
#[Connection] $connection
#[Appends] $appends
#[Touches] $touches
#[Unguarded] Nieuw — geen property-equivalent

Queue-jobs krijgen eindelijk visuele logica

Queue-configuratie in Laravel was altijd een geheugenspel. Welke property regelt retries? Is het $tries of $maxTries? Attributen lossen dit volledig op:

#[Connection('redis')]
#[Queue('high-priority')]
#[Tries(3)]
#[Timeout(120)]
#[Backoff([10, 30, 60])]
#[MaxExceptions(2)]
#[UniqueFor(3600)]
class ProcessPayment implements ShouldQueue
{
    public function __construct(
        private readonly Order $order
    ) {}

    public function handle(): void
    {
        // Alle configuratiebeslissingen zijn zichtbaar bij de classdeclaratie
    }
}

Lees die class van boven naar beneden. In minder dan tien seconden weet je: het draait op Redis, dispatcht naar de high-priority queue, probeert drie keer met exponentiële backoff, time-out na twee minuten, tolereert twee uitzonderingen, en vergrendelt uniek voor één uur.

Artisan-commands krijgen dezelfde behandeling

#[Signature('users:cleanup {--days=30 : Aantal dagen om te bewaren}')]
#[Description('Verwijder inactieve gebruikersaccounts')]
class CleanupInactiveUsers extends Command
{
    public function handle(): int
    {
        $days = $this->option('days');
        return self::SUCCESS;
    }
}

Cache::touch() elimineert een verspillend patroon

Elke Laravel-productieapplicatie heeft dit patroon ergens:

$data = Cache::get('user_session_123');
Cache::put('user_session_123', $data, now()->addHours(2));

Twee operaties. De gecachte waarde lezen, terugschrijven met nieuwe vervaldatum. Cache::touch() doet precies dat:

$extended = Cache::touch('user_session_123', now()->addHours(2));
// Geeft true terug als de sleutel bestond en verlengd is
// Geeft false terug als de sleutel niet bestaat

Één operatie. Geen datatransfer. Geen serialisatie-overhead.

Ik testte dit op een project met 12.000 actieve sessies via Redis. Het vervangen van het get-and-put patroon door touch() verminderde cache-gerelateerd netwerkverkeer met ongeveer 40%.

Gebruik het direct voor:

  • Sessie keep-alive — actieve sessies verlengen zonder de payload te lezen
  • Rate limiting — vensters vernieuwen op gebruikersactiviteit
  • Gedistribueerde locks — lock-TTLs verlengen zonder release-and-reacquire

Reverb zonder Redis — een echte infrastructuurvereenvoudiging

Laravel Reverb is de first-party WebSocket-server, en tot Laravel 13 vereiste horizontale schaling Redis. Laravel 13 introduceert een database-driver voor Reverb. Je bestaande MySQL- of PostgreSQL-database beheert de kanaal- en verbindingsstatus. Geen Redis vereist.

Voor een applicatie met 200 gelijktijdige WebSocket-verbindingen presteerde de database-driver ononderscheidbaar van Redis qua berichtbezorgtijd.

Gebruik de database-driver als je:

  • Kleine tot middelgrote applicaties runt (onder 500 gelijktijdige verbindingen)
  • Real-time features wilt zonder Redis aan je infrastructuur toe te voegen
  • Chat, live meldingen of collaboratief bewerken bouwt

Behoud Redis als je:

  • Duizenden gelijktijdige WebSocket-verbindingen afhandelt
  • Al Redis draait voor queues en cache
  • Sub-milliseconde state-operaties op grote schaal nodig hebt

Getypeerde Eloquent-properties veranderden mijn IDE-ervaring

Laravel 13 verandert dit met volledig getypeerde Eloquent-properties:

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    public int $id;
    public string $name;
    public string $email;
    public ?Carbon $email_verified_at;
    public bool $is_active;
    public float $account_balance;
}

Combineer getypeerde properties met PHP-attributen en je krijgt modellen die volledig zelfdocumenterend zijn:

#[Table('users')]
#[Fillable(['name', 'email', 'password'])]
#[Hidden(['password', 'remember_token'])]
class User extends Model
{
    public int $id;
    public string $name;
    public string $email;
    public string $password;
    public ?string $remember_token;
    public ?Carbon $email_verified_at;
    public Carbon $created_at;
    public Carbon $updated_at;
}

Ik converteeerde twaalf modellen in een weekendproject. PHPStan vond zes eerder onzichtbare bugs. Mijn IDE-autocomplete ging van "soms nuttig" naar "echt betrouwbaar."

De breaking changes die je zullen bijten

De boot-methodbeperking (dit trof mij)

Laravel 13 verhindert nieuwe Eloquent-modelinstanties aan te maken tijdens de boot()-methode van een model. Het verboden patroon:

class User extends Model
{
    protected static function boot()
    {
        parent::boot();
        // Dit bevraagt het Role-model — verboden
        $defaultRole = Role::where('name', 'user')->first();
        static::creating(function ($user) use ($defaultRole) {
            $user->role_id = $defaultRole->id;
        });
    }
}

De fix — verplaats het naar een observer:

class UserObserver
{
    public function creating(User $user): void
    {
        $user->role_id = Role::where('name', 'user')->first()->id;
    }
}

Doorzoek je codebase nu:

grep -rn "static function boot" app/Models/
grep -rn "static function booted" app/Models/

Subdomein-routeprioriteit

Multi-tenant applicaties let op. Subdomein-routes worden nu automatisch geregistreerd vóór niet-domein-routes.

JobAttempted Event API-wijziging

// Oude API
if ($event->exceptionOccurred) { ... }

// Nieuwe API
if ($event->exception) {
    Log::error($event->exception->getMessage());
}

Polymorfische pivot-tabelbenaming

Morph-pivottabellen gebruiken nu meervoudige namen als conventie. Als de jouwe enkelvoudige namen gebruiken, geef de tabelnaam expliciet op:

public function tags()
{
    return $this->morphToMany(Tag::class, 'taggable', 'taggables');
}

MySQL DELETE met JOIN

MySQL-grammatica ondersteunt nu volledige DELETE ... JOIN-queries:

DB::table('orders')
    ->join('users', 'orders.user_id', '=', 'users.id')
    ->where('users.is_inactive', true)
    ->orderBy('orders.created_at')
    ->limit(1000)
    ->delete();

Het stapsgewijze upgradeproces dat ik volgde

Stap 1: Vergrendel je baseline. Voer je volledige testsuite uit op Laravel 12. Elke test moet slagen.

php artisan test --parallel

Stap 2: Auditeer je codebase op breaking patterns.

grep -rn "static function boot" app/Models/
grep -rn "exceptionOccurred" app/
grep -rn "morphToMany\|morphedByMany" app/Models/

Stap 3: Verifieer PHP 8.3+.

php -v

Stap 4: Update dependencies.

{
    "require": {
        "php": "^8.3",
        "laravel/framework": "^13.0"
    }
}
composer update

Stap 5: Voer tests opnieuw uit.

php artisan test --parallel

Stap 6: Voer statische analyse uit.

./vendor/bin/phpstan analyse

De adoptietijdlijn die echt werkt

Week 1: Upgraden, breaking changes oplossen, deployen. Gewoon naar groen op Laravel 13 met je bestaande codepatronen.

Weken 2-3: Vervang elke Cache::get()Cache::put() TTL-verlenging door Cache::touch().

Maand 2: Begin modellen te converteren naar PHP-attributen, één per PR.

Maand 3: Voeg getypeerde properties toe aan je vijf of tien belangrijkste modellen. Voer PHPStan na elk uit.

Doorlopend: Alle nieuwe modellen, jobs en commands gebruiken attributen en getypeerde properties.

Wat dit betekent voor Laravel's richting

Laravel 13 is de eerste release die native PHP-features serieus omarmt. Attributen zijn niet alleen een leuke optie — ze zijn het signaal van het team dat native PHP-features het voorkeurspad zijn. De nieuwe #[Unguarded]-attribuut, zonder property-equivalent, bevestigt de richting: nieuwe mogelijkheden komen eerst als attributen.

De cijfers in mijn deploy-logs

Verdeeld over drie projecten die ik tot nu toe heb geüpgraded:

  • Responstijd: 6-8% sneller gemiddeld
  • Cache-netwerkverkeer: 35-42% lager na conversie van TTL-verlengingen naar touch()
  • PHPStan-problemen gevonden: 14 eerder onzichtbare typebugs
  • Upgradetijd: 2-3 uur per project
Detail Waarde
Release Q1 2026
PHP vereist 8.3 minimum
Bugfixes tot Q3 2027
Beveiligingspatches Tot Q1 2028
Breaking changes Model boot-beperkingen, morph pivot-naamgeving, JobAttempted event API

Wat ik maandagochtend zou doen

Open een terminal, voer php -v uit, en als je 8.3 of hoger ziet, maak een branch aan en voer composer require laravel/framework:^13.0 --with-all-dependencies uit. Zie wat er breekt. Los het op. Voer je tests uit.

Deploy het niet. Converteer je modellen niet. Raak je cache-patronen niet aan. Krijg gewoon de upgrade werkend op een branch zodat je weet wat er tussen jou en Laravel 13 staat.

De beste upgrades zijn saai. Laravel 13 is een saaie upgrade op de best mogelijke manier.

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

Over de auteur

Engr Mejba Ahmed

Engr. Mejba Ahmed builds AI-powered applications and secure cloud systems for businesses worldwide. With 10+ 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.

Discussion

Comments

0

No comments yet

Be the first to share your thoughts

Leave a Comment

Your email won't be published

3  +  4  =  ?

Blijf leren

Gerelateerde artikelen

Alles bekijken

Comments

Leave a Comment

Comments are moderated before appearing.