Vai al contenuto principale

Introduzione

Laravel 13 è stato rilasciato a marzo 2026. Questa guida illustra la procedura di aggiornamento da Laravel 12.x a 13.x.
Il tempo stimato necessario all’aggiornamento è di circa 10 minuti. L’impatto dei breaking change sull’applicazione varia in base a dimensione e funzionalità usate.

Aggiornamento assistito dall’AI

Puoi automatizzare l’aggiornamento con Laravel Boost. Boost è un server MCP di prima parte che fornisce agli assistenti AI prompt di aggiornamento passo passo. Installalo nell’app Laravel 12 e usa lo slash command /upgrade-laravel-v13 con Claude Code, Cursor, OpenCode, Gemini o VS Code per avviare l’aggiornamento a Laravel 13. Il comando richiede laravel/boost ^2.0. Anche con strumenti AI che non supportano gli slash command puoi eseguire la stessa procedura di aggiornamento referenziando direttamente il file di prompt. Incolla nell’AI il prompt seguente così com’è.
prompt
Leggi il prompt fornito da Laravel Boost ed esegui l'aggiornamento da Laravel 12 a 13.
https://raw.githubusercontent.com/laravel/boost/refs/heads/main/src/Mcp/Prompts/UpgradeLaravelv13/upgrade-laravel-v13.blade.php

- Riflette le modifiche dello scheletro `laravel/laravel`. Controlla il branch 13.x, non master.
- Per `database/migrations/*_create_cache_table.php`, non modificare la migration esistente ma crearne una nuova.
- Imposta `serialization` di `config/session.php` a `php`. `'serialization' => 'php'`
- In Laravel 13 la modifica che genera più errori nelle app è quella al comportamento della cache: cerca gli usi della cache nel progetto e, se sembrano sicuri da consentire, aggiorna `serializable_classes` di config/cache.php per consentirli.

'serializable_classes' => [
    App\Data\CachedDashboardStats::class,
    App\Support\CachedPricingSnapshot::class,
    Illuminate\Support\Collection::class,
    Illuminate\Database\Eloquent\Collection::class,
],

Cambiamenti per livello di impatto

Impatto: Alto

  • Aggiornamento delle dipendenze
  • Aggiornamento dell’installer di Laravel
  • Protezione anti-forgery delle richieste (CSRF)

Impatto: Medio

  • Configurazione serializable_classes della cache
  • Configurazione serialization della sessione

Impatto: Basso

  • Prefisso della cache e nome del cookie di sessione
  • Serializzazione dei modelli in collection
  • Container::call e default di classi nullable
  • Priorità di registrazione delle rotte con dominio
  • Payload dell’eccezione dell’evento JobAttempted
  • Binding delle callback di extend del Manager
  • Query MySQL DELETE (JOIN / ORDER BY / LIMIT)
  • Nomi delle view di paginazione Bootstrap
  • Generazione dei nomi delle tabelle pivot polimorfiche
  • Rinomina di una proprietà dell’evento QueueBusy
  • Reset della factory Str tra test

Procedura di aggiornamento

Aggiornamento delle dipendenze

Impatto: Alto Aggiorna in composer.json le seguenti dipendenze.
{
  "require": {
    "laravel/framework": "^13.0",
    "laravel/tinker": "^3.0"
  },
  "require-dev": {
    "phpunit/phpunit": "^12.0",
    "pestphp/pest": "^4.0"
  }
}
Se usi Laravel Boost, aggiornalo di conseguenza.
{
  "require": {
    "laravel/boost": "^2.0"
  }
}
Dopo l’aggiornamento, installa le dipendenze con il comando seguente.
composer update

Aggiornamento dell’installer di Laravel

Impatto: Alto Se usi la CLI dell’installer di Laravel per creare nuove app, aggiornalo a una versione compatibile con Laravel 13.x. Se l’hai installato con composer global require:
composer global update laravel/installer
Se usi la versione bundle di Laravel Herd, aggiorna Herd stesso all’ultima release.

Modifiche non retrocompatibili (Breaking Changes)

