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

# Laravel 11 之后新应用结构 FAQ

> 汇总迁移到 Laravel 11 之后 Slim Application Skeleton 时的常见疑问与解答。适合中途加入仍保留旧结构升级项目的开发者，或依据旧版书籍、教程学习的开发者。

Laravel 11 以「Slim Application Skeleton」为名，大幅调整了应用结构。当你中途加入一个仍以旧结构升级的项目，或以 Laravel 10 及更早的书籍、教程学习时，容易感到困惑。本文汇总的正是 Laravel 11 发布时在 Laracasts 论坛和 Stack Overflow 上最常被问到的问题。

<Info>
  本 FAQ 面向**新项目**（Laravel 11 及以上）。既有项目的升级步骤请参考[迁移指南](/zh/advanced/app-structure-migration)。
</Info>

<AccordionGroup>
  <Accordion title="config 文件变少了">
    新结构从项目中删除了变更频率较低的 config 文件，这些文件转而使用框架内的 `config/`。

    项目侧的 `config/` 与框架侧的 `config/` 会进行合并，**以项目侧为优先**。只有在需要自定义时才创建对应文件，即可生效。

    ```bash theme={null}
    # 查看框架内的 config
    cat vendor/laravel/framework/config/cors.php

    # 复制到项目侧进行自定义
    php artisan config:publish cors
    ```
  </Accordion>

  <Accordion title="Controller 中不能使用 $this->validate() 或 $this->authorize()">
    在 Laravel 11 的新结构中，`App\Http\Controllers\Controller` 是一个空类，不再继承 `Illuminate\Routing\Controller`，也不再使用 `ValidatesRequests` / `AuthorizesRequests` trait。

    | Laravel 10                         | Laravel 11 |
    | ---------------------------------- | ---------- |
    | 继承 `Illuminate\Routing\Controller` | 空类         |
    | 使用 `ValidatesRequests` trait       | 无          |
    | 使用 `AuthorizesRequests` trait      | 无          |

    参考：[Laravel 10 的 Controller](https://github.com/laravel/laravel/blob/10.x/app/Http/Controllers/Controller.php) vs [Laravel 11 的 Controller](https://github.com/laravel/laravel/blob/11.x/app/Http/Controllers/Controller.php)

    **替代方案：**

    ```php theme={null}
    // 使用 $request->validate()
    public function store(Request $request)
    {
        $validated = $request->validate([
            'title' => 'required|string|max:255',
        ]);
    }

    // 使用 Gate::authorize()
    use Illuminate\Support\Facades\Gate;

    public function update(Request $request, Post $post)
    {
        Gate::authorize('update', $post);
    }
    ```

    如果使用频繁，也可以把 `App\Http\Controllers\Controller` 恢复为 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;

    abstract class Controller extends BaseController
    {
        use AuthorizesRequests, ValidatesRequests;
    }
    ```
  </Accordion>

  <Accordion title="没有 app/Http/Middleware 无法修改中间件配置 / 没有 app/Http/Kernel.php">
    新结构中 `app/Http/Kernel.php` 已被废弃，中间件配置改在 `bootstrap/app.php` 的 `withMiddleware()` 中进行。

    ```php theme={null}
    // bootstrap/app.php
    ->withMiddleware(function (Middleware $middleware) {
        // TrustProxies 等中间件所配置的项目都可以通过 Middleware 类的方法配置
        $middleware->trustProxies(at: '*');

        // 添加全局中间件
        $middleware->append(\App\Http\Middleware\MyMiddleware::class);

        // 为路由组配置别名
        $middleware->alias([
            'my-middleware' => \App\Http\Middleware\MyMiddleware::class,
        ]);

        // 自定义 Web / API 中间件组
        $middleware->web(append: [
            \App\Http\Middleware\HandleInertiaRequests::class,
        ]);
    })
    ```

    自定义中间件文件本身仍然创建在 `app/Http/Middleware/` 目录中。
  </Accordion>

  <Accordion title="Controller 中无法使用 $this->middleware()">
    `$this->middleware()` 是 `Illuminate\Routing\Controller` 的功能，新结构的空基类 Controller 中不可用。

    请改为实现 `HasMiddleware` 接口并定义 `middleware()` 方法。

    ```php theme={null}
    use Illuminate\Routing\Controllers\HasMiddleware;
    use Illuminate\Routing\Controllers\Middleware;

    class UserController extends Controller implements HasMiddleware
    {
        public static function middleware(): array
        {
            return [
                'auth',
                new Middleware('log', only: ['index']),
                new Middleware('subscribed', except: ['index']),
            ];
        }
    }
    ```

    Laravel 13 以后也可以使用 `#[Middleware]` 属性。

    ```php theme={null}
    use Illuminate\Routing\Controllers\HasMiddleware;
    use App\Http\Middleware\EnsureTokenIsValid;

    #[Middleware(EnsureTokenIsValid::class)]
    class UserController extends Controller
    {
        // ...
    }
    ```

    参考：[Controller 中间件](https://laravel.com/docs/13.x/controllers#controller-middleware)
  </Accordion>

  <Accordion title="没有 $this->authorizeResource()">
    `authorizeResource()` 依赖 `AuthorizesRequests` trait，因此在新结构的空基类 Controller 中不可用。

    **有几种应对方式。**

    **1. 把基类 Controller 恢复为 Laravel 10 的形式（最简便）**

    ```php theme={null}
    // app/Http/Controllers/Controller.php
    use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
    use Illuminate\Routing\Controller as BaseController;

    abstract class Controller extends BaseController
    {
        use AuthorizesRequests;
    }
    ```

    这样就可以使用 `$this->authorizeResource()`。

    **2. 在每个方法上添加 Laravel 13 的 `#[Authorize]` 属性**

    ```php theme={null}
    use Illuminate\Routing\Attributes\Controllers\Authorize;
    use App\Models\Post;

    class PostController extends Controller
    {
        #[Authorize('view', 'post')]
        public function show(Post $post) { /* ... */ }

        #[Authorize('update', 'post')]
        public function update(Request $request, Post $post) { /* ... */ }
    }
    ```
  </Accordion>

  <Accordion title="没有 app/Console/Kernel.php，无法配置 Schedule">
    新结构中 `app/Console/Kernel.php` 已被废弃，Schedule 的配置位置也发生了变化。

    **在 `routes/console.php` 中定义（推荐）：**

    ```php theme={null}
    // routes/console.php
    use Illuminate\Support\Facades\Schedule;

    Schedule::command('emails:send')->daily();
    Schedule::job(new SendEmails)->everyFiveMinutes();
    ```

    **在 `bootstrap/app.php` 中定义：**

    ```php theme={null}
    ->withSchedule(function (Schedule $schedule) {
        $schedule->command('emails:send')->daily();
    })
    ```
  </Accordion>

  <Accordion title="不知道如何注册事件和监听器">
    新结构废弃了 `EventServiceProvider`，事件与监听器的手动注册不再必需。

    **自动注册的机制：** 只要监听器类的 `handle()` 方法参数上以类型声明指定了事件类，即可自动注册。

    ```php theme={null}
    namespace App\Listeners;

    use App\Events\OrderShipped;

    class SendShipmentNotification
    {
        // 只要在 handle() 的参数中指定事件类即可自动注册
        public function handle(OrderShipped $event): void
        {
            // ...
        }
    }
    ```

    如果需要手动注册，可以在 `AppServiceProvider::boot()` 中处理。

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

    public function boot(): void
    {
        Event::listen(
            OrderShipped::class,
            SendShipmentNotification::class,
        );
    }
    ```
  </Accordion>

  <Accordion title="不知道如何向 config/app.php 添加 ServiceProvider">
    新结构中，ServiceProvider 的清单管理从 `config/app.php` 迁移到了 `bootstrap/providers.php`。

    ```php theme={null}
    // bootstrap/providers.php
    return [
        App\Providers\AppServiceProvider::class,
        App\Providers\MyCustomServiceProvider::class, // ← 添加到这里
    ];
    ```

    使用 `artisan make:provider` 创建的 Provider 会自动追加到此文件中。
  </Accordion>

  <Accordion title="没有 app/Exceptions/Handler.php">
    新结构中 `app/Exceptions/Handler.php` 已被废弃，异常处理的配置改在 `bootstrap/app.php` 的 `withExceptions()` 中进行。

    ```php theme={null}
    // bootstrap/app.php
    ->withExceptions(function (Exceptions $exceptions) {
        // 不上报特定异常
        $exceptions->dontReport(InvalidOrderException::class);

        // 自定义渲染
        $exceptions->render(function (InvalidOrderException $e, Request $request) {
            return response()->view('errors.invalid-order', status: 500);
        });

        // 修改未认证时的跳转目标
        $exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) {
            return $request->is('api/*');
        });
    })
    ```
  </Accordion>

  <Accordion title="想要自定义路由">
    路由的自定义在 `bootstrap/app.php` 的 `withRouting()` 中进行。

    **添加路由文件：**

    ```php theme={null}
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        then: function () {
            Route::middleware('web')
                ->prefix('admin')
                ->group(base_path('routes/admin.php'));
        },
    )
    ```

    **完全接管（与 Laravel 10 的 `RouteServiceProvider` 相同）：**

    指定 `using` 后，Laravel 默认的路由注册将完全被禁用，你可以自行控制所有路由。

    ```php theme={null}
    ->withRouting(
        using: function () {
            Route::middleware('web')
                ->group(base_path('routes/web.php'));

            Route::middleware('api')
                ->prefix('api')
                ->group(base_path('routes/api.php'));
        },
    )
    ```
  </Accordion>

  <Accordion title="想要替换 Laravel 默认的 ServiceProvider">
    向 `config/app.php` 添加 `providers` 键后，会与框架内的 `config/app.php` 合并生效。

    ```php theme={null}
    // config/app.php
    use Illuminate\Support\ServiceProvider;

    return [
        // ...
        'providers' => ServiceProvider::defaultProviders()->replace([
            Illuminate\Foo\FooServiceProvider::class => Bar\BarServiceProvider::class,
        ])->toArray(),
    ];
    ```

    通过 `replace()` 方法可以将默认 Provider 替换为自定义实现。
  </Accordion>

  <Accordion title="没有 routes/api.php">
    从 Laravel 11 起，API 功能默认不再包含，如需使用可单独安装。

    ```bash theme={null}
    php artisan install:api
    ```

    该命令会创建/配置以下内容：

    * `routes/api.php`
    * 用于 API 认证的 [Laravel Sanctum](https://laravel.com/docs/sanctum) 配置
    * 将 API 路由注册到 `bootstrap/app.php`
  </Accordion>

  <Accordion title="没有 routes/channels.php">
    从 Laravel 11 起，广播功能默认也不再包含，如需使用可单独安装。

    ```bash theme={null}
    php artisan install:broadcasting
    ```

    该命令会创建/配置以下内容：

    * `routes/channels.php`
    * [Laravel Reverb](https://laravel.com/docs/reverb) 的安装配置
    * 广播的配置文件
  </Accordion>

  <Accordion title="想要判断中途加入的项目使用新结构还是旧结构">
    请查看 `bootstrap/app.php` 的内容。

    **Laravel 11 之后的新结构（或已迁移）：**

    ```php theme={null}
    // bootstrap/app.php
    return Application::configure(basePath: dirname(__DIR__))
        ->withRouting(...)
        ->withMiddleware(...)
        ->withExceptions(...)
        ->create();
    ```

    **仍以 Laravel 10 之前的旧结构升级的项目：**

    ```php theme={null}
    // bootstrap/app.php
    $app = new Illuminate\Foundation\Application(
        $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
    );

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

    形如 `return Application::configure(...` 就是新结构，否则就是仍以旧结构升级的项目。将旧结构项目迁移到新结构的步骤请参考[迁移指南](/zh/advanced/app-structure-migration)。
  </Accordion>
</AccordionGroup>

## 相关页面

<Card title="Laravel 11 之后的应用结构" icon="sitemap" href="/zh/advanced/app-structure">
  详细讲解新应用结构的全貌以及 `ApplicationBuilder` 的内部实现。
</Card>

<Card title="旧结构到新结构的迁移指南" icon="arrow-right" href="/zh/advanced/app-structure-migration">
  讲解从 Laravel 10 的旧应用结构迁移到 Laravel 11 之后新结构的步骤。
</Card>


## Related topics

- [Laravel 11 之后的应用结构](/zh/advanced/app-structure.md)
- [旧结构到新结构的迁移指南](/zh/advanced/app-structure-migration.md)
- [目录结构](/zh/directory-structure.md)
- [Laravel 10 升级到 11](/zh/blog/upgrade-10-to-11.md)
- [Laravel 11 升级到 12 指南](/zh/blog/upgrade-11-to-12.md)
