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

# Guide de migration de l'ancienne vers la nouvelle structure

> Procédure pour migrer d'une ancienne structure d'application Laravel 10 vers le Slim Application Skeleton de Laravel 11+.

## Introduction

Ce guide décrit la procédure pour migrer d'une ancienne structure d'application Laravel 10 (avec les classes `Kernel` et de multiples service providers) vers le Slim Application Skeleton de Laravel 11+.

<Warning>
  **La documentation officielle ne recommande pas cette migration.**

  * L'ancienne structure Laravel 10 **continue de fonctionner telle quelle** sur Laravel 11+. Aucun retrait de support n'est prévu, y compris pour la version actuelle Laravel 13.
  * La migration est **totalement facultative**. Elle n'est ni imposée, ni recommandée.
  * Si vous ne comprenez pas en profondeur les différences entre l'ancienne et la nouvelle structure, il est fortement conseillé d'éviter la migration. C'est une opération destinée aux développeurs familiers avec l'intérieur du framework.
  * Avant la migration, **effectuez impérativement une sauvegarde** et assurez-vous que tous les tests passent.
</Warning>

## Quand la migration devient nécessaire

Vous pouvez envisager la migration dans les cas suivants :

* Vous voulez aligner votre projet sur la structure standard la plus récente pour que les nouveaux membres puissent se référer facilement à la documentation officielle
* Vous souhaitez maintenir la cohérence avec des packages ou des starter kits nouvellement créés sur Laravel 11+
* Vous voulez réduire les fichiers de configuration et les classes hérités de l'ancienne structure pour simplifier la base de code

## Prérequis

Ce guide part des hypothèses suivantes :

* **La mise à niveau de Laravel est terminée** (`laravel/framework ^11.0` ou supérieur)
* Tous les tests existants passent
* Vous comprenez la structure d'application Laravel 11+ (consultez [Structure d'application Laravel 11+](/fr/advanced/app-structure))

## Exemple de migration

Nous illustrons ici la procédure de migration d'un projet créé avec Laravel 10 + Breeze (stack Blade) vers la nouvelle structure d'application, tout en conservant Breeze.