Sicurezza

Protezione anti-forgery delle richieste

Impatto: Alto Il middleware CSRF di Laravel è stato rinominato da VerifyCsrfToken a PreventRequestForgery. È stata aggiunta anche la verifica dell’origine della richiesta tramite l’header Sec-Fetch-Site. VerifyCsrfToken e ValidateCsrfToken restano come alias deprecati, ma tutti i riferimenti diretti vanno aggiornati a PreventRequestForgery. Occorre particolare attenzione nei punti in cui il middleware viene escluso, come test e definizioni di rotta.
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;

// Laravel <= 12.x
->withoutMiddleware([VerifyCsrfToken::class]);

// Laravel >= 13.x
->withoutMiddleware([PreventRequestForgery::class]);
Anche l’API di configurazione dei middleware supporta ora preventRequestForgery(...).

Cache

Impatto: Basso Il prefisso predefinito delle chiavi di cache e Redis usa ora un suffisso separato da trattino. Il nome del cookie di sessione predefinito usa ora Str::snake(...). Molte applicazioni impostano valori espliciti nei file di configurazione e quindi non risentono di questa modifica. Sono interessate solo le app che si affidano ai fallback del framework.
// Laravel <= 12.x
Str::slug((string) env('APP_NAME', 'laravel'), '_').'_cache_';
Str::slug((string) env('APP_NAME', 'laravel'), '_').'_database_';
Str::slug((string) env('APP_NAME', 'laravel'), '_').'_session';

// Laravel >= 13.x
Str::slug((string) env('APP_NAME', 'laravel')).'-cache-';
Str::slug((string) env('APP_NAME', 'laravel')).'-database-';
Str::snake((string) env('APP_NAME', 'laravel')).'_session';
Per mantenere il comportamento precedente, impostali esplicitamente nel file .env.
CACHE_PREFIX=myapp_cache_
REDIS_PREFIX=myapp_database_
SESSION_COOKIE=myapp_session

Configurazione serializable_classes della cache

Impatto: Medio Alla configurazione predefinita di cache è stata aggiunta l’opzione serializable_classes, con default false. Serve a prevenire attacchi di deserializzazione PHP tramite gadget chain in caso di compromissione di APP_KEY. Se la tua app salva intenzionalmente oggetti PHP in cache, devi elencare esplicitamente le classi ammesse alla deserializzazione.
// config/cache.php
'serializable_classes' => [
    App\Data\CachedDashboardStats::class,
    App\Support\CachedPricingSnapshot::class,
],
Se stavi deserializzando oggetti in cache arbitrari, devi passare a una whitelist esplicita di classi o a payload di cache non-oggetto (per esempio array).

Configurazione serialization della sessione

Impatto: Medio Nello scheletro di Laravel 13 (laravel/laravel), a config/session.php è stato aggiunto 'serialization' => 'json'. Tuttavia, il default interno del framework rimane php.
Se usi uno strumento AI e applichi tali quali le modifiche dello scheletro, in config/session.php potrebbe essere aggiunto 'serialization' => 'json'. Questa modifica cambia il metodo di serializzazione delle sessioni e le app che salvano oggetti PHP in sessione possono generare errori.
La guida ufficiale di aggiornamento non riporta questa modifica. Va intesa come “non è necessario modificarla”. In Laravel a volte, per aggiornamenti dall’ultima major, alcune modifiche non compaiono nella guida di aggiornamento perché non hanno impatto. Quando dovrai aggiornare tra qualche anno, ti serviranno queste informazioni: registrarle in modo non ufficiale è importante. Per mantenere il comportamento delle versioni precedenti a Laravel 12, imposta esplicitamente 'serialization' => 'php'.
// config/session.php
'serialization' => 'php',
Se vuoi attivare la serializzazione JSON, verifica prima che nessuna sessione contenga oggetti PHP.

Container

Container::call e default di classi nullable

Impatto: Basso Container::call rispetta ora i valori di default dei parametri di classe nullable quando il binding non esiste (in linea con il comportamento introdotto in Laravel 12 per la constructor injection).
$container->call(function (?Carbon $date = null) {
    return $date;
});

