Laravel
RoadMap
Documents
Architecture Concepts
- https://laravel.com/docs/12.x/lifecycle
- https://laravel.com/docs/12.x/container
- https://laravel.com/docs/12.x/providers
- https://laravel.com/docs/12.x/facades
核心筆記(0):Request Lifecycle
Laravel 的生命週期是一個由外部到核心,再由核心到外部的流程,Service Providers 是其中最關鍵的啟動環節。
- 啟動與核心 (First Steps & Kernels)
- 入口點: 所有請求都從 public/index.php 開始。您的 Web 伺服器(Nginx/Apache)配置會將所有流量導向此文件。
- 創建核心: index.php 載入 Composer Autoloader 後,從 bootstrap/app.php 獲取應用程式實例,並創建了 Service Container。(Create App)
- 核心處理: 請求被送至 HTTP Kernel (Illuminate\Foundation\Http\Kernel),它是所有請求流動的中央位置。
- Bootstrappers: Kernel 定義了一系列 bootstrappers(啟動程式),它們在請求執行前運行,負責配置錯誤處理、日誌記錄和環境偵測等框架內部配置。
- 服務提供者 (Service Providers) — 框架配置的中心
這是應用程式啟動的最重要步驟。
- 註冊 (Register): Kernel 實例化所有 Service Providers,並呼叫所有 Provider 的 register 方法。此階段負責將框架的各種組件(如資料庫、佇列、驗證等)綁定到 Service Container。(app->bind , app->singleton)
- 引導 (Boot): 在所有 register 方法執行完畢後,Laravel 才會呼叫所有 Provider 的 boot 方法。這確保了在執行啟動邏輯時,所有容器綁定都已完成。
關鍵性: 幾乎所有 Laravel 的主要功能都是由 Service Provider 啟動和配置的。
- 中介層與路由 (Middleware & Routing)
- 中介層堆疊 (Middleware Stack): 在請求到達路由之前,HTTP Kernel 會讓請求穿過全域中介層(例如處理 Session、CSRF 驗證)。
- 路由分派: 應用程式啟動 完畢後,Router 接手請求,將其分派給匹配的路由或控制器。
- 路由中介層: 路由器還會執行路由專屬的中介層(例如驗證使用者是否登入)。
- 控制器執行: 請求通過所有中介層後,執行路由或控制器方法,生成 Response。
- 結束與響應 (Finishing Up)
- 響應回傳: 控制器返回的 Response 會反向穿過所有路由中介層,讓中介層有機會在響應發出前進行最後的修改。
- 最終發送: HTTP Kernel 的 handle 方法返回 Response 給應用程式實例,最終由應用程式實例呼叫 send 方法,將響應內容發送給使用者的瀏覽器,完成整個生命週期。
核心筆記(1):Service Provider 與 Service Container
Service Provider
💡 比喻: 應用程式的 「總務處」或「啟動器」,負責組織和啟動所有服務。
php artisan make:provider ExampleServiceProvider
| 職責 | 說明 |
|---|---|
| 定義 | 這是 Laravel 啟動所有服務的中心點。 |
| 功能 | 告訴 Laravel 應該在什麼時候、如何綁定 (Binding) 各種應用程式元件。 |
兩個核心方法
| 方法 | 職責 (只能做什麼) | 時機 (何時執行) | 核心用途 |
|---|---|---|---|
register() | 只用於綁定服務到容器。不可使用其他服務、註冊路由或事件。 | 在 boot() 之前,所有 Provider 會先執行此方法。 | 將 介面 綁定到 具體實現。 |
boot() | 執行啟動邏輯。所有服務已註冊完成,可以安全地使用。 | 在所有 Provider 的 register() 完成後執行。 | 註冊路由、視圖、事件監聽器、中介層。 |
Service Container
💡 比喻: 一個 「智慧工廠」或「中央倉庫」,負責生產和交付所需的類別實例。
// class binding
$this->app->bind(\App\Contracts\PaymentGateway::class, \App\Services\StripeGateway::class);
// closure factory(每次解析都執行 closure)
$this->app->bind(\App\Contracts\PaymentGateway::class, function ($app) {
return new \App\Services\StripeGateway(config('services.stripe'));
});
// singleton(比較)
// 只在第一次解析時建立一次實例,之後每次都回傳相同物件
$this->app->singleton(\App\Services\ConfigManager::class, function ($app) {
return new \App\Services\ConfigManager(config('app'));
});
// 其他綁定方式
// instance:手動註冊已建立的物件
$service = new Transistor(new PodcastParser);
$this->app->instance(Transistor::class, $service);
// contextual binding:針對特定類別需求給不同實作
$this->app->when(\App\Services\OrderService::class)
->needs(\App\Contracts\PaymentGateway::class)
->give(\App\Services\StripeGateway::class);
// 解析與傳參
$gw = app(\App\Contracts\PaymentGateway::class);
$gw = app()->make(\App\Contracts\PaymentGateway::class, ['option' => 'value']); // 傳參給 closure
核心概念 (IoC/DI)
- IoC (控制反轉 - Inversion of Control): 將類別實例化的控制權交給容器,讓程式碼更專注於業務邏輯。
- DI (依賴注入 - Dependency Injection): 容器自動將類別 A 依賴的類別 B 實例「注入」給 A 的建構子或方法。
- 用途: 提高程式碼的彈性、可測試性和解耦度。
兩種核心綁定方式
主要在 Service Provider 的 register() 方法中使用以下兩種方式:
| 綁定方式 | 語法範例 (Service Provider 中) | 實例化行為 | 適用情境 |
|---|---|---|---|
bind | $this->app->bind(Interface::class, Concrete::class); | 每次解析時,都會創建一個 全新的 實例。 | 無狀態的服務,例如:單次交易的支付閘道。 |
singleton | $this->app->singleton(Service::class, function(){...}); | 只在第一次解析時創建,之後每次都返回 同一個 實例。 | 創建成本高、需要全局共享或維持狀態的服務,例如:配置管理器、日誌管理器。 |
如何手動解析 (取出) 服務?
除了最常用的自動依賴注入 (DI) 外,您也可以透過以下方式手動從容器中獲取服務實例:
| 方法 | 範例 |
|---|---|
| 全域輔助函數 | app(PaymentGatewayInterface::class); 或 app('sms.sender'); |
| Facade | \App::make(Service::class); |
resolve() 函數 | resolve(Service::class); |
釐清 Service Provider 與 Service Container 關係
核心比喻與職責劃分
| 概念 | 英文名稱 | 廚房中的比喻 | 核心職責 |
|---|---|---|---|
| 服務容器 | Service Container | 「廚房冰箱」 (或中央工具架) | 負責儲存所有服務(類別)和食譜(綁定方法),並在需要時交付實例。 |
| 服務提供者 | Service Provider | 「採購員兼食譜製作者」 | 是一個類別,負責配置和註冊服務的「食譜」給冰箱(容器),並啟動應用程式的其他元件。 |
詳細功能區分
- 服務容器 (Service Container)
- 本質: 是一個存放和管理所有服務實例的中央工具。
- 工作內容: 負責「製造」和「交付」您所需的類別實例,自動處理複雜的依賴關係 (DI)。
- 關鍵動詞: 管理 (Manage)、解析 (Resolve)、交付 (Deliver)。
- 您如何使用它: 通常使用 DI (依賴注入) 或
app()輔助函數來向它請求服務。
- 服務提供者 (Service Provider)
- 本質: 是一個設定和配置應用程式的類別。
- 工作內容: 在應用程式啟動時運行,註冊服務的綁定方式 (
bind,singleton) 到容器中,並負責應用程式的啟動邏輯(如註冊路由、視圖等)。 - 關鍵動詞: 提供 (Provide)、註冊 (Register)、啟動 (Bootstrapping)。
- 您如何使用它: 透過覆寫
register()和boot()方法來定義您的服務和啟動邏輯。
總結:配置與執行
- Provider (採購員) 告訴 Container (冰箱):「當有人需要
PaymentGateway時,你要給他Stripe實例。」 (register()階段) - Container (冰箱) 記下這個食譜(綁定)。
- 當您的控制器 (Controller) 宣告需要
PaymentGateway時,Container 會根據 Provider 提供的食譜,自動製造並提供Stripe實例給控制器。
核心筆記(2):Facade
Facade(門面)是 Laravel 提供的一種**「靜態介面」,讓你可以用簡單、靜態的方式來存取應用程式服務容器 (Service Container)** 中的實例。
Static Proxy
| 特性 | 說明 |
|---|---|
| 表面現象 | 看起來像是您在呼叫一個靜態方法,例如 Cache::get('key')。 |
| 底層本質 | 它是一個代理 (Proxy)。Facade 類別本身沒有該方法,而是將靜態呼叫轉發給容器中解析出的真實物件實例。 |
| 實現機制 | 依賴 PHP 的魔術方法 __callStatic(),以及 getFacadeAccessor() 方法。 |
與 Service Container
Facade 和 Service Container 相輔相成。
| 步驟 | 簡稱 | 說明 (由內向外) |
|---|---|---|
| Call | 靜態呼叫 | 應用程式發出靜態呼叫,例如 Cache::get()。 |
| Obtain Key | 獲取鍵值 | Facade 執行 protected static function getFacadeAccessor(),回傳容器的綁定鍵(例如 'cache')。 |
| Resolve | 容器解析 | Service Container 根據該鍵,解析並實例化出真正的服務物件。 |
| Execute | 轉發執行 | Facade 將靜態呼叫轉發給這個解析出的實例方法執行。 |
優點
- 簡潔語法 (Terse Syntax): 讓程式碼簡潔易讀,無需手動注入或使用冗長的
new關鍵字。 - 可測試性 (Testability): 儘管看起來是靜態呼叫,但因為底層是物件實例,所以可以在測試中輕鬆地進行 Mock/Stub(使用
Facade::shouldReceive())。 - 延遲載入 (Lazy Loading): 服務實例只在被第一次呼叫時才由容器解析,節省資源。