Zum Hauptinhalt springen

Einleitung

Laravel 9 wurde am 8. Februar 2022 veröffentlicht. Dieser Leitfaden fasst die Upgrade-Schritte von Laravel 8.x auf 9.x sowie die einflussreichsten Änderungen zusammen.
Der geschätzte Zeitaufwand für das Upgrade beträgt etwa 30 Minuten. Je nach Nutzung von Mailversand, Dateispeicher, benutzerdefinierten Casts und Overrides von Core-Klassen kann der Aufwand jedoch steigen.

Automatisches Upgrade mit Laravel Shift

Sie können das Upgrade auch mit Laravel Shift automatisieren. Shift unterstützt Sie beim Aktualisieren von composer.json und Konfigurationsdateien und ist ein guter Ausgangspunkt für den Diff-Vergleich.

Änderungen nach Auswirkung

Auswirkung: Hoch

  • Aktualisierung der Abhängigkeiten
  • Umstieg auf Flysystem 3.x
  • Umstieg auf Symfony Mailer

Auswirkung: Mittel

  • Methoden firstOrNew / firstOrCreate / updateOrCreate bei BelongsToMany
  • Verhalten von Custom Casts bei null
  • Standard-Timeout des HTTP-Clients
  • Ergänzung von PHP-Rückgabetypen
  • Umbenennung des Postgres-schema-Settings
  • Entfall der assertDeleted-Methode
  • Verlagerung des lang-Verzeichnisses
  • Änderung der Password-Rule
  • Änderung der Methoden when / unless
  • Umgang mit nicht validierten Array-Keys in der Validierung

Upgrade-Schritte

Abhängigkeiten aktualisieren

Auswirkung: Hoch Laravel 9 erfordert PHP 8.0.2 oder höher. Prüfen Sie zuerst die Abhängigkeiten in composer.json.
{
  "require": {
    "php": "^8.0.2",
    "laravel/framework": "^9.0",
    "spatie/laravel-ignition": "^1.0"
  },
  "require-dev": {
    "nunomaduro/collision": "^6.1"
  }
}
Für einige Anwendungen sind zusätzlich diese Updates nötig:
  • facade/ignition entfernen und durch spatie/laravel-ignition:^1.0 ersetzen
  • Bei Nutzung von pusher/pusher-php-server auf ^5.0 aktualisieren
  • Prüfen, ob die verwendeten Dritt-Pakete eine Laravel-9-kompatible Version haben
  • Bei Nutzung des Vonage-Notification-Channels dessen separaten Upgrade-Guide beachten
Installieren Sie anschließend die Abhängigkeiten:
composer update

PHP-Version

Auswirkung: Hoch Laravel 9 benötigt PHP 8.0.2 oder höher. Vereinheitlichen Sie die PHP-Version in CI, lokaler und produktiver Umgebung, bevor Sie das Upgrade fortsetzen.

Umstieg auf Symfony Mailer

Auswirkung: Hoch Eine der zentralen Änderungen in Laravel 9 ist der Wechsel von SwiftMailer (Maintenance-Ende Dezember 2021) zu Symfony Mailer. Für Anwendungen, die nur Mail::to()->send() verwenden, sind die Auswirkungen gering. Wer die Low-Level-API von SwiftMailer direkt nutzt, muss prüfen.

Treiber-Abhängigkeiten

# Nur bei Nutzung von Mailgun ergänzen
composer require symfony/mailgun-mailer symfony/http-client

# Bei Nutzung von Postmark das SwiftMailer-Paket entfernen und ersetzen
composer remove wildbit/swiftmailer-postmark
composer require symfony/postmark-mailer symfony/http-client

Von withSwiftMessage zu withSymfonyMessage

// Laravel 8.x: SwiftMailer-basiert
$this->withSwiftMessage(function ($message) {
    $message->getHeaders()->addTextHeader('Custom-Header', 'Header Value');
});

// Laravel 9.x: Symfony-Mailer-basiert
use Symfony\Component\Mime\Email;

$this->withSymfonyMessage(function (Email $message) {
    $message->getHeaders()->addTextHeader('Custom-Header', 'Header Value');
});
send, html, raw und plain von Illuminate\Mail\Mailer liefern nun Illuminate\Mail\SentMessage statt void. Auch die Eigenschaft message im Event MessageSent enthält jetzt Symfony\Component\Mime\Email statt Swift_Message.

