Vai al contenuto principale

Introduzione

Laravel 9 è stato rilasciato l’8 febbraio 2022. Questa guida riassume la procedura di aggiornamento da Laravel 8.x a 9.x e le modifiche di maggiore impatto.
Il tempo stimato per l’aggiornamento è di circa 30 minuti. Il carico di lavoro può aumentare in base a invio email, storage dei file, cast personalizzati e override di classi core del framework.

Aggiornamento automatico con Laravel Shift

Puoi automatizzare l’aggiornamento con Laravel Shift. Shift aiuta ad aggiornare composer.json e i file di configurazione ed è un buon punto di partenza per il confronto delle differenze.

Cambiamenti per livello di impatto

Impatto: Alto

  • Aggiornamento delle dipendenze
  • Migrazione a Flysystem 3.x
  • Migrazione a Symfony Mailer

Impatto: Medio

  • Metodi firstOrNew / firstOrCreate / updateOrCreate di BelongsToMany
  • Cast personalizzati e comportamento su null
  • Timeout predefinito del client HTTP
  • Aggiunta di return type PHP
  • Rinomina della configurazione schema di Postgres
  • Rimozione del metodo assertDeleted
  • Spostamento della directory lang
  • Modifiche alle regole della password
  • Modifiche ai metodi when / unless
  • Trattamento delle chiavi di array non validate

Procedura di aggiornamento

Aggiornamento delle dipendenze

Impatto: Alto Laravel 9 richiede PHP 8.0.2 o superiore. Rivedi prima di tutto le dipendenze in composer.json.
{
  "require": {
    "php": "^8.0.2",
    "laravel/framework": "^9.0",
    "spatie/laravel-ignition": "^1.0"
  },
  "require-dev": {
    "nunomaduro/collision": "^6.1"
  }
}
Ulteriori aggiornamenti da verificare per le applicazioni interessate:
  • Rimuovi facade/ignition e sostituiscilo con spatie/laravel-ignition:^1.0
  • Se usi pusher/pusher-php-server, aggiornalo a ^5.0
  • Verifica che i pacchetti di terze parti in uso siano compatibili con Laravel 9
  • Se usi il canale di notifica Vonage, consulta anche la guida di aggiornamento dedicata
Dopo l’aggiornamento installa le dipendenze.
composer update

Requisiti versione PHP

Impatto: Alto Laravel 9 richiede PHP 8.0.2 o superiore. Allinea la versione di PHP in CI, ambiente di sviluppo locale e produzione prima di procedere all’aggiornamento.

Migrazione a Symfony Mailer

Impatto: Alto Una delle grandi modifiche di Laravel 9 è la migrazione da SwiftMailer (fine manutenzione dicembre 2021) a Symfony Mailer. Le app che usano solo Mail::to()->send() ne risentono poco; se invece tocchi direttamente le API a basso livello di SwiftMailer devi verificare.

Dipendenze dei driver

# Aggiungi solo se usi Mailgun
composer require symfony/mailgun-mailer symfony/http-client

# Per Postmark rimuovi il pacchetto SwiftMailer e sostituiscilo
composer remove wildbit/swiftmailer-postmark
composer require symfony/postmark-mailer symfony/http-client

Da withSwiftMessage a withSymfonyMessage

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

// Laravel 9.x: basato su Symfony Mailer
use Symfony\Component\Mime\Email;

$this->withSymfonyMessage(function (Email $message) {
    $message->getHeaders()->addTextHeader('Custom-Header', 'Header Value');
});
send, html, raw e plain di Illuminate\Mail\Mailer restituiscono ora Illuminate\Mail\SentMessage invece di void. Inoltre, la proprietà message dell’evento MessageSent contiene Symfony\Component\Mime\Email invece di Swift_Message.

Revisione della configurazione SMTP

In Symfony Mailer l’opzione stream di SMTP è stata rimossa e le impostazioni supportate si spostano al livello superiore.
return [
    'mailers' => [
        'smtp' => [
            // Configurazione Laravel 8.x
            'stream' => [
                'ssl' => [
                    'verify_peer' => false,
                ],
            ],

            // Configurazione Laravel 9.x
            'verify_peer' => false,
        ],
    ],
];
Non è più necessario impostare esplicitamente auth_mode. È più sicuro spostare l’operatività verso la validazione degli indirizzi email prima dell’invio, invece che recuperarli dopo.

Migrazione a Flysystem 3.x

Impatto: Alto Laravel 9 aggiorna l’implementazione interna della facade Storage da Flysystem 1.x a 3.x. I metodi di manipolazione dei file mantengono la massima compatibilità possibile, ma ci sono differenze in eccezioni, valori di ritorno e registrazione degli adapter.

Installazione aggiuntiva dei driver

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

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

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

Principali modifiche di comportamento di Storage

  • put / write / writeStream sovrascrivono di default i file esistenti
  • In caso di fallimento della scrittura viene restituito false, non un’eccezione
  • Leggendo un file inesistente si ottiene null, non un’eccezione
  • delete su un file inesistente restituisce true
  • L’adapter cached è stato rimosso: la chiave cache nella configurazione dei disk può essere eliminata
Se vuoi mantenere il vecchio comportamento con eccezioni in scrittura, imposta l’opzione throw.
return [
    'disks' => [
        'public' => [
            'driver' => 'local',
            // Abilita solo se vuoi che venga sollevata un'eccezione in caso di errore di scrittura
            'throw' => true,
        ],
    ],
];
Se registri driver di filesystem personalizzati, aggiorna la callback di Storage::extend() in modo che restituisca direttamente un Illuminate\Filesystem\FilesystemAdapter.

