Passer au contenu principal

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

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+)

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

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

$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

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();
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.
2

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

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

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

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.
// routes/console.php (Laravel 11+)
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send')->daily();
Après migration, supprimez app/Console/Kernel.php.
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.
4

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

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.
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.
->withExceptions(function (Exceptions $exceptions) {
    $exceptions->dontFlash([
        'my_sensitive_field',
    ]);
})
Une fois le portage terminé, supprimez app/Exceptions/Handler.php.
5

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

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.
->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().
// 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.
6

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) :
FichierDestination
app/Providers/AuthServiceProvider.phpGate::policy() etc. dans AppServiceProvider::boot()
app/Providers/BroadcastServiceProvider.phpSi vous utilisez Broadcasting, withBroadcasting() dans bootstrap/app.php
app/Providers/EventServiceProvider.phpEvent::listen() etc. dans AppServiceProvider::boot()
app/Providers/RouteServiceProvider.phpTraité à l’étape précédente
Exemple de migration de l’ancien app/Providers/AuthServiceProvider.php :
// 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();
    }
}
// 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.
// 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

// bootstrap/providers.php
return [
    App\Providers\AppServiceProvider::class,
];
Si bootstrap/providers.php existe, Laravel le charge en priorité comme liste des providers.
7

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

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

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

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

Mettre à jour public/index.php

Puisqu’il a été modifié pour la nouvelle structure, réécrivez-le entièrement.
<?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());
10

Mettre à jour artisan

Le fichier artisan doit être entièrement réécrit de la même façon.
#!/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);
11

Mettre à jour tests/TestCase.php

Le trait CreatesApplication n’est plus nécessaire, donc modifiez-le. Vous pouvez supprimer tests/CreatesApplication.php.
<?php

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    //
}
12

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

Vérification du fonctionnement

Une fois la migration terminée, procédez aux vérifications suivantes dans cet ordre.
# 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.

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 emplacementNouvel emplacement
Middlewares HTTPapp/Http/Kernel.phpwithMiddleware() dans bootstrap/app.php
Gestion des exceptionsapp/Exceptions/Handler.phpwithExceptions() dans bootstrap/app.php
Définition des tâches planifiéesapp/Console/Kernel.phproutes/console.php
Enregistrement des routesapp/Providers/RouteServiceProvider.phpwithRouting() dans bootstrap/app.php
Liste des service providersTableau providers de config/app.phpbootstrap/providers.php
Enregistrement des policiesapp/Providers/AuthServiceProvider.phpEnregistrement automatique. Manuel avec AppServiceProvider::boot()
Event listenersapp/Providers/EventServiceProvider.phpEnregistrement automatique. Manuel avec AppServiceProvider::boot()

Liens de référence

Dernière modification le 13 juillet 2026