> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Upgraden van Laravel 8 naar 9

> Het stappenplan voor de upgrade van Laravel 8.x naar 9.x en uitleg van de belangrijkste wijzigingen

## Inleiding

Laravel 9 is uitgebracht op 8 februari 2022. Deze gids zet de stappen voor de upgrade van Laravel 8.x naar 9.x op een rij, samen met de wijzigingen met de grootste impact.

<Info>
  De geschatte tijd voor de upgrade is **ongeveer 30 minuten**. Afhankelijk van je gebruik van e-mailverzending, filestorage, custom casts en overrides van corekwlassen van het framework kan het werk echter toenemen.
</Info>

### Automatisch upgraden met Laravel Shift

Je kunt de upgrade ook automatiseren met [Laravel Shift](https://laravelshift.com/). Shift helpt bij het bijwerken van `composer.json` en de configuratiebestanden, en is daarmee een handig startpunt voor het controleren van de verschillen.

***

## Wijzigingen per impactniveau

### Impact: hoog

* Dependencies bijwerken
* Migratie naar Flysystem 3.x
* Migratie naar Symfony Mailer

### Impact: middel

* De methodes `firstOrNew` / `firstOrCreate` / `updateOrCreate` van `BelongsToMany`
* Custom casts en het gedrag met `null`
* Standaardtimeout van de HTTP-client
* Toevoeging van PHP-returntypes
* Hernoemde `schema`-instelling voor Postgres
* Vervallen van de methode `assertDeleted`
* Verplaatsing van de `lang`-directory
* Wijziging van de wachtwoordregel
* Wijziging van de methodes `when` / `unless`
* Behandeling van niet-gevalideerde arraykeys bij validatie

***

## Upgradestappen

### Dependencies bijwerken

**Impact: hoog**

Laravel 9 vereist **PHP 8.0.2 of hoger**. Loop eerst de dependencies in `composer.json` na.

```json theme={null}
{
  "require": {
    "php": "^8.0.2",
    "laravel/framework": "^9.0",
    "spatie/laravel-ignition": "^1.0"
  },
  "require-dev": {
    "nunomaduro/collision": "^6.1"
  }
}
```

Daarnaast zijn voor de betreffende applicaties de volgende updates nodig.

* Verwijder `facade/ignition` en vervang het door `spatie/laravel-ignition:^1.0`
* Gebruik je `pusher/pusher-php-server`, werk die dan bij naar `^5.0`
* Controleer of de third-party packages die je gebruikt een Laravel 9-compatibele versie hebben
* Gebruik je het Vonage-notificatiekanaal, bekijk dan ook de aparte upgradegids

Installeer daarna de dependencies.

```shell theme={null}
composer update
```

***

## PHP-versievereiste

**Impact: hoog**

Laravel 9 vereist PHP 8.0.2 of hoger. Zorg dat de PHP-versie in je CI, lokale ontwikkelomgeving en productieomgeving overeenkomt voordat je de upgrade doorvoert.

***

### Migratie naar Symfony Mailer

**Impact: hoog**

Een van de grote wijzigingen in Laravel 9 is de migratie van SwiftMailer, waarvan het onderhoud in december 2021 eindigde, naar Symfony Mailer. Applicaties die alleen het gebruikelijke `Mail::to()->send()` gebruiken worden weinig geraakt, maar raak je de low-level API van SwiftMailer direct aan, dan moet je dit nalopen.

#### Driver-dependencies

```shell theme={null}
# Alleen toevoegen als je Mailgun gebruikt
composer require symfony/mailgun-mailer symfony/http-client

# Gebruik je Postmark, verwijder dan de SwiftMailer-package en vervang hem
composer remove wildbit/swiftmailer-postmark
composer require symfony/postmark-mailer symfony/http-client
```

#### Van `withSwiftMessage` naar `withSymfonyMessage`

```php theme={null}
// Laravel 8.x: gebaseerd op SwiftMailer
$this->withSwiftMessage(function ($message) {
    $message->getHeaders()->addTextHeader('Custom-Header', 'Header Value');
});

// Laravel 9.x: gebaseerd op Symfony Mailer
use Symfony\Component\Mime\Email;

$this->withSymfonyMessage(function (Email $message) {
    $message->getHeaders()->addTextHeader('Custom-Header', 'Header Value');
});
```

De methodes `send`, `html`, `raw` en `plain` van `Illuminate\Mail\Mailer` geven nu geen `void` maar een `Illuminate\Mail\SentMessage` terug. Ook bevat de property `message` van het `MessageSent`-event nu een `Symfony\Component\Mime\Email` in plaats van een `Swift_Message`.

#### De SMTP-configuratie nalopen

In Symfony Mailer is de SMTP-optie `stream` vervallen; de ondersteunde instellingen verhuizen naar het topniveau.

```php theme={null}
return [
    'mailers' => [
        'smtp' => [
            // Configuratie in Laravel 8.x
            'stream' => [
                'ssl' => [
                    'verify_peer' => false,
                ],
            ],

            // Configuratie in Laravel 9.x
            'verify_peer' => false,
        ],
    ],
];
```

Het expliciet instellen van `auth_mode` is ook niet meer nodig. Het is veiliger om e-mailadressen vóór het verzenden te valideren, in plaats van ongeldige adressen na verzending op te ruimen.

***

### Migratie naar Flysystem 3.x

**Impact: hoog**

Laravel 9 heeft de interne implementatie van de `Storage`-facade bijgewerkt van Flysystem 1.x naar 3.x. De bestandsbewerkingsmethodes zijn zoveel mogelijk compatibel gebleven, maar er zijn verschillen rond excepties, returnwaarden en adapterregistratie.

#### Extra drivers installeren

```shell theme={null}
# Amazon S3-driver
composer require -W league/flysystem-aws-s3-v3 "^3.0"

# FTP-driver
composer require league/flysystem-ftp "^3.0"

# SFTP-driver
composer require league/flysystem-sftp-v3 "^3.0"
```

#### De belangrijkste gedragswijzigingen van `Storage`

* `put` / `write` / `writeStream` overschrijven bestaande bestanden nu standaard
* Bij een mislukte schrijfactie wordt `false` teruggegeven in plaats van een exceptie
* Het lezen van een niet-bestaand bestand geeft `null` terug in plaats van een exceptie
* `delete` op een niet-bestaand bestand geeft `true` terug
* De cached adapter is verwijderd, dus je kunt de `cache`-key uit je `disk`-configuratie verwijderen

Wil je zoals voorheen een exceptie bij een mislukte schrijfactie, stel dan de optie `throw` in.

```php theme={null}
return [
    'disks' => [
        'public' => [
            'driver' => 'local',
            // Alleen inschakelen als je bij schrijffouten een exceptie wilt
            'throw' => true,
        ],
    ],
];
```

Registreer je een eigen filesystem-driver, pas de implementatie dan zo aan dat de callback van `Storage::extend()` direct een `Illuminate\Filesystem\FilesystemAdapter` teruggeeft.

***

### `firstOrNew` / `firstOrCreate` / `updateOrCreate` van `BelongsToMany`

**Impact: middel**

In Laravel 8 werd de attributenarray die je als eerste argument aan deze methodes doorgaf vergeleken met de tussentabel. In Laravel 9 wordt vergeleken met de tabel van het gerelateerde model.

```php theme={null}
// Zoekt en werkt name bij op de tabel van het gerelateerde model
$user->roles()->updateOrCreate([
    'name' => 'Administrator',
]);
```

Daarnaast accepteert `firstOrCreate` nu een tweede argument `$values`, waardoor het gedrag gelijkgetrokken is met de andere relaties.

```php theme={null}
// created_by alleen mergen bij het aanmaken
$user->roles()->firstOrCreate([
    'name' => 'Administrator',
], [
    'created_by' => $user->id,
]);
```

***

### Custom casts en `null`

**Impact: middel**

In Laravel 9 wordt de `set`-methode van een custom cast ook aangeroepen wanneer je `null` toewijst aan het gecaste attribuut. Casts die geen rekening houden met `null` kunnen na de upgrade excepties gooien.

```php theme={null}
public function set($model, $key, $value, $attributes)
{
    // In Laravel 9 kan null worden doorgegeven
    if ($value === null) {
        return [
            'address_line_one' => null,
            'address_line_two' => null,
        ];
    }

    if (! $value instanceof AddressModel) {
        throw new InvalidArgumentException('Geef een AddressModel door.');
    }

    return [
        'address_line_one' => $value->lineOne,
        'address_line_two' => $value->lineTwo,
    ];
}
```

***

### Standaardtimeout van de HTTP-client

**Impact: middel**

De standaardtimeout van de HTTP-client is nu 30 seconden. Voorheen kon hij onbeperkt blijven wachten.

```php theme={null}
use Illuminate\Support\Facades\Http;

// Verleng de timeout alleen voor langdurige API-aanroepen
$response = Http::timeout(120)->get('https://example.com/api/status');
```

***

### Toevoeging van PHP-returntypes

**Impact: middel**

In Laravel 9 zijn, in lijn met de vereisten van PHP zelf en Symfony, returntypes toegevoegd aan diverse coreklassen. Extend je coreklassen van Laravel en override je methodes zoals `offsetGet`, `offsetSet`, `jsonSerialize`, `open` of `read`, voeg dan dezelfde returntypes toe aan je eigen implementaties.

```php theme={null}
// Bij het overriden van coreklassen het returntype gelijk houden
public function offsetGet($key): mixed
{
    return parent::offsetGet($key);
}
```

***

### Hernoemde `schema`-instelling voor Postgres

**Impact: middel**

Stel je in een Postgres-verbinding een zoekpad in, wijzig dan de keynaam in `config/database.php` van `schema` naar `search_path`.

```php theme={null}
return [
    'pgsql' => [
        // Laravel 8.x
        'schema' => 'public',

        // Laravel 9.x
        'search_path' => 'public',
    ],
];
```

***

### Van `assertDeleted` naar `assertModelMissing`

**Impact: middel**

Vervang `assertDeleted`, dat je gebruikte om te controleren of een model is verwijderd, door `assertModelMissing`.

```php theme={null}
// Voorheen
$this->assertDeleted($user);

// Nu
$this->assertModelMissing($user);
```

***

### Verplaatsing van de `lang`-directory

**Impact: middel**

In nieuwe Laravel 9-applicaties staan de taalbestanden niet meer in `resources/lang`, maar in de directory `lang` in de projectroot. Laat je je bestaande app gewoon draaien, dan is de impact klein, maar als je richting de nieuwe skeleton wilt of vanuit een package vertaalbestanden publiceert, loop dit dan na.

```php theme={null}
// Bepaal de publicatielocatie met langPath() in plaats van een vast pad
$this->publishes([
    __DIR__.'/../lang' => app()->langPath('vendor/package-name'),
]);
```

***

### Wijziging van de wachtwoordregel

**Impact: middel**

De regel `password`, die controleert of een waarde overeenkomt met het wachtwoord van de ingelogde gebruiker, is hernoemd naar `current_password`.

```php theme={null}
// Voorheen
'password' => ['required', 'password'],

// Nu
'password' => ['required', 'current_password'],
```

***

### Wijziging van de methodes `when` / `unless`

**Impact: middel**

In Laravel 8 werd een closure die je aan `when` of `unless` doorgaf zelf als truthy geëvalueerd, waardoor de conditionele tak onbedoeld kon worden uitgevoerd. In Laravel 9 wordt de closure uitgevoerd en wordt de returnwaarde als voorwaarde gebruikt.

```php theme={null}
$collection->when(function ($collection) {
    // De waarde die je hier teruggeeft wordt als voorwaarde geëvalueerd
    return false;
}, function ($collection) {
    // Omdat false werd teruggegeven, wordt dit niet uitgevoerd
    $collection->merge([1, 2, 3]);
});
```

***

### Behandeling van niet-gevalideerde arraykeys

**Impact: middel**

In Laravel 9 worden niet-gevalideerde arraykeys altijd uitgesloten van de array die `validated()` teruggeeft. Wil je het compatibele gedrag van Laravel 8 behouden, roep dan expliciet `includeUnvalidatedArrayKeys()` aan.

```php theme={null}
use Illuminate\Support\Facades\Validator;

public function boot()
{
    // Alleen inschakelen als je hetzelfde gedrag als Laravel 8 wilt
    Validator::includeUnvalidatedArrayKeys();
}
```

***

## Samenvatting

Bij de upgrade van Laravel 8 naar 9 draait het vooral om de update naar PHP 8.0.2, de migratie naar Symfony Mailer en de overstap op Flysystem 3.x. Door e-mailverzending, storage, custom casts en testhelpers vooraf te controleren, verminder je problemen na de upgrade.

| Wijziging                                   | Impact | Actie                                                                     |
| ------------------------------------------- | ------ | ------------------------------------------------------------------------- |
| PHP 8.0.2 / `laravel/framework:^9.0`        | Hoog   | Werk `composer.json` en je runtime-omgeving bij                           |
| Migratie naar Symfony Mailer                | Hoog   | Controleer de SwiftMailer-API's en de mailconfiguratie                    |
| Flysystem 3.x                               | Hoog   | Controleer returnwaarden, excepties en driver-dependencies van `Storage`  |
| De upsert-methodes van `BelongsToMany`      | Middel | Controleer dat er nu op de tabel van het gerelateerde model wordt gezocht |
| Custom casts en `null`                      | Middel | Zorg dat `set()` niet breekt bij `null`                                   |
| 30-secondentimeout van de HTTP-client       | Middel | Geef alleen waar nodig expliciet `timeout()` op                           |
| `assertDeleted` vervallen                   | Middel | Vervang door `assertModelMissing()`                                       |
| Verplaatsing van de `lang`-directory        | Middel | Wijzig vaste padverwijzingen naar `app()->langPath()`                     |
| Gewijzigde `password`-regel                 | Middel | Werk bij naar `current_password`                                          |
| Uitsluiting van niet-gevalideerde arraykeys | Middel | Gebruik alleen indien nodig `includeUnvalidatedArrayKeys()`               |

***

## Referenties

* [Officiële upgradegids (Engels)](https://laravel.com/docs/9.x/upgrade)
* [laravel/docs 9.x `upgrade.md`](https://github.com/laravel/docs/blob/9.x/upgrade.md)
* [Diff van de laravel/laravel-repository (8.x → 9.x)](https://github.com/laravel/laravel/compare/8.x...9.x)
* [Laravel Shift](https://laravelshift.com) — communitydienst die upgrades automatiseert
* [Symfony Mailer-documentatie](https://symfony.com/doc/6.0/mailer.html)
* [Flysystem 3-documentatie](https://flysystem.thephpleague.com/v3/docs/)


## Related topics

- [Upgraden van Laravel 9 naar 10](/nl/blog/upgrade-9-to-10.md)
- [Upgraden van Laravel 10 naar 11](/nl/blog/upgrade-10-to-11.md)
- [Upgradegids van Laravel 12 naar 13](/nl/blog/upgrade-12-to-13.md)
- [Upgradegids van Laravel 11 naar 12](/nl/blog/upgrade-11-to-12.md)
- [Migratiegids van laravel/ui naar Fortify](/nl/blog/ui-to-fortify.md)