<Steps>
  <Step title="Remplacer bootstrap/app.php">
    L'ancien `bootstrap/app.php` créait une instance `$app` et enregistrait les kernels. Nous le remplaçons par la chaîne `Application::configure()`.

    **Ancien (Laravel 10) :**

    ```php theme={null}
    <?php

    $app = new Illuminate\Foundation\Application(
        $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
    );

    $app->singleton(
        Illuminate\Contracts\Http\Kernel::class,
        App\Http\Kernel::class
    );

    $app->singleton(
        Illuminate\Contracts\Console\Kernel::class,
        App\Console\Kernel::class
    );

    $app->singleton(
        Illuminate\Contracts\Debug\ExceptionHandler::class,
        App\Exceptions\Handler::class
    );

    return $app;
    ```

    **Nouveau (Laravel 11+) :**

    ```php theme={null}
    <?php

    use Illuminate\Foundation\Application;
    use Illuminate\Foundation\Configuration\Exceptions;
    use Illuminate\Foundation\Configuration\Middleware;

    return Application::configure(basePath: dirname(__DIR__))
        ->withRouting(
            web: __DIR__.'/../routes/web.php',
            commands: __DIR__.'/../routes/console.php',
            health: '/up',
        )
        ->withMiddleware(function (Middleware $middleware) {
            //
        })
        ->withExceptions(function (Exceptions $exceptions) {
            //
        })->create();
    ```

    <Info>
      Les callbacks `withMiddleware()` et `withExceptions()` sont l'endroit où porter la configuration des fichiers Kernel que nous supprimerons aux étapes suivantes. Laissez-les vides pour l'instant, ils seront complétés dans les étapes ultérieures.
    </Info>
  </Step>

  <Step title="Supprimer le HTTP Kernel (app/Http/Kernel.php)">
    `app/Http/Kernel.php` définissait les middlewares globaux, les groupes de middlewares et les alias de middlewares.

    **Ancien (app/Http/Kernel.php) :**

    ```php theme={null}
    <?php

    namespace App\Http;

    use Illuminate\Foundation\Http\Kernel as HttpKernel;

    class Kernel extends HttpKernel
    {
        protected $middleware = [
            \App\Http\Middleware\TrustProxies::class,
            \Illuminate\Http\Middleware\HandleCors::class,
            \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
            \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
            \App\Http\Middleware\TrimStrings::class,
            \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        ];

        protected $middlewareGroups = [
            'web' => [
                \App\Http\Middleware\EncryptCookies::class,
                \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
                \Illuminate\Session\Middleware\StartSession::class,
                \Illuminate\View\Middleware\ShareErrorsFromSession::class,
                \App\Http\Middleware\VerifyCsrfToken::class,
                \Illuminate\Routing\Middleware\SubstituteBindings::class,
            ],
            'api' => [
                \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
                \Illuminate\Routing\Middleware\SubstituteBindings::class,
            ],
        ];

        protected $middlewareAliases = [
            'auth' => \App\Http\Middleware\Authenticate::class,
            // ...
        ];
    }
    ```

    Dans Laravel 11, les middlewares ci-dessus sont intégrés en interne dans le framework comme valeurs par défaut. Si vous n'avez **aucune** personnalisation, vous pouvez supprimer directement `app/Http/Kernel.php`.

    **Si vous avez des personnalisations** (ajouts ou exclusions de middlewares personnalisés), portez-les dans `withMiddleware()` de `bootstrap/app.php` avant de supprimer le fichier.

    ```php theme={null}
    ->withMiddleware(function (Middleware $middleware) {
        // Ajout d'un middleware global
        $middleware->append(\App\Http\Middleware\MyCustomMiddleware::class);

        // Ajout d'un middleware au groupe web
        $middleware->web(append: [
            \App\Http\Middleware\HandleInertiaRequests::class,
        ]);

        // Ajout d'alias de middlewares
        $middleware->alias([
            'role' => \App\Http\Middleware\CheckRole::class,
        ]);
    })
    ```

    Une fois le portage terminé, supprimez `app/Http/Kernel.php`.

    <Info>
      Dans Laravel 11, les classes de middlewares par défaut telles que `TrustProxies`, `EncryptCookies`, `VerifyCsrfToken` peuvent également être supprimées de `app/Http/Middleware/`. Ces classes étant intégrées au framework, les fichiers eux-mêmes deviennent inutiles s'il n'y a pas de personnalisation.
    </Info>
  </Step>

  <Step title="Supprimer le Console Kernel (app/Console/Kernel.php)">
    `app/Console/Kernel.php` était chargé de la définition des tâches planifiées et du chargement automatique des commandes.

    **Ancien (app/Console/Kernel.php) :**

    ```php theme={null}
    <?php

    namespace App\Console;

    use Illuminate\Console\Scheduling\Schedule;
    use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

    class Kernel extends ConsoleKernel
    {
        protected function schedule(Schedule $schedule): void
        {
            // $schedule->command('inspire')->hourly();
        }

        protected function commands(): void
        {
            $this->load(__DIR__.'/Commands');
            require base_path('routes/console.php');
        }
    }
    ```

    **Le chargement automatique des commandes** n'a plus besoin de `$this->load()` sous Laravel 11+ car le répertoire `app/Console/Commands/` est scanné automatiquement.

    **La définition des tâches planifiées** est migrée vers `routes/console.php` ou `withSchedule()` dans `bootstrap/app.php`.

    ```php theme={null}
    // routes/console.php (Laravel 11+)
    use Illuminate\Support\Facades\Schedule;

    Schedule::command('emails:send')->daily();
    ```

    Après migration, supprimez `app/Console/Kernel.php`.

    <Info>
      Ne supprimez pas les fichiers de commandes Artisan personnalisées présents dans `app/Console/`. Conservez ces fichiers de commandes et ne supprimez que le fichier de la classe Kernel.
    </Info>
  </Step>

  <Step title="Supprimer le gestionnaire d'exceptions (app/Exceptions/Handler.php)">
    `app/Exceptions/Handler.php` était chargé de la configuration du reporting et du rendu des exceptions.

    **Ancien (app/Exceptions/Handler.php) :**

    ```php theme={null}
    <?php

    namespace App\Exceptions;

    use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
    use Throwable;

    class Handler extends ExceptionHandler
    {
        protected $dontFlash = [
            'current_password',
            'password',
            'password_confirmation',
        ];

        public function register(): void
        {
            $this->reportable(function (Throwable $e) {
                //
            });
        }
    }
    ```

    Si vous avez des personnalisations, portez-les dans `withExceptions()` de `bootstrap/app.php` avant de le supprimer.

    ```php theme={null}
    use Throwable;
    use Illuminate\Http\Request;

    ->withExceptions(function (Exceptions $exceptions) {
        // Personnalisation du reporting d'exceptions
        $exceptions->report(function (Throwable $e) {
            // ...
        });

        // Personnalisation du rendu d'exceptions
        $exceptions->render(function (\App\Exceptions\InvalidOrderException $e, Request $request) {
            return response()->view('errors.invalid-order', status: 500);
        });
    })
    ```

    Si vous aviez ajouté vos propres entrées à `$dontFlash`, elles se portent de la même façon.

    ```php theme={null}
    ->withExceptions(function (Exceptions $exceptions) {
        $exceptions->dontFlash([
            'my_sensitive_field',
        ]);
    })
    ```

    Une fois le portage terminé, supprimez `app/Exceptions/Handler.php`.
  </Step>

  <Step title="Supprimer RouteServiceProvider et migrer l'enregistrement des routes">
    `app/Providers/RouteServiceProvider.php` s'occupait du chargement des fichiers de routes et de la configuration du rate limiting.

    **Ancien (app/Providers/RouteServiceProvider.php) :**

    ```php theme={null}
    <?php

    namespace App\Providers;

    use Illuminate\Cache\RateLimiting\Limit;
    use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\RateLimiter;
    use Illuminate\Support\Facades\Route;

    class RouteServiceProvider extends ServiceProvider
    {
        public const HOME = '/home';

        public function boot(): void
        {
            RateLimiter::for('api', function (Request $request) {
                return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
            });

            $this->routes(function () {
                Route::middleware('api')
                    ->prefix('api')
                    ->group(base_path('routes/api.php'));

                Route::middleware('web')
                    ->group(base_path('routes/web.php'));
            });
        }
    }
    ```

    **L'enregistrement des fichiers de routes** est migré vers `withRouting()` dans `bootstrap/app.php`.

    ```php theme={null}
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php', // si vous utilisez les routes API
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ```

    **Le rate limiting** est migré vers `AppServiceProvider::boot()`.

    ```php theme={null}
    // app/Providers/AppServiceProvider.php
    use Illuminate\Cache\RateLimiting\Limit;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\RateLimiter;

    public function boot(): void
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
        });
    }
    ```

    Si vous utilisez la constante `HOME` quelque part, remplacez-la par la chaîne d'URL directement ou déplacez la constante dans `AppServiceProvider`.

    Après migration, supprimez `app/Providers/RouteServiceProvider.php`.
  </Step>

  <Step title="Organiser les service providers">
    Laravel 10 fournissait par défaut 5 service providers. Nous les consolidons dans un seul `AppServiceProvider.php`.

    **Providers à supprimer (déplacer le contenu dans AppServiceProvider avant suppression) :**

    | Fichier                                      | Destination                                                                  |
    | -------------------------------------------- | ---------------------------------------------------------------------------- |
    | `app/Providers/AuthServiceProvider.php`      | `Gate::policy()` etc. dans `AppServiceProvider::boot()`                      |
    | `app/Providers/BroadcastServiceProvider.php` | Si vous utilisez Broadcasting, `withBroadcasting()` dans `bootstrap/app.php` |
    | `app/Providers/EventServiceProvider.php`     | `Event::listen()` etc. dans `AppServiceProvider::boot()`                     |
    | `app/Providers/RouteServiceProvider.php`     | Traité à l'étape précédente                                                  |

    **Exemple de migration de l'ancien app/Providers/AuthServiceProvider.php :**

    ```php theme={null}
    // Ancien : app/Providers/AuthServiceProvider.php
    use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

    class AuthServiceProvider extends ServiceProvider
    {
        protected $policies = [
            Post::class => PostPolicy::class,
        ];

        public function boot(): void
        {
            $this->registerPolicies();
        }
    }
    ```

    ```php theme={null}
    // Nouveau : app/Providers/AppServiceProvider.php
    // Si vous respectez la convention de nommage standard de Laravel, la détection est automatique et cela n'est pas nécessaire
    use Illuminate\Support\Facades\Gate;

    class AppServiceProvider extends ServiceProvider
    {
        public function boot(): void
        {
            Gate::policy(Post::class, PostPolicy::class);
        }
    }
    ```

    Une fois les fichiers de providers inutiles supprimés, supprimez le tableau `providers` dans `config/app.php`.

    ```php theme={null}
    // Tableau providers de config/app.php
    // Vous pouvez tout supprimer
    'providers' => ServiceProvider::defaultProviders()->merge([
    App\Providers\AppServiceProvider::class,
    // Supprimez également les entrées des providers supprimés
    // App\Providers\AuthServiceProvider::class, // supprimer
    // App\Providers\EventServiceProvider::class, // supprimer
    // App\Providers\RouteServiceProvider::class, // supprimer
    ])->toArray(),
    ```

    Créez ensuite `bootstrap/providers.php` pour prendre en charge la nouvelle structure.

    ```php theme={null}
    <?php

    // bootstrap/providers.php
    return [
        App\Providers\AppServiceProvider::class,
    ];
    ```

    <Info>
      Si `bootstrap/providers.php` existe, Laravel le charge en priorité comme liste des providers.
    </Info>
  </Step>

  <Step title="Mettre à jour la classe de base des contrôleurs">
    La classe de base `Controller` de Laravel 10 utilisait les traits `AuthorizesRequests` et `ValidatesRequests`. La nouvelle classe de base de Laravel 11 est une classe abstraite simple qui n'utilise pas ces traits.

    **Ancien (Laravel 10) :**

    ```php theme={null}
    <?php

    namespace App\Http\Controllers;

    use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
    use Illuminate\Foundation\Validation\ValidatesRequests;
    use Illuminate\Routing\Controller as BaseController;

    class Controller extends BaseController
    {
        use AuthorizesRequests, ValidatesRequests;
    }
    ```

    **Nouveau (Laravel 11+) :**

    ```php theme={null}
    <?php

    namespace App\Http\Controllers;

    abstract class Controller
    {
        //
    }
    ```

    Les fonctionnalités fournies par les traits se remplacent ainsi :

    | Ancienne méthode (via trait)            | Nouvelle méthode                                                                                                                   |
    | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
    | `$this->validate($request, $rules)`     | `$request->validate($rules)`                                                                                                       |
    | `$this->authorize('update', $post)`     | `Gate::authorize('update', $post)`                                                                                                 |
    | `$this->authorizeResource(Post::class)` | Il n'y a pas de remplacement simple sous Laravel 11 ; conservez `App\Http\Controllers\Controller` avec la spécification Laravel 10 |

    Si des contrôleurs existants utilisent les méthodes des traits, vous pouvez soit corriger chaque contrôleur, soit conserver les traits dans la classe de base `Controller`. Il n'est pas obligatoire de tout changer d'un coup.

    <Warning>
      Si les contrôleurs d'authentification générés par Breeze ou Jetstream appellent `$this->validate()` ou `$this->authorize()`, vérifiez impérativement le comportement avant de changer la classe de base.
    </Warning>
  </Step>

  <Step title="Supprimer les fichiers config inutiles">
    Les fichiers comme `config/cors.php`, `config/hashing.php`, `config/view.php` qui n'ont pas été modifiés par rapport à la version par défaut peuvent être supprimés. Conservez les fichiers que vous avez modifiés.
  </Step>

  <Step title="Mettre à jour public/index.php">
    Puisqu'il a été modifié pour la nouvelle structure, réécrivez-le entièrement.

    ```php theme={null}
    <?php

    use Illuminate\Foundation\Application;
    use Illuminate\Http\Request;

    define('LARAVEL_START', microtime(true));

    // Determine if the application is in maintenance mode...
    if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
    require $maintenance;
    }

    // Register the Composer autoloader...
    require __DIR__.'/../vendor/autoload.php';

    // Bootstrap Laravel and handle the request...
    /** @var Application $app */
    $app = require_once __DIR__.'/../bootstrap/app.php';

    $app->handleRequest(Request::capture());
    ```
  </Step>

  <Step title="Mettre à jour artisan">
    Le fichier artisan doit être entièrement réécrit de la même façon.

    ```php theme={null}
    #!/usr/bin/env php
    <?php

    use Illuminate\Foundation\Application;
    use Symfony\Component\Console\Input\ArgvInput;

    define('LARAVEL_START', microtime(true));

    // Register the Composer autoloader...
    require __DIR__.'/vendor/autoload.php';

    // Bootstrap Laravel and handle the command...
    /** @var Application $app */
    $app = require_once __DIR__.'/bootstrap/app.php';

    $status = $app->handleCommand(new ArgvInput);

    exit($status);
    ```
  </Step>

  <Step title="Mettre à jour tests/TestCase.php">
    Le trait `CreatesApplication` n'est plus nécessaire, donc modifiez-le.
    Vous pouvez supprimer `tests/CreatesApplication.php`.

    ```php theme={null}
    <?php

    namespace Tests;

    use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

    abstract class TestCase extends BaseTestCase
    {
        //
    }
    ```
  </Step>

  <Step title="Mettre à jour .env, .env.example, phpunit.xml">
    Comme `CACHE_DRIVER` est devenu `CACHE_STORE` et que des entrées ont été ajoutées, modifiez-les si nécessaire.
    Ces modifications concernent aussi les fichiers de config et l'environnement de production, alors procédez avec prudence.
    Il n'est pas obligatoire de suivre ces changements à la lettre.
  </Step>

  <Step title="Vérification du fonctionnement">
    Une fois la migration terminée, procédez aux vérifications suivantes dans cet ordre.

    ```shell theme={null}
    # Vider les caches de configuration
    php artisan config:clear
    php artisan route:clear
    php artisan cache:clear

    # Vérifier que les routes sont correctement enregistrées
    php artisan route:list

    # Exécuter les tests
    php artisan test
    ```

    En cas de problème, restaurez les fichiers supprimés depuis votre sauvegarde et examinez les messages d'erreur.
  </Step>
