ThinkPHP 6/8 採用獨立驗證器類 (think\Validate) 取代舊版 Model 內建 $_validate,支援場景、規則管線、自定義驗證器、批次驗證、事件,更符合單一職責原則。
<?php
namespace app\validate;
use think\Validate;
class Article extends Validate {
// 規則定義
protected $rule = [
'title' => 'require|max:100|token',
'category_id' => 'require|integer|gt:0',
'content' => 'require',
'cover' => 'file|image|max:2048', // 檔案驗證
'tags' => 'array|max:10',
'status' => 'in:0,1,2', // 0草稿 1發布 2下架
];
// 自定義訊息
protected $message = [
'title.require' => '文章標題不能為空',
'title.max' => '標題長度不能超過 100 字元',
'title.token' => '標題包含敏感詞',
'category_id.require' => '請選擇分類',
'category_id.integer' => '分類 ID 必須為整數',
'category_id.gt' => '分類 ID 必須大於 0',
'content.require' => '文章內容不能為空',
'cover.image' => '封面必須為圖片檔案',
'cover.max' => '封面大小不能超過 2MB',
'tags.array' => '標籤格式錯誤',
'tags.max' => '標籤最多 10 個',
'status.in' => '狀態值不合法',
];
// 場景定義 (僅驗證指定欄位)
protected $scene = [
'create' => ['title', 'category_id', 'content', 'cover', 'tags'],
'edit' => ['title', 'category_id', 'content', 'cover', 'tags', 'status'],
'publish' => ['status'],
'batch' => ['title', 'category_id', 'content'],
];
// 自定義驗證方法 (rule 中使用 'token')
protected function token($value, $rule, $data): bool {
$forbidden = ['敏感詞1', '敏感詞2'];
foreach ($forbidden as $w) {
if (str_contains($value, $w)) return false;
}
return true;
}
}
<?php
namespace app\controller;
use app\validate\Article;
use think\facade\Validate;
use think\exception\ValidateException;
class ArticleController {
public function create() {
$data = request()->post();
try {
// 方式一:直接實例化驗證器 (推薦,支援場景)
$validate = new Article();
$validate->scene('create')->check($data);
// 驗證通過,$data 為安全資料
$article = ArticleModel::create($data);
return json(['id' => $article->id]);
} catch (ValidateException $e) {
return json(['error' => $e->getError()], 422);
}
}
public function batchCreate() {
$list = request()->post('list/a', []); // 陣列
// 批次驗證:每筆資料套用 batch 場景
$validate = new Article();
$validate->scene('batch');
if (!$validate->batch()->check($list)) {
return json(['errors' => $validate->getError()], 422);
}
// $list 現為通過驗證的陣列
ArticleModel::insertAll($list);
return json(['count' => count($list)]);
}
}
| 規則 | 說明 | 範例 |
|---|---|---|
require | 欄位必須存在且非空 (0 視為空需用 require|number) | 'age'=>'require|number' |
max:len | 字串/陣列/檔案長度 ≤ len | 'title'=>'max:100' |
min:len | 長度 ≥ len | 'pwd'=>'min:8' |
length:min,max | 長度區間 | 'pwd'=>'length:8,20' |
email | Email 格式 | 'email'=>'email' |
mobile | 中國手機號 | 'phone'=>'mobile' |
url | URL 格式 | 'site'=>'url' |
date | 日期格式 (Y-m-d) | 'birth'=>'date' |
alpha | 純字母 | 'code'=>'alpha' |
alphaNum | 字母數字 | 'username'=>'alphaNum' |
integer/float/number | 型別檢查 | 'age'=>'integer|gt:0' |
in:a,b,c | 值在列表中 | 'status'=>'in:0,1,2' |
notin:a,b | 值不在列表中 | 'role'=>'notin:admin,root' |
regex:pattern | 正則匹配 | 'code'=>'regex:^[A-Z]{3}\d{4}$' |
unique:table,column,except,pk | 資料庫唯一性 | 'email'=>'unique:users,email' |
confirm:field | 與另一欄位相同 (密碼確認) | 'repassword'=>'confirm:password' |
different:field | 與另一欄位不同 | 'new_pwd'=>'different:password' |
file|image|max:size | 檔案上傳驗證 | 'avatar'=>'file|image|max:1024' |
requireIf:field,value | 條件必填 | 'card_no'=>'requireIf:pay_type,card' |
requireWith:field | 另一欄位存在時必填 | 'b'=>'requireWith:a' |
// app/request/ArticleRequest.php
namespace app\request;
use think\facade\Validate;
class ArticleRequest {
public function create(): array {
$validate = Validate::make([
'title' => 'require|max:100',
'content' => 'require',
], [
'title.require' => '標題必填',
]);
if (!$validate->check(request()->post())) {
throw new \think\exception\ValidateException($validate->getError());
}
return request()->post();
}
}
// 控制器
public function create(ArticleRequest $request) {
$data = $request->create(); // 自動驗證、異常拋出
ArticleModel::create($data);
}
// 路由: POST /validate/field
public function validateField() {
$field = request()->post('field');
$value = request()->post('value');
$validate = Validate::make([$field => $this->rules[$field] ?? ''], $this->messages);
$validate->check([$field => $value]);
return json(['valid' => true, 'message' => $validate->getError()]);
}
// lang/zh-tw/validate.php
return [
'title.require' => '標題不可為空',
'email.email' => '信箱格式錯誤',
];
// 驗證器自動讀取 lang/{locale}/validate.php
| 錯誤 | 原因 | 解決 |
|---|---|---|
| 驗證不生效 | 未呼叫 check() 或未捕獲 ValidateException | 確認流程:new Validate -> scene() -> check() -> try/catch |
unique 規則報錯 | 資料表/欄位名錯誤、未排除當前主鍵 (編輯時) | 'email'=>'unique:users,email,{{id}},id' (模板變數) |
| 檔案驗證失敗 | 未設定 enctype="multipart/form-data"、PHP file_uploads=Off | 檢查表單屬性、php.ini |
| 批次驗證只回傳第一個錯誤 | 未使用 batch() 方法 | $validate->batch()->check($list) |
完整驗證器範例專案 (含單元測試、多語言、自定義規則庫) 見 GitHub。