跳转到主要内容

前言

本指南讲解如何将 Laravel 10 之前的旧应用结构(拥有 Kernel 类以及多个 Service Provider 的结构)迁移到 Laravel 11 之后的 Slim Application Skeleton。
官方文档并不推荐进行此迁移。
  • Laravel 10 的旧结构在 Laravel 11 之后仍然照常运行。截至包含 Laravel 13 的当前版本,都没有终止支持的计划。
  • 迁移完全是可选的,既不是强制也不是推荐。
  • 如果你没有深入理解新旧结构的差异,强烈建议不要进行迁移。这是面向精通框架内部的开发者的工作。
  • 迁移前务必做好备份,并确保所有测试都能通过。

何时需要迁移

以下情况下可以考虑迁移:
  • 希望让项目符合最新的标准结构,方便新成员对照官方文档
  • 希望与 Laravel 11 之后新建的包或 Starter Kit 保持结构一致
  • 希望减少沿袭自旧结构的配置文件和类,让代码库更简洁

前置条件

本指南假设:
  • Laravel 版本升级已完成laravel/framework ^11.0 及以上)
  • 现有测试全部通过
  • 已理解 Laravel 11 之后的应用结构(参考Laravel 11 之后的应用结构

迁移示例

下面演示如何在保留 Breeze(Blade 栈)的前提下,仅将 Laravel 10 + Breeze 项目的应用结构迁移到新结构。
1

替换 bootstrap/app.php

旧的 bootstrap/app.php 会创建 $app 实例并注册 Kernel。我们将它替换为 Application::configure() 的链式调用。旧(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;
新(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();
withMiddleware()withExceptions() 的回调是接收下一步将要删除的 Kernel 文件设置的地方。先保持为空,在后续步骤中再补充。
2

删除 HTTP Kernel(app/Http/Kernel.php)

app/Http/Kernel.php 中定义了全局中间件、中间件组以及中间件别名。旧(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,
        // ...
    ];
}
Laravel 11 中,上述中间件已作为默认值内置于框架中。如果没有自定义,可以直接删除 app/Http/Kernel.php如果有自定义(添加或移除了自定义中间件),请先将其迁移到 bootstrap/app.phpwithMiddleware() 后再删除。
->withMiddleware(function (Middleware $middleware) {
    // 添加全局中间件
    $middleware->append(\App\Http\Middleware\MyCustomMiddleware::class);

    // 向 web 组添加中间件
    $middleware->web(append: [
        \App\Http\Middleware\HandleInertiaRequests::class,
    ]);

    // 添加中间件别名
    $middleware->alias([
        'role' => \App\Http\Middleware\CheckRole::class,
    ]);
})
迁移完成后,删除 app/Http/Kernel.php
在 Laravel 11 中,TrustProxiesEncryptCookiesVerifyCsrfToken 等默认中间件类也可以从 app/Http/Middleware/ 中删除。这些类已内置在框架侧,如果不需要自定义,文件本身也可以不保留。
3

删除 Console Kernel(app/Console/Kernel.php)

app/Console/Kernel.php 负责定义计划任务并自动加载命令。旧(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');
    }
}
命令的自动加载在 Laravel 11 以后由框架自动扫描 app/Console/Commands/ 目录,因此不再需要写 $this->load()计划任务的定义可以迁移到 routes/console.phpbootstrap/app.phpwithSchedule()
// routes/console.php(Laravel 11 以后)
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send')->daily();
迁移完成后,删除 app/Console/Kernel.php
请不要删除 app/Console/ 目录内的自定义 Artisan 命令文件。命令文件保持不动,仅删除 Kernel 类文件即可。
4

删除异常处理器(app/Exceptions/Handler.php)

app/Exceptions/Handler.php 负责异常上报与渲染的配置。旧(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) {
            //
        });
    }
}
如果有自定义内容,请先迁移到 bootstrap/app.phpwithExceptions() 再删除。
use Throwable;
use Illuminate\Http\Request;

->withExceptions(function (Exceptions $exceptions) {
    // 异常上报的自定义
    $exceptions->report(function (Throwable $e) {
        // ...
    });

    // 异常渲染的自定义
    $exceptions->render(function (\App\Exceptions\InvalidOrderException $e, Request $request) {
        return response()->view('errors.invalid-order', status: 500);
    });
})
$dontFlash 添加过自定义字段的话,也可以按相同方式迁移。
->withExceptions(function (Exceptions $exceptions) {
    $exceptions->dontFlash([
        'my_sensitive_field',
    ]);
})
迁移完成后,删除 app/Exceptions/Handler.php
5

删除 RouteServiceProvider 并迁移路由注册

app/Providers/RouteServiceProvider.php 负责路由文件的加载与限流的配置。旧(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'));
        });
    }
}
路由文件的注册迁移到 bootstrap/app.phpwithRouting()
->withRouting(
    web: __DIR__.'/../routes/web.php',
    api: __DIR__.'/../routes/api.php', // 使用 API 路由时
    commands: __DIR__.'/../routes/console.php',
    health: '/up',
)
限流迁移到 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());
    });
}
如果有地方使用了 HOME 常量,请直接替换成 URL 字符串,或将常量迁移到 AppServiceProvider迁移完成后,删除 app/Providers/RouteServiceProvider.php
6

整理 Service Provider

