> ## 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-TW/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="控制器中無法使用 $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="控制器中無法使用 $this->middleware()">
    `$this->middleware()` 是 `Illuminate\Routing\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]` Attribute。

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

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

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

  <Accordion title="沒有 $this->authorizeResource()">
    `authorizeResource()` 依賴 `AuthorizesRequests` trait，因此在新結構中空的基底控制器無法使用。

    **對應方法有幾種。**

    **1. 將基底控制器恢復為 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]` Attribute**

    ```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`，排程的設定位置也已變更。

    **寫在 `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 新增服務提供者">
    新結構中，服務提供者的清單管理從 `config/app.php` 改為 `bootstrap/providers.php`。

    ```php theme={null}
    // bootstrap/providers.php
    return [
        App\Providers\AppServiceProvider::class,
        App\Providers\MyCustomServiceProvider::class, // ← 在此加入
    ];
    ```

    以 `artisan make: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()` 方法可將預設提供者替換為自訂實作。
  </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) 設定
    * `bootstrap/app.php` 中的 API 路由註冊
  </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-TW/advanced/app-structure-migration)。
  </Accordion>
</AccordionGroup>

## 相關頁面

<Card title="Laravel 11 以後的應用程式結構" icon="sitemap" href="/zh-TW/advanced/app-structure">
  完整說明新的應用程式結構全貌與 `ApplicationBuilder` 的內部實作。
</Card>

<Card title="從舊結構到新結構的遷移指南" icon="arrow-right" href="/zh-TW/advanced/app-structure-migration">
  說明從 Laravel 10 的舊應用程式結構遷移至 Laravel 11 以後新結構的步驟。
</Card>


## Related topics

- [Laravel 11 以後的應用程式結構](/zh-TW/advanced/app-structure.md)
- [從舊結構到新結構的遷移指南](/zh-TW/advanced/app-structure-migration.md)
- [從 Laravel 10 升級到 11](/zh-TW/blog/upgrade-10-to-11.md)
- [目錄結構](/zh-TW/directory-structure.md)
- [Laravel Boost](/zh-TW/boost.md)
