# 基於 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` |