Laravel 10 默认提供了 5 个 Service Provider。请将它们整合到 AppServiceProvider.php 一个文件中。要删除的 Provider(将内容迁移到 AppServiceProvider 后删除):
文件迁移目的地
app/Providers/AuthServiceProvider.phpAppServiceProvider::boot() 中的 Gate::policy()
app/Providers/BroadcastServiceProvider.php若使用广播,则 bootstrap/app.phpwithBroadcasting()
app/Providers/EventServiceProvider.phpAppServiceProvider::boot() 中的 Event::listen()
app/Providers/RouteServiceProvider.php已在上一步处理
迁移旧 app/Providers/AuthServiceProvider.php 内容的示例:
// 旧: 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();
    }
}
// 新: app/Providers/AppServiceProvider.php
// 如果遵循 Laravel 的标准命名约定,会自动检测,无需此步骤
use Illuminate\Support\Facades\Gate;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Gate::policy(Post::class, PostPolicy::class);
    }
}
删除不再需要的 Provider 文件后,删除 config/app.php 中的 providers 数组。
// config/app.php 的 providers 数组
// 全部删除即可
'providers' => ServiceProvider::defaultProviders()->merge([
App\Providers\AppServiceProvider::class,
// 已删除的 Provider 也从这里移除
// App\Providers\AuthServiceProvider::class, // 删除
// App\Providers\EventServiceProvider::class, // 删除
// App\Providers\RouteServiceProvider::class, // 删除
])->toArray(),
接着创建 bootstrap/providers.php 以适配新结构。
<?php

// bootstrap/providers.php
return [
    App\Providers\AppServiceProvider::class,
];
bootstrap/providers.php 存在时,Laravel 会将其作为 Provider 列表优先加载。
7

更新 Controller 基类

Laravel 10 的 Controller 基类使用了 AuthorizesRequestsValidatesRequests trait。Laravel 11 的新基类是不包含这些 trait 的简单抽象类。旧(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;
}
新(Laravel 11 以后):
<?php

namespace App\Http\Controllers;

abstract class Controller
{
    //
}
trait 提供的功能按以下方式替代:
旧方式(通过 trait)新方式
$this->validate($request, $rules)$request->validate($rules)
$this->authorize('update', $post)Gate::authorize('update', $post)
$this->authorizeResource(Post::class)Laravel 11 中没有简单的替代方式,可保留 App\Http\Controllers\Controller 为 Laravel 10 的形式
如果现有 Controller 使用了 trait 的方法,可以选择修改每个 Controller,或者在 Controller 基类中保留这些 trait。不必一次性全部改完也能运行。
如果 Breeze 或 Jetstream 生成的认证 Controller 使用了 $this->validate()$this->authorize(),请在修改基类之前务必进行动作验证。
8

删除不需要的 config 文件

config/cors.phpconfig/hashing.phpconfig/view.php 等未从默认值修改的文件可以删除。有修改的文件请保留。
9

更新 public/index.php

由于随新结构做了改动,需要整体重写。
<?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

更新 artisan

artisan 文件也同样需要全部重写。
#!/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

更新 tests/TestCase.php

由于不再需要 CreatesApplication trait,需要做调整。 tests/CreatesApplication.php 可以删除。
<?php

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    //
}
12

更新 .env、.env.example、phpunit.xml

CACHE_DRIVER 变为 CACHE_STORE 等,条目也有增删,如有需要请调整。 这类改动涉及 config 文件和生产环境,请谨慎处理。 不必强行跟进。
13

验证

迁移完成后,按以下顺序进行验证。
# 清除配置缓存
php artisan config:clear
php artisan route:clear
php artisan cache:clear

# 确认路由是否正确注册
php artisan route:list

# 运行测试
php artisan test
遇到问题时,请从备份中恢复被删除的文件并查看错误信息。

迁移后的文件结构

迁移后的项目结构大致如下(展示相对旧结构的变化)。
app/
├── Console/
│   └── Commands/          ← 自定义命令保持不变
│   (Kernel.php 已删除)
├── Exceptions/
│   (Handler.php 已删除)
├── Http/
│   ├── Controllers/
│   │   └── Controller.php ← 改为简洁的抽象类
│   └── Middleware/        ← 自定义中间件保持不变
│   (Kernel.php 已删除)
├── Models/
└── Providers/
    └── AppServiceProvider.php ← 统一到 1 个文件
    (AuthServiceProvider.php 已删除)
    (BroadcastServiceProvider.php 已删除)
    (EventServiceProvider.php 已删除)
    (RouteServiceProvider.php 已删除)
bootstrap/
├── app.php                ← 替换为 Application::configure() 形式
└── providers.php          ← 新建
routes/
├── web.php
├── api.php                ← 仅在需要时保留
└── console.php            ← 计划任务定义也写在这里

小结

迁移内容旧位置新位置
HTTP 中间件app/Http/Kernel.phpbootstrap/app.phpwithMiddleware()
异常处理app/Exceptions/Handler.phpbootstrap/app.phpwithExceptions()
计划任务定义app/Console/Kernel.phproutes/console.php
路由注册app/Providers/RouteServiceProvider.phpbootstrap/app.phpwithRouting()
Service Provider 列表config/app.phpproviders 数组bootstrap/providers.php
Policy 注册app/Providers/AuthServiceProvider.php自动注册。或在 AppServiceProvider::boot() 中手动注册
事件监听器app/Providers/EventServiceProvider.php自动注册。或在 AppServiceProvider::boot() 中手动注册

参考链接

最后修改于 2026年7月13日