# 基於 Laravel 12 的最佳開發規範 本規範旨在幫助團隊在專案開發中建立穩固、可維護、且高效能的系統。以下內容同時兼顧簡潔要點與詳細說明,適用於各層級的開發實踐。 --- ## 一、快速要點 - **PR 與測試:** 所有拉取請求(PR)必須通過 Pint 檢查並完成所有測試,確保程式碼品質與一致性。 - **主鍵策略:** 全站主鍵統一採用 ULID 或 UUID,適合分佈式系統。 - **方法型別:** 每個方法必須明確聲明回傳型別,增進可讀性與早期錯誤檢查。 - **非同步處理:** 考慮使用 Job Queues 處理耗時任務,如使用者資料輸入或查詢。 - **命名慣例:** 模型、控制器、儲存庫及服務層均採用單數命名;變數與方法使用 camelCase,而資料庫欄位、Request 參數則使用 lower_snake_case。 - **API 格式:** 統一回應格式為包含 `success`、`message`、`data`(並可附加 `errorCode` 與 `statusCode`)的陣列。 --- ## 二、詳細開發準則 ### 1. 程式碼品質與測試 - **PR 檢查:** - 所有拉取請求必須通過 [Pint](https://laravel.com/docs/12.x/pint) 靜態檢查與單元測試。 - 建議結合 CI/CD 流程,確保代碼達到團隊標準後再進行合併。 - **主鍵選型:** - 採用 ULID/UUID 作為全站主鍵,確保唯一性並支援分佈式系統(可參考相關套件如 `laravel-ultramanager`)。 - **方法與型別:** - 所有方法必須強制宣告回傳型別,符合 PHP 7+ 的現代開發要求,降低潛在錯誤。 - **非同步任務:** - 耗時任務(例如使用者資料寫入、查詢)應優先使用 Laravel 內建的 Job Queue 處理,改善應用程式響應速度與系統效能。 - **安全性與加密:** - 第三方資料加密預設使用非對稱加密(如 RSA)來保護敏感資訊;內部資料可採用對稱加密(如 AES)或使用 Laravel 的 `encrypt()`/`decrypt()` 系列函數。 - **資料庫與 ORM 實踐:** - 避免使用 `get()` 一次性查詢所有欄位,應只查詢需要用到的欄位: ```php // 範例: User::select('id', 'name', 'email')->get(); ``` - 注意 N+1 問題,適當使用 eager loading(如 `with('relation')`)。 - **多語系支援:** - 對前端字串實現多語系處理,使用 Laravel 本地化功能(如 `Lang::get()`)進行管理。 - **系統參數:** - 將與系統相關的設定統一存放在 `config/settings.php` 中。 --- ### 2. 架構與命名慣例 #### (1) 模型(Model) - **命名:** - 模型名稱使用 PascalCase 且為單數,如 `User`、`Product`。 - **邏輯分離:** - 模型僅處理資料屬性,不應包含複雜業務邏輯,業務邏輯應移至 Service 層。 - **共用功能:** - 常見功能(例如搜尋、日誌記錄、操作者記錄)建議以 Trait 方式實作: ```php use App\Traits\Searchable; class User extends Authenticatable { use Searchable; // ... } ``` - **多型關係:** - 嚴禁在大資料量處理中使用 polymorphic relationship,以免影響查詢效能。 #### (2) 控制器(Controller) - **目錄結構:** - API 控制器存放於 `App\Http\Controllers\Api` 目錄下,Web 控制器存放於 `App\Http\Controllers\Web` 目錄下。 - **依賴注入:** - 控制器應透過 Contracts 注入 Service,禁止直接操作 Model 或 Repository: ```php interface UserServiceContract { public function getUserById($id); } class UserController extends Controller { protected $userService; public function __construct(UserServiceContract $userService) { $this->userService = $userService; } public function show($id) { return $this->userService->getUserById($id); } } ``` - **驗證:** - 所有表單驗證一律採用 Custom Request,保持控制器邏輯乾淨。 - **API 回應:** - API 回應格式統一為: ```php response([ 'success' => true, 'message' => '操作成功。', 'data' => [...], 'errorCode' => null, 'statusCode' => 200, ], 200); ``` #### (3) 儲存庫(Repository)與服務層(Service) - **命名規則:** - 儲存庫與服務類別均使用單數命名,如 `UserRepository`、`UserService`。 - **職責分離:** - Repository 僅與模型互動,專注資料存取;Service 層則專注處理業務邏輯,並呼叫 Repository。 - **Eloquent 優先:** - 優先使用 Eloquent ORM 進行資料操作,避免直接使用原生 SQL。 #### (4) 常數管理 - **命名與分類:** - 常數名稱使用 TITLE_CASE,如 `STATUS_PASS`。 - 建議將常用狀態或類型抽離成常數類別(單數),如: ```php class UserStatus { public const ACTIVE = 'active'; public const INACTIVE = 'inactive'; } // 使用範例: if ($user->status === UserStatus::ACTIVE) { // ... } ``` #### (5) 路由設定 - **命名慣例:** - 路由使用 lower-kebab-case 並必須設定 name,例如: ```php Route::get('/user/{id}', [UserController::class, 'show'])->name('user.show'); ``` - **區分 API 與 Web:** - API 路由定義於 `routes/api.php` 且名稱統一加上 `api.` 前綴,Web 路由定義於 `routes/web.php`。 - **中介層設定:** - middleware 的判斷統一放在路由中定義,禁止直接在程式中呼叫中介類別。 #### (6) Traits 重用 - 將常用功能(如記錄操作、角色 ID 記錄、自動更新建立/更新者欄位、搜尋功能)抽離成 Trait,達到程式碼重用: ```php trait RecordSignature { public static function bootRecordSignature() { static::creating(function ($model) { $model->create_by = auth()->id(); }); static::updating(function ($model) { $model->update_by = auth()->id(); }); } } ``` --- ### 3. 命名慣例與程式風格 - **函式/方法、屬性、變數:** - 採用 camelCase,如 `$userName`、`getFullName()`。 - **陣列索引、資料庫欄位、模型 Fillable、模型關聯、Request:** - 採用 lower_snake_case,如 `user_name`、`$request->user_name`。 - **驗證欄位:** - 範例: ```php $validated = $request->validated(); ``` --- ### 4. 測試策略 - **測試命名:** - 測試檔案與方法命名使用 lower_snake_case,例如 `test_user_controller.php`。 - **資料庫測試:** - 建議使用 `LazilyRefreshDatabase` 取代 `RefreshDatabase`,僅刷新必要資料,提升測試效率。 - **資料生成:** - 優先採用 Faker 產生測試資料,如: ```php $user = User::factory()->create(); ``` - **斷言與模擬:** - 優先使用 `assertEquals` 進行精確斷言,並利用 Mockery 模擬第三方 API 呼叫。 - **時間凍結:** - 使用 `freezeTime` 或 Carbon 的 `setTestNow` 方法確保時間敏感操作的正確性。 --- ### 5. 加密與解密 - **可逆加密:** - 使用 Laravel 提供的 `encrypt()` 與 `decrypt()` 進行加解密,範例如下: ```php $encrypted = encrypt('secret'); $decrypted = decrypt($encrypted); ``` - 字串加解密可使用 `encryptString()` 與 `decryptString()`。 - **不可逆加密:** - 使用 `bcrypt()` 進行雜湊處理,例如: ```php $hashed = bcrypt('password'); ``` --- ### 6. API 回應格式 - **統一格式:** - API 回應統一以以下格式返回,包含 `success`、`message`、`data`,並可附帶 `errorCode` 與 `statusCode`: ```php response([ 'success' => true, 'message' => '操作成功。', 'data' => [...], 'errorCode' => null, 'statusCode' => 200, ], 200); ``` --- ### 7. 其他最佳實踐 - **HTTP 請求:** - 使用 Http Facade 進行 cURL 請求: ```php // 基本請求 $response = Http::get('https://api.example.com/data'); // 繞過本地 SSL 驗證 $response = Http::withOptions(['verify' => config('http.verify')]) ->get('https://api.example.com/data'); // Bearer Token 驗證 $response = Http::withToken($bearerToken) ->get('https://api.example.com/data'); ``` - **遠端屬性賦值:** - 統一採用 `$appends` 與 `getAttribute()` 實現訪問器方法: ```php protected $appends = ['full_name']; public function getFullNameAttribute() { return $this->first_name . ' ' . $this->last_name; } ``` - **資料庫交易:** - 進行多關聯模型 CRUD 時,務必使用 `DB::transaction`,並在操作中執行 `commit` 與 `rollback`: ```php DB::transaction(function () { // CRUD 操作 DB::commit(); }); ``` - **批量資料處理:** - 傳送不確定筆數資料時,以陣列形式送至後端進行處理。 --- ### 8. 規範總覽表 | **類別** | **命名慣例** | **示例** | |------------------------|----------------------|------------------------------------| | 模型 | PascalCase, 單數 | `User`, `Product` | | 控制器子目錄 | 單數 | `UserController` | | 儲存庫/服務 | 單數 | `UserRepository`, `UserService` | | 變數/方法 | camelCase | `$userName`, `getFullName()` | | 資料庫欄位/請求 | lower_snake_case | `user_name`, `$request->user_name` | | 路由名稱 | lower-kebab-case | `user-profile` | | 常數 | TITLE_CASE | `STATUS_PASS` |