// Laravel <= 12.x: restituisce un'istanza Carbon
// Laravel >= 13.x: restituisce null

Database

Query MySQL DELETE

Impatto: Basso Laravel compila ora, nella grammatica MySQL, DELETE ... JOIN completi che includono ORDER BY e LIMIT. Nelle versioni precedenti, in un DELETE con JOIN le clausole ORDER BY / LIMIT potevano essere ignorate. In Laravel 13 queste clausole sono incluse nell’SQL generato. Di conseguenza, alcuni engine di database che non supportano questa sintassi possono generare QueryException.

Eloquent

Generazione dei nomi delle tabelle pivot polimorfiche

Impatto: Basso Quando viene stimato il nome della tabella di modelli pivot polimorfici che usano una classe pivot personalizzata, Laravel genera ora nomi al plurale. Se dipendevi dal precedente nome stimato al singolare, dichiara esplicitamente il nome della tabella nel modello pivot.
class RoleUser extends MorphPivot
{
    protected $table = 'role_user'; // Indicazione esplicita
}

Serializzazione dei modelli in collection

Impatto: Basso Quando una collection di modelli Eloquent viene serializzata e ripristinata (per esempio, in un job in coda), le relazioni eager-loaded vengono ora ripristinate per i modelli. Se il tuo codice si aspettava che dopo la deserializzazione le relazioni non esistessero, va corretto.

Code

Payload dell’eccezione dell’evento JobAttempted

Impatto: Basso L’evento Illuminate\Queue\Events\JobAttempted espone ora, al posto della precedente proprietà boolean $exceptionOccurred, un oggetto eccezione (o null) nella proprietà $exception.
// Laravel <= 12.x
if ($event->exceptionOccurred) {
    // Si è verificata un'eccezione
}

// Laravel >= 13.x
if ($event->exception !== null) {
    // Si è verificata un'eccezione
    $exception = $event->exception;
}

Rinomina di una proprietà dell’evento QueueBusy

Impatto: Basso Nell’evento Illuminate\Queue\Events\QueueBusy, la proprietà $connection è stata rinominata in $connectionName per coerenza con altri eventi di coda.
// Laravel <= 12.x
$event->connection;

// Laravel >= 13.x
$event->connectionName;

Routing

Priorità di registrazione delle rotte con dominio

Impatto: Basso Le rotte con un dominio esplicito hanno ora priorità sulle rotte senza dominio nella matching delle rotte. In questo modo le rotte catch-all su sottodominio funzionano in modo coerente anche se le rotte senza dominio sono registrate prima.

Support

Binding delle callback di extend del Manager

Impatto: Basso Le closure dei driver personalizzati registrate con il metodo extend del Manager vengono ora bindate all’istanza del manager. Se in queste callback usavi $this riferendoti a un altro oggetto (ad esempio l’istanza del service provider), devi spostare il valore nella closure con use (...).
// Laravel <= 12.x
Manager::extend('custom', function ($app) {
    return $this->createCustomDriver($app); // $this è il service provider
});

// Laravel >= 13.x
$provider = $this;
Manager::extend('custom', function ($app) use ($provider) {
    return $provider->createCustomDriver($app);
});

Reset della factory Str tra test

Impatto: Basso Laravel resetta ora le factory personalizzate Str nel teardown dei test. Se dipendevi dal fatto che le factory personalizzate di UUID / ULID / stringhe casuali persistessero tra i metodi di test, impostale in ciascun test o in un setup hook.

View

Nomi delle view di paginazione Bootstrap

Impatto: Basso I nomi interni delle view di paginazione predefinite di Bootstrap 3 sono diventati espliciti.
// Laravel <= 12.x
pagination::default
pagination::simple-default

// Laravel >= 13.x
pagination::bootstrap-3
pagination::simple-bootstrap-3
Se referenzi direttamente i vecchi nomi, aggiornali.

Funzionalità deprecate