</Steps>

## Structure de fichiers après migration

Après la migration, le projet aura la structure suivante (les changements par rapport à l'ancienne structure sont indiqués) :

```
app/
├── Console/
│   └── Commands/          ← Les commandes personnalisées sont conservées
│   (Kernel.php est supprimé)
├── Exceptions/
│   (Handler.php est supprimé)
├── Http/
│   ├── Controllers/
│   │   └── Controller.php ← Modifié en une classe abstraite simple
│   └── Middleware/        ← Les middlewares personnalisés sont conservés
│   (Kernel.php est supprimé)
├── Models/
└── Providers/
    └── AppServiceProvider.php ← Consolidé en un seul fichier
    (AuthServiceProvider.php est supprimé)
    (BroadcastServiceProvider.php est supprimé)
    (EventServiceProvider.php est supprimé)
    (RouteServiceProvider.php est supprimé)
bootstrap/
├── app.php                ← Remplacé par le format Application::configure()
└── providers.php          ← Nouvellement créé
routes/
├── web.php
├── api.php                ← Uniquement si nécessaire
└── console.php            ← Les définitions de tâches planifiées vont ici
```

## Récapitulatif

| Contenu migré                    | Ancien emplacement                       | Nouvel emplacement                                                   |
| -------------------------------- | ---------------------------------------- | -------------------------------------------------------------------- |
| Middlewares HTTP                 | `app/Http/Kernel.php`                    | `withMiddleware()` dans `bootstrap/app.php`                          |
| Gestion des exceptions           | `app/Exceptions/Handler.php`             | `withExceptions()` dans `bootstrap/app.php`                          |
| Définition des tâches planifiées | `app/Console/Kernel.php`                 | `routes/console.php`                                                 |
| Enregistrement des routes        | `app/Providers/RouteServiceProvider.php` | `withRouting()` dans `bootstrap/app.php`                             |
| Liste des service providers      | Tableau `providers` de `config/app.php`  | `bootstrap/providers.php`                                            |
| Enregistrement des policies      | `app/Providers/AuthServiceProvider.php`  | Enregistrement automatique. Manuel avec `AppServiceProvider::boot()` |
| Event listeners                  | `app/Providers/EventServiceProvider.php` | Enregistrement automatique. Manuel avec `AppServiceProvider::boot()` |

## Liens de référence

* [Structure d'application Laravel 11+](/fr/advanced/app-structure)
* [FAQ nouvelle structure](/fr/advanced/app-structure-faq)
* [Guide officiel de mise à niveau (11.x)](https://github.com/laravel/docs/blob/11.x/upgrade.md)
* [Diff dépôt laravel/laravel 10.x → 11.x](https://github.com/laravel/laravel/compare/10.x...11.x)
* [Controller.php de Laravel 10](https://github.com/laravel/laravel/blob/10.x/app/Http/Controllers/Controller.php)
* [Controller.php de Laravel 11](https://github.com/laravel/laravel/blob/11.x/app/Http/Controllers/Controller.php)


## Related topics

- [Guide de migration de laravel/ui vers Fortify](/fr/blog/ui-to-fortify.md)
- [FAQ sur la nouvelle structure d'application Laravel 11+](/fr/advanced/app-structure-faq.md)
- [Gestion de la compatibilité de versions de package](/fr/advanced/package-versioning.md)
- [Cas d'usage pratiques de Laravel Pennant](/fr/blog/laravel-pennant.md)
- [Guide de mise à niveau de Laravel 12 vers 13](/fr/blog/upgrade-12-to-13.md)
