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

# Guida all'aggiornamento da Laravel 12 a 13

> Spiega la procedura di aggiornamento da Laravel 12 a Laravel 13, i breaking change, le funzionalità deprecate e i punti salienti delle novità.

## Introduzione

Laravel 13 è stato rilasciato a marzo 2026. Questa guida illustra la procedura di aggiornamento da Laravel 12.x a 13.x.

<Info>
  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.
</Info>

### Aggiornamento assistito dall'AI

Puoi automatizzare l'aggiornamento con [Laravel Boost](https://github.com/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'è.

```text prompt theme={null}
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.

```json theme={null}
{
  "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.

```json theme={null}
{
  "require": {
    "laravel/boost": "^2.0"
  }
}
```

Dopo l'aggiornamento, installa le dipendenze con il comando seguente.

```shell theme={null}
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`:

```shell theme={null}
composer global update laravel/installer
```

Se usi la versione bundle di [Laravel Herd](https://herd.laravel.com), 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.

```php theme={null}
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

#### Prefisso della cache e nome del cookie di sessione

**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.

```php theme={null}
// 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`.

```ini theme={null}
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.

```php theme={null}
// 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`.

<Warning>
  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.
</Warning>

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'`.

```php theme={null}
// 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).

```php theme={null}
$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.

```php theme={null}
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`.

```php theme={null}
// 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.

```php theme={null}
// 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 (...)`.

```php theme={null}
// 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.

```php theme={null}
// 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 `VerifyCsrfToken`       | `PreventRequestForgery`      |
| Middleware `ValidateCsrfToken`     | `PreventRequestForgery`      |
| `JobAttempted::$exceptionOccurred` | `JobAttempted::$exception`   |
| `QueueBusy::$connection`           | `QueueBusy::$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.

```php theme={null}
// 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`.

```php theme={null}
// 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.

```php theme={null}
'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`.

```php theme={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.

```ini theme={null}
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.

```shell theme={null}
php artisan cache:clear
```

***

## Riferimenti

* [Guida ufficiale all'aggiornamento (in inglese)](https://laravel.com/docs/13.x/upgrade)
* [Diff del repository laravel/laravel (12.x → 13.x)](https://github.com/laravel/laravel/compare/12.x...13.x)
* [Laravel Shift](https://laravelshift.com) — servizio della community che automatizza l'aggiornamento
* [Laravel Boost](https://github.com/laravel/boost) — server MCP per aggiornamenti assistiti dall'AI


## Related topics

- [Guida all'aggiornamento da Laravel 11 a 12](/it/blog/upgrade-11-to-12.md)
- [Riepilogo delle novità di Laravel 13](/it/blog/laravel-13-new-features.md)
- [Aggiornamento da Laravel 8 a 9](/it/blog/upgrade-8-to-9.md)
- [Aggiornamento da Laravel 9 a 10](/it/blog/upgrade-9-to-10.md)
- [Aggiornamento da Laravel 10 a 11](/it/blog/upgrade-10-to-11.md)