FunzionalitàAlternativa
Middleware VerifyCsrfTokenPreventRequestForgery
Middleware ValidateCsrfTokenPreventRequestForgery
JobAttempted::$exceptionOccurredJobAttempted::$exception
QueueBusy::$connectionQueueBusy::$connectionName

Aggiunte ai contract

Impatto: Molto basso Rilevante solo se hai implementazioni personalizzate.

Contract Dispatcher

Al contract Illuminate\Contracts\Bus\Dispatcher è stato aggiunto il metodo dispatchAfterResponse($command, $handler = null).

Contract ResponseFactory

Al contract Illuminate\Contracts\Routing\ResponseFactory è stata aggiunta la firma di eventStream.

Contract MustVerifyEmail

Al contract Illuminate\Contracts\Auth\MustVerifyEmail è stato aggiunto markEmailAsUnverified().

Contract Queue

Al contract Illuminate\Contracts\Queue\Queue sono stati aggiunti i seguenti metodi di ispezione delle dimensioni della coda (prima erano dichiarati solo tramite docblock).
  • pendingSize
  • delayedSize
  • reservedSize
  • creationTimeOfOldestPendingJob

Contract Store / Repository

Ai contract di cache è stato aggiunto il metodo touch per l’estensione del TTL.
// Illuminate\Contracts\Cache\Store
public function touch($key, $seconds);

Punti salienti delle nuove funzionalità

Aggiornamento assistito dall’AI (Laravel Boost)

Laravel Boost è il server MCP ufficiale. Si integra con editor AI e permette di semi-automatizzare l’aggiornamento con il comando /upgrade-laravel-v13.

Verifica dell’origine tramite header Sec-Fetch-Site

Il middleware PreventRequestForgery effettua una verifica aggiuntiva dell’origine tramite l’header Sec-Fetch-Site. Ciò rafforza la protezione CSRF.

Deserializzazione sicura della cache

Grazie alla configurazione serializable_classes vengono deserializzate solo le classi consentite. Migliora la sicurezza contro gli attacchi di deserializzazione PHP.

eventStream per SSE (Server-Sent Events)

Al contract ResponseFactory è stato aggiunto eventStream, migliorando il supporto a Server-Sent Events.

Migliore visibilità delle code

I metodi pendingSize, delayedSize e reservedSize del contract Queue permettono un monitoraggio più fine dello stato della coda.

Problemi comuni nella migrazione e soluzioni

Problema: i test CSRF falliscono

Sintomo: i test che referenziano VerifyCsrfToken falliscono con errore di classe non trovata. Soluzione: aggiorna tutti i riferimenti a PreventRequestForgery.
// Prima
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
->withoutMiddleware([VerifyCsrfToken::class]);

// Dopo
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;
->withoutMiddleware([PreventRequestForgery::class]);

Problema: non si riesce a ripristinare oggetti dalla cache

Sintomo: i dati recuperati dalla cache sono null oppure viene sollevata UnserializationFailedException. Soluzione: aggiungi le classi usate a serializable_classes in config/cache.php oppure converti in array i valori da cachare.
'serializable_classes' => [
    App\Models\User::class,
    App\Data\SomeData::class,
],

Problema: il listener JobAttempted non funziona

Sintomo: $event->exceptionOccurred è null o dà errore di proprietà indefinita. Soluzione: cambia in $event->exception !== null.
// Prima
if ($event->exceptionOccurred) { ... }

// Dopo
if ($event->exception !== null) { ... }

Problema: le sessioni vengono invalidate

Sintomo: dopo l’aggiornamento gli utenti vengono sloggati. Soluzione: il nome del cookie di sessione predefinito è cambiato. Imposta esplicitamente SESSION_COOKIE in .env per mantenere il valore precedente.
SESSION_COOKIE=laravel_session

Problema: chiavi di cache non trovate

Sintomo: dopo l’aggiornamento aumentano i cache miss. Soluzione: il prefisso della cache è cambiato. Imposta CACHE_PREFIX in .env o svuota la cache.
php artisan cache:clear

Riferimenti

Ultima modifica il 13 luglio 2026