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

# Mise à niveau de Laravel 8 vers 9

> Procédure de mise à niveau de Laravel 8.x vers 9.x et explication des principaux changements

## Introduction

Laravel 9 a été publié le 8 février 2022. Ce guide organise la procédure de mise à niveau de Laravel 8.x vers 9.x et les changements les plus impactants.

<Info>
  Le temps estimé pour la mise à niveau est d'**environ 30 minutes**. Cependant, la charge de travail peut augmenter selon l'utilisation de l'envoi d'e-mails, du stockage de fichiers, des casts personnalisés et des surcharges des classes principales du framework.
</Info>

### Mise à niveau automatisée avec Laravel Shift

Vous pouvez également automatiser la mise à niveau avec [Laravel Shift](https://laravelshift.com/). Shift aide à mettre à jour `composer.json` et les fichiers de configuration, ce qui en fait un bon point de départ pour vérifier les diffs.

***

## Changements par niveau d'impact

### Impact : élevé

* Mise à jour des dépendances
* Migration vers Flysystem 3.x
* Migration vers Symfony Mailer

### Impact : moyen

* Méthodes `firstOrNew` / `firstOrCreate` / `updateOrCreate` de `BelongsToMany`
* Comportement de Custom Casts avec `null`
* Timeout par défaut du client HTTP
* Ajout des PHP Return Types
* Renommage du paramètre `schema` pour Postgres
* Suppression de la méthode `assertDeleted`
* Déplacement du répertoire `lang`
* Modification de la règle de mot de passe
* Modification des méthodes `when` / `unless`
* Traitement des clés de tableau non validées dans la validation

***

## Procédure de mise à niveau

### Mise à jour des dépendances

**Impact : élevé**

Laravel 9 nécessite **PHP 8.0.2 ou supérieur**. Commencez par revoir les dépendances dans `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"
  }
}
```

De plus, les mises à jour suivantes peuvent être nécessaires selon votre application.

* Supprimer `facade/ignition` et le remplacer par `spatie/laravel-ignition:^1.0`
* Si vous utilisez `pusher/pusher-php-server`, mettre à jour vers `^5.0`
* Vérifier que les paquets tiers utilisés disposent d'une version compatible avec Laravel 9
* Si vous utilisez le canal de notification Vonage, consulter également son guide de mise à niveau spécifique

Après la mise à jour, installez les dépendances.

```shell theme={null}
composer update
```

***

## Version requise de PHP

**Impact : élevé**

Laravel 9 requiert PHP 8.0.2 ou supérieur. Alignez la version de PHP sur l'ensemble de vos environnements — CI, développement local et production — avant de poursuivre la mise à niveau.

***

### Migration vers Symfony Mailer

**Impact : élevé**

L'un des grands changements de Laravel 9 est la migration de SwiftMailer (dont la maintenance a pris fin en décembre 2021) vers Symfony Mailer. Les applications qui utilisent uniquement le classique `Mail::to()->send()` sont peu impactées, tandis qu'une vérification est nécessaire si vous manipulez directement l'API bas niveau de SwiftMailer.

#### Dépendances des pilotes

```shell theme={null}
# À ajouter uniquement si vous utilisez Mailgun
composer require symfony/mailgun-mailer symfony/http-client

# Si vous utilisez Postmark, supprimez le paquet pour SwiftMailer et remplacez-le
composer remove wildbit/swiftmailer-postmark
composer require symfony/postmark-mailer symfony/http-client
```

#### De `withSwiftMessage` à `withSymfonyMessage`

```php theme={null}
// Laravel 8.x : basé sur SwiftMailer
$this->withSwiftMessage(function ($message) {
    $message->getHeaders()->addTextHeader('Custom-Header', 'Header Value');
});

// Laravel 9.x : basé sur Symfony Mailer
use Symfony\Component\Mime\Email;

$this->withSymfonyMessage(function (Email $message) {
    $message->getHeaders()->addTextHeader('Custom-Header', 'Header Value');
});
```

Les méthodes `send`, `html`, `raw`, `plain` de `Illuminate\Mail\Mailer` renvoient désormais `Illuminate\Mail\SentMessage` au lieu de `void`. De plus, la propriété `message` de l'événement `MessageSent` contient désormais `Symfony\Component\Mime\Email` au lieu de `Swift_Message`.

#### Révision de la configuration SMTP

Dans Symfony Mailer, l'option `stream` pour SMTP a été supprimée, et les options prises en charge sont déplacées au niveau supérieur.

```php theme={null}
return [
    'mailers' => [
        'smtp' => [
            // Configuration Laravel 8.x
            'stream' => [
                'ssl' => [
                    'verify_peer' => false,
                ],
            ],

            // Configuration Laravel 9.x
            'verify_peer' => false,
        ],
    ],
];
```

La configuration explicite de `auth_mode` n'est plus nécessaire. Il est plus sûr d'orienter votre workflow vers une validation des adresses e-mail invalides avant l'envoi, plutôt que de tenter de les récupérer après.

***

### Migration vers Flysystem 3.x

**Impact : élevé**

Laravel 9 met à jour l'implémentation interne de la façade `Storage` de Flysystem 1.x vers 3.x. Les méthodes de manipulation de fichiers restent aussi compatibles que possible, mais il existe des différences autour des exceptions, des valeurs de retour et de l'enregistrement des adaptateurs.

#### Installation supplémentaire des pilotes

```shell theme={null}
# Pilote Amazon S3
composer require -W league/flysystem-aws-s3-v3 "^3.0"

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

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

#### Principaux changements de comportement de `Storage`

* `put` / `write` / `writeStream` écrasent les fichiers existants par défaut
* En cas d'échec d'écriture, `false` est retourné au lieu d'une exception
* La lecture d'un fichier inexistant renvoie `null` au lieu d'une exception
* `delete` sur un fichier inexistant renvoie `true`
* L'adaptateur mis en cache a été supprimé ; vous pouvez supprimer la clé `cache` dans la configuration `disk`

Si vous souhaitez conserver le comportement précédent (exception en cas d'échec d'écriture), activez l'option `throw`.

```php theme={null}
return [
    'disks' => [
        'public' => [
            'driver' => 'local',
            // À activer uniquement si vous voulez lever une exception en cas d'échec d'écriture
            'throw' => true,
        ],
    ],
];
```

Si vous enregistrez des pilotes de système de fichiers personnalisés, ajustez le callback de `Storage::extend()` pour qu'il retourne directement une instance de `Illuminate\Filesystem\FilesystemAdapter`.

***

### `firstOrNew` / `firstOrCreate` / `updateOrCreate` de `BelongsToMany`

**Impact : moyen**

Dans Laravel 8, le tableau d'attributs passé en premier argument à ces méthodes était comparé à la table intermédiaire. Dans Laravel 9, il est comparé à la table du modèle associé.

```php theme={null}
// Recherche et met à jour name sur la table du modèle associé
$user->roles()->updateOrCreate([
    'name' => 'Administrator',
]);
```

De plus, `firstOrCreate` accepte désormais un deuxième argument `$values`, alignant son comportement sur celui des autres relations.

```php theme={null}
// Fusionne created_by uniquement lors de la création
$user->roles()->firstOrCreate([
    'name' => 'Administrator',
], [
    'created_by' => $user->id,
]);
```

***

### Custom Casts et `null`

**Impact : moyen**

Dans Laravel 9, la méthode `set` d'un custom cast est appelée même lorsque `null` est affecté à l'attribut casté. Les casts qui ne prévoient pas `null` peuvent lever une exception après la mise à niveau.

```php theme={null}
public function set($model, $key, $value, $attributes)
{
    // Dans Laravel 9, null peut être passé
    if ($value === null) {
        return [
            'address_line_one' => null,
            'address_line_two' => null,
        ];
    }

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

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

***

### Timeout par défaut du client HTTP

**Impact : moyen**

Le timeout par défaut du client HTTP est désormais de 30 secondes. Auparavant, il pouvait attendre indéfiniment.

```php theme={null}
use Illuminate\Support\Facades\Http;

// Prolonger le timeout individuellement pour les appels d'API plus longs
$response = Http::timeout(120)->get('https://example.com/api/status');
```

***

### Ajout des PHP Return Types

**Impact : moyen**

Dans Laravel 9, des types de retour ont été ajoutés aux différentes classes principales pour respecter les exigences de PHP et de Symfony. Si vous surchargez `offsetGet`, `offsetSet`, `jsonSerialize`, `open`, `read`, etc. dans des classes qui héritent des classes principales de Laravel, ajoutez les mêmes types de retour à vos propres implémentations.

```php theme={null}
// Alignez le type de retour lors de la surcharge des classes principales
public function offsetGet($key): mixed
{
    return parent::offsetGet($key);
}
```

***

### Renommage du paramètre `schema` pour Postgres

**Impact : moyen**

Si vous configurez le search path sur une connexion Postgres, modifiez le nom de la clé de `schema` en `search_path` dans `config/database.php`.

```php theme={null}
return [
    'pgsql' => [
        // Laravel 8.x
        'schema' => 'public',

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

***

### De `assertDeleted` à `assertModelMissing`

**Impact : moyen**

`assertDeleted`, utilisé pour vérifier la suppression d'un modèle, doit être remplacé par `assertModelMissing`.

```php theme={null}
// Avant
$this->assertDeleted($user);

// Après
$this->assertModelMissing($user);
```

***

### Déplacement du répertoire `lang`

**Impact : moyen**

Dans les nouvelles applications Laravel 9, les fichiers de langue sont placés à la racine du projet dans `lang`, et non plus dans `resources/lang`. Cela a peu d'impact si vous vous contentez de faire tourner votre application existante, mais vérifiez cela si vous vous alignez sur le nouveau squelette ou si vous publiez des fichiers de traduction dans un paquet.

```php theme={null}
// Utilisez langPath() pour déterminer la destination de publication au lieu d'un chemin fixe
$this->publishes([
    __DIR__.'/../lang' => app()->langPath('vendor/package-name'),
]);
```

***

### Modification de la règle de mot de passe

**Impact : moyen**

La règle `password`, qui vérifie que la valeur correspond au mot de passe de l'utilisateur actuellement connecté, a été renommée en `current_password`.

```php theme={null}
// Avant
'password' => ['required', 'password'],

// Après
'password' => ['required', 'current_password'],
```

***

### Modification des méthodes `when` / `unless`

**Impact : moyen**

Dans Laravel 8, lorsque vous passiez une closure à `when` ou `unless`, la closure elle-même était évaluée comme truthy, ce qui pouvait entraîner l'exécution involontaire de la branche conditionnelle. Dans Laravel 9, la closure est exécutée et sa valeur de retour est utilisée comme condition.

```php theme={null}
$collection->when(function ($collection) {
    // La valeur retournée ici est évaluée comme condition
    return false;
}, function ($collection) {
    // false a été retourné, donc ce traitement n'est pas exécuté
    $collection->merge([1, 2, 3]);
});
```

***

### Traitement des clés de tableau non validées

**Impact : moyen**

Dans Laravel 9, `validated()` exclut toujours les clés de tableau non validées du tableau qu'elle retourne. Pour conserver le comportement compatible de Laravel 8, appelez explicitement `includeUnvalidatedArrayKeys()`.

```php theme={null}
use Illuminate\Support\Facades\Validator;

public function boot()
{
    // À activer uniquement si vous souhaitez retrouver le comportement de Laravel 8
    Validator::includeUnvalidatedArrayKeys();
}
```

***

## Résumé

La mise à niveau de Laravel 8 vers 9 se concentre sur la mise à jour vers PHP 8.0.2, la migration vers Symfony Mailer et la compatibilité avec Flysystem 3.x. En inspectant d'abord l'envoi d'e-mails, le stockage, les casts personnalisés et les helpers de test, vous limiterez les problèmes après la mise à niveau.

| Changement                                 | Impact | Action                                                                            |
| ------------------------------------------ | ------ | --------------------------------------------------------------------------------- |
| PHP 8.0.2 / `laravel/framework:^9.0`       | élevé  | Mettre à jour `composer.json` et l'environnement d'exécution                      |
| Migration vers Symfony Mailer              | élevé  | Vérifier les API basées sur SwiftMailer et la configuration mail                  |
| Flysystem 3.x                              | élevé  | Vérifier les valeurs de retour, exceptions et dépendances de pilotes de `Storage` |
| Méthodes upsert de `BelongsToMany`         | moyen  | Vérifier que la cible de recherche est désormais la table du modèle associé       |
| Custom Casts et `null`                     | moyen  | Corriger `set()` pour qu'elle ne casse pas en recevant `null`                     |
| Timeout de 30 secondes du client HTTP      | moyen  | Spécifier `timeout()` uniquement pour les requêtes nécessaires                    |
| Suppression de `assertDeleted`             | moyen  | Remplacer par `assertModelMissing()`                                              |
| Déplacement du répertoire `lang`           | moyen  | Remplacer les références par chemin fixe par `app()->langPath()`                  |
| Modification de la règle `password`        | moyen  | Mettre à jour vers `current_password`                                             |
| Exclusion des clés de tableau non validées | moyen  | N'utiliser `includeUnvalidatedArrayKeys()` qu'en cas de besoin                    |

***

## Ressources

* [Guide officiel de mise à niveau (anglais)](https://laravel.com/docs/9.x/upgrade)
* [`upgrade.md` de laravel/docs 9.x](https://github.com/laravel/docs/blob/9.x/upgrade.md)
* [Diff du dépôt laravel/laravel (8.x → 9.x)](https://github.com/laravel/laravel/compare/8.x...9.x)
* [Laravel Shift](https://laravelshift.com) — service communautaire d'automatisation des mises à niveau
* [Documentation Symfony Mailer](https://symfony.com/doc/6.0/mailer.html)
* [Documentation Flysystem 3](https://flysystem.thephpleague.com/v3/docs/)


## Related topics

- [Mise à niveau de Laravel 9 vers 10](/fr/blog/upgrade-9-to-10.md)
- [Guide de mise à niveau de Laravel 12 vers 13](/fr/blog/upgrade-12-to-13.md)
- [Mise à niveau de Laravel 10 à 11](/fr/blog/upgrade-10-to-11.md)
- [Guide de mise à niveau de Laravel 11 à 12](/fr/blog/upgrade-11-to-12.md)
- [Les nouveautés de Laravel 13](/fr/blog/laravel-13-new-features.md)