SMTP-Konfiguration überarbeiten

Bei Symfony Mailer entfällt die stream-Option in der SMTP-Konfiguration; die unterstützten Einstellungen wandern auf die oberste Ebene.
return [
    'mailers' => [
        'smtp' => [
            // Konfiguration in Laravel 8.x
            'stream' => [
                'ssl' => [
                    'verify_peer' => false,
                ],
            ],

            // Konfiguration in Laravel 9.x
            'verify_peer' => false,
        ],
    ],
];
Die explizite auth_mode-Konfiguration ist ebenfalls nicht mehr nötig. Statt ungültige Mails im Nachhinein zurückzuholen, ist es sicherer, sie bereits vor dem Versand zu validieren.

Umstieg auf Flysystem 3.x

Auswirkung: Hoch Laravel 9 hat die interne Implementierung der Storage-Facade von Flysystem 1.x auf 3.x aktualisiert. Die Dateimethoden bleiben so kompatibel wie möglich; es gibt jedoch Unterschiede bei Exceptions, Rückgabewerten und Adapter-Registrierung.

Zusätzliche Treiber installieren

# Amazon-S3-Treiber
composer require -W league/flysystem-aws-s3-v3 "^3.0"

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

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

Wichtige Verhaltensänderungen von Storage

  • put / write / writeStream überschreiben bestehende Dateien standardmäßig
  • Bei Schreibfehlern wird false statt einer Exception zurückgegeben
  • Beim Lesen nicht vorhandener Dateien wird null statt einer Exception zurückgegeben
  • delete auf einer nicht existierenden Datei gibt true zurück
  • Der Cached Adapter wurde entfernt; der Key cache in disk-Konfigurationen kann gelöscht werden
Wenn Sie wie bisher bei Schreibfehlern Exceptions möchten, setzen Sie die Option throw.
return [
    'disks' => [
        'public' => [
            'driver' => 'local',
            // Nur aktivieren, wenn Schreibfehler eine Exception auslösen sollen
            'throw' => true,
        ],
    ],
];
Wenn Sie eigene Filesystem-Treiber registrieren, passen Sie den Callback von Storage::extend() so an, dass er direkt einen Illuminate\Filesystem\FilesystemAdapter zurückgibt.

firstOrNew / firstOrCreate / updateOrCreate bei BelongsToMany

Auswirkung: Mittel In Laravel 8 wurden die im ersten Argument übergebenen Attribute mit der Pivot-Tabelle verglichen. In Laravel 9 werden sie mit der Tabelle des verwandten Modells verglichen.
// Suche und Aktualisierung von name in der Tabelle des verwandten Modells
$user->roles()->updateOrCreate([
    'name' => 'Administrator',
]);
Außerdem akzeptiert firstOrCreate nun als zweites Argument $values und richtet sich damit nach dem Verhalten der anderen Relationen.
// Beim Anlegen zusätzlich created_by mergen
$user->roles()->firstOrCreate([
    'name' => 'Administrator',
], [
    'created_by' => $user->id,
]);

Custom Casts und null

