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

# Aggiornamento da Laravel 8 a 9

> Procedura di aggiornamento da Laravel 8.x a 9.x e spiegazione delle principali modifiche

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

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

### Aggiornamento automatico con Laravel Shift

Puoi automatizzare l'aggiornamento con [Laravel Shift](https://laravelshift.com/). 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`.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

| Modifica                             | Impatto | Azione                                                               |
| ------------------------------------ | ------- | -------------------------------------------------------------------- |
| PHP 8.0.2 / `laravel/framework:^9.0` | Alto    | Aggiorna `composer.json` e l'ambiente di esecuzione                  |
| Migrazione a Symfony Mailer          | Alto    | Verifica le API SwiftMailer e la configurazione mail                 |
| Flysystem 3.x                        | Alto    | Verifica valori di ritorno, eccezioni e driver di `Storage`          |
| Metodi upsert di `BelongsToMany`     | Medio   | Nota che il target di ricerca è ora la tabella del modello correlato |
| Cast personalizzati e `null`         | Medio   | Correggi `set()` perché regga `null`                                 |
| Timeout HTTP di 30 secondi           | Medio   | Specifica `timeout()` solo dove serve                                |
| Rimozione di `assertDeleted`         | Medio   | Sostituisci con `assertModelMissing()`                               |
| Spostamento di `lang`                | Medio   | Cambia i riferimenti fissi in `app()->langPath()`                    |
| Modifica regola `password`           | Medio   | Aggiorna a `current_password`                                        |
| Esclusione chiavi non validate       | Medio   | Usa `includeUnvalidatedArrayKeys()` solo se necessario               |

***

## Riferimenti

* [Guida ufficiale all'aggiornamento (in inglese)](https://laravel.com/docs/9.x/upgrade)
* [laravel/docs 9.x `upgrade.md`](https://github.com/laravel/docs/blob/9.x/upgrade.md)
* [Diff del repository laravel/laravel (8.x → 9.x)](https://github.com/laravel/laravel/compare/8.x...9.x)
* [Laravel Shift](https://laravelshift.com) — servizio della community che automatizza l'aggiornamento
* [Documentazione Symfony Mailer](https://symfony.com/doc/6.0/mailer.html)
* [Documentazione Flysystem 3](https://flysystem.thephpleague.com/v3/docs/)


## Related topics

- [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)
- [Guida all'aggiornamento da Laravel 11 a 12](/it/blog/upgrade-11-to-12.md)
- [Guida all'aggiornamento da Laravel 12 a 13](/it/blog/upgrade-12-to-13.md)