firstOrNew / firstOrCreate / updateOrCreate di BelongsToMany

Impatto: Medio In Laravel 8 l’array di attributi passato come primo argomento a questi metodi era confrontato con la tabella pivot. In Laravel 9 il confronto avviene con la tabella del modello correlato.
// Cerca e aggiorna name nella tabella del modello correlato
$user->roles()->updateOrCreate([
    'name' => 'Administrator',
]);
Inoltre, firstOrCreate può ora ricevere $values come secondo argomento, allineandosi al comportamento delle altre relazioni.
// Aggiunge created_by solo in fase di creazione
$user->roles()->firstOrCreate([
    'name' => 'Administrator',
], [
    'created_by' => $user->id,
]);

Cast personalizzati e null

Impatto: Medio In Laravel 9 il metodo set dei custom cast viene chiamato anche assegnando null. I cast che non prevedono null potrebbero generare eccezioni dopo l’aggiornamento.
public function set($model, $key, $value, $attributes)
{
    // In Laravel 9 può arrivare null
    if ($value === null) {
        return [
            'address_line_one' => null,
            'address_line_two' => null,
        ];
    }

    if (! $value instanceof AddressModel) {
        throw new InvalidArgumentException('Devi passare un AddressModel.');
    }

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

Timeout predefinito del client HTTP

Impatto: Medio Il timeout predefinito del client HTTP è ora di 30 secondi. Prima era possibile attendere all’infinito.
use Illuminate\Support\Facades\Http;

// Estendi individualmente il timeout solo per le chiamate API lunghe
$response = Http::timeout(120)->get('https://example.com/api/status');

Aggiunta di return type PHP

Impatto: Medio In Laravel 9 sono stati aggiunti i tipi di ritorno a varie classi core in linea con i requisiti di PHP e Symfony. Se estendi classi core di Laravel e sovrascrivi metodi come offsetGet, offsetSet, jsonSerialize, open o read, aggiungi gli stessi tipi di ritorno anche nella tua implementazione.
// Se sovrascrivi una classe core, allinea il tipo di ritorno
public function offsetGet($key): mixed
{
    return parent::offsetGet($key);
}

Rinomina della configurazione schema di Postgres

Impatto: Medio Se imposti il search path per la connessione Postgres, in config/database.php cambia la chiave da schema a search_path.
return [
    'pgsql' => [
        // Laravel 8.x
        'schema' => 'public',

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

Da assertDeleted a assertModelMissing

Impatto: Medio Il metodo assertDeleted usato per verificare l’eliminazione di un modello va sostituito con assertModelMissing.
// Prima
$this->assertDeleted($user);

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

Spostamento della directory lang

Impatto: Medio Nelle nuove app Laravel 9 i file di lingua non stanno più in resources/lang ma nella directory lang nella root del progetto. Per le app esistenti l’impatto è ridotto se non intendi allinearti al nuovo scheletro, ma se vuoi allinearti o se pubblichi traduzioni nei tuoi pacchetti, rivedi il codice.
// Usa langPath() invece di un path fisso per stabilire la destinazione di pubblicazione
$this->publishes([
    __DIR__.'/../lang' => app()->langPath('vendor/package-name'),
]);

Modifiche alle regole della password

Impatto: Medio La regola password che verifica che la password corrisponda a quella dell’utente attualmente loggato è stata rinominata in current_password.
// Prima
'password' => ['required', 'password'],

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

Modifiche ai metodi when / unless

Impatto: Medio In Laravel 8, passando una closure a when o unless, la closure stessa veniva valutata come truthy e il branch veniva eseguito involontariamente. In Laravel 9 la closure viene eseguita e il suo valore di ritorno è usato come condizione.
$collection->when(function ($collection) {
    // Il valore restituito qui viene valutato come condizione
    return false;
}, function ($collection) {
    // Poiché è stato restituito false, questa operazione non viene eseguita
    $collection->merge([1, 2, 3]);
});

Trattamento delle chiavi di array non validate

Impatto: Medio In Laravel 9 le chiavi di array non validate vengono sempre escluse dall’array restituito da validated(). Se vuoi mantenere il comportamento di Laravel 8, chiama esplicitamente includeUnvalidatedArrayKeys().
use Illuminate\Support\Facades\Validator;

public function boot()
{
    // Abilita solo se vuoi tornare al comportamento di Laravel 8
    Validator::includeUnvalidatedArrayKeys();
}

Conclusioni

L’aggiornamento da Laravel 8 a 9 verte su PHP 8.0.2, migrazione a Symfony Mailer e adattamento a Flysystem 3.x. Ispezionando prima invio email, storage, cast personalizzati e helper di test riduci gli imprevisti post-aggiornamento.
ModificaImpattoAzione
PHP 8.0.2 / laravel/framework:^9.0AltoAggiorna composer.json e l’ambiente di esecuzione
Migrazione a Symfony MailerAltoVerifica le API SwiftMailer e la configurazione mail
Flysystem 3.xAltoVerifica valori di ritorno, eccezioni e driver di Storage
Metodi upsert di BelongsToManyMedioNota che il target di ricerca è ora la tabella del modello correlato
Cast personalizzati e nullMedioCorreggi set() perché regga null
Timeout HTTP di 30 secondiMedioSpecifica timeout() solo dove serve
Rimozione di assertDeletedMedioSostituisci con assertModelMissing()
Spostamento di langMedioCambia i riferimenti fissi in app()->langPath()
Modifica regola passwordMedioAggiorna a current_password
Esclusione chiavi non validateMedioUsa includeUnvalidatedArrayKeys() solo se necessario

Riferimenti

Ultima modifica il 13 luglio 2026