Auswirkung: Mittel In Laravel 9 wird die set-Methode eines Custom-Casts auch dann aufgerufen, wenn null zugewiesen wird. Casts, die null nicht vorsehen, können nach dem Upgrade Exceptions werfen.
public function set($model, $key, $value, $attributes)
{
    // In Laravel 9 kann null übergeben werden
    if ($value === null) {
        return [
            'address_line_one' => null,
            'address_line_two' => null,
        ];
    }

    if (! $value instanceof AddressModel) {
        throw new InvalidArgumentException('Bitte ein AddressModel übergeben.');
    }

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

Standard-Timeout des HTTP-Clients

Auswirkung: Mittel Der Default-Timeout des HTTP-Clients liegt nun bei 30 Sekunden. Zuvor wartete er im Zweifel unbegrenzt.
use Illuminate\Support\Facades\Http;

// Nur für langlaufende API-Aufrufe den Timeout verlängern
$response = Http::timeout(120)->get('https://example.com/api/status');

Ergänzung von PHP-Rückgabetypen

Auswirkung: Mittel In Laravel 9 wurden entsprechend den PHP- und Symfony-Anforderungen Rückgabetypen in zahlreichen Core-Klassen ergänzt. Wenn Sie Core-Klassen erweitern und dabei offsetGet, offsetSet, jsonSerialize, open, read u. Ä. überschreiben, ergänzen Sie in Ihrer Implementierung dieselben Rückgabetypen.
// Bei Override einer Core-Klasse den Rückgabetyp angleichen
public function offsetGet($key): mixed
{
    return parent::offsetGet($key);
}

Umbenennung des Postgres-schema-Settings

Auswirkung: Mittel Wenn Sie den Search-Path in Postgres-Verbindungen konfigurieren, benennen Sie in config/database.php den Key von schema in search_path um.
return [
    'pgsql' => [
        // Laravel 8.x
        'schema' => 'public',

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

assertDeletedassertModelMissing

Auswirkung: Mittel Ersetzen Sie assertDeleted zur Prüfung eines gelöschten Modells durch assertModelMissing.
// Vorher
$this->assertDeleted($user);

// Nachher
$this->assertModelMissing($user);

Verlagerung des lang-Verzeichnisses

Auswirkung: Mittel In neuen Laravel-9-Anwendungen liegen die Sprachdateien nun im Projekt-Root unter lang und nicht mehr unter resources/lang. Bestehende Anwendungen laufen weiterhin ohne größere Auswirkungen, doch bei Angleichung an das neue Skelett oder wenn ein Paket Übersetzungen veröffentlicht, sollten Sie das prüfen.
// Ziel-Pfad statt fest codiert über langPath() auflösen
$this->publishes([
    __DIR__.'/../lang' => app()->langPath('vendor/package-name'),
]);

Änderung der Password-Rule

Auswirkung: Mittel Die Rule password, die den Abgleich mit dem Passwort des angemeldeten Nutzers durchführt, wurde in current_password umbenannt.
// Vorher
'password' => ['required', 'password'],

// Nachher
'password' => ['required', 'current_password'],

Änderung der Methoden when / unless

Auswirkung: Mittel In Laravel 8 galten Closures, die an when oder unless übergeben wurden, als truthy – der Zweig wurde unbeabsichtigt ausgeführt. In Laravel 9 wird die Closure ausgeführt und ihr Rückgabewert als Bedingung verwendet.
$collection->when(function ($collection) {
    // Der hier zurückgegebene Wert wird als Bedingung ausgewertet
    return false;
}, function ($collection) {
    // Da false zurückkam, wird dies nicht ausgeführt
    $collection->merge([1, 2, 3]);
});

Umgang mit nicht validierten Array-Keys

Auswirkung: Mittel In Laravel 9 werden nicht validierte Array-Keys in der Rückgabe von validated() stets ausgeschlossen. Wenn Sie das alte Verhalten aus Laravel 8 wünschen, rufen Sie explizit includeUnvalidatedArrayKeys() auf.
use Illuminate\Support\Facades\Validator;

public function boot()
{
    // Nur aktivieren, wenn das alte Laravel-8-Verhalten benötigt wird
    Validator::includeUnvalidatedArrayKeys();
}

Fazit

Beim Upgrade von Laravel 8 auf 9 stehen die Aktualisierung auf PHP 8.0.2, der Wechsel zu Symfony Mailer und die Umstellung auf Flysystem 3.x im Zentrum. Wenn Sie zuerst Mailversand, Storage, eigene Casts und Test-Helper prüfen, verringern sich die Nachwirkungen nach dem Upgrade.
ÄnderungAuswirkungMaßnahme
PHP 8.0.2 / laravel/framework:^9.0Hochcomposer.json und Laufzeitumgebung aktualisieren
Umstieg auf Symfony MailerHochSwiftMailer-APIs und Mail-Konfiguration prüfen
Flysystem 3.xHochRückgabewerte, Exceptions und Treiber-Abhängigkeiten von Storage prüfen
Upsert-Methoden bei BelongsToManyMittelVergleichsziel liegt nun bei der Tabelle des verwandten Modells
Custom Casts und nullMittelset() gegen null-Werte robust machen
30-s-Timeout beim HTTP-ClientMittelNur bei Bedarf timeout() explizit setzen
Entfall von assertDeletedMittelDurch assertModelMissing() ersetzen
Verlagerung des lang-VerzeichnissesMittelFeste Pfade durch app()->langPath() ersetzen
Änderung der password-RuleMittelZu current_password migrieren
Ausschluss nicht validierter Array-KeysMittelBei Bedarf includeUnvalidatedArrayKeys() nutzen

Referenzen

Zuletzt geändert am 13. Juli 2026