本文目录导读:

在 PHP 中区分不同的验证场景(如“新增”和“编辑”),核心思路是根据当前操作的状态或来源数据,动态调整验证规则。
由于 PHP 不像 Laravel 那样有内置的 FormRequest 场景概念,你需要自己实现逻辑,以下是几种最常用、最清晰的区分方法:
根据请求方法区分(最常用)
这是最直观的方法,尤其适用于 RESTful 风格接口。
- 新增:通常对应
POST或PUT方法。 - 编辑:通常对应
PATCH或POST(带 ID 参数)。
代码示例:
<?php
function validateUserInput($request) {
$rules = [];
if ($request->isMethod('POST')) {
// 新增场景:邮箱必须唯一,密码必填
$rules['email'] = 'required|email|unique:users,email';
$rules['password'] = 'required|min:6';
} elseif ($request->isMethod('PATCH') || $request->isMethod('PUT')) {
// 编辑场景:邮箱唯一性验证排除当前用户,密码可选
$rules['email'] = 'required|email|unique:users,email,' . $request->id;
$rules['password'] = 'sometimes|min:6'; // 仅在填写时验证
}
// 公共规则
$rules['name'] = 'required|max:255';
return performValidation($request->all(), $rules);
}
优点:简单、符合 HTTP 语义。 缺点:如果接口设计不标准(如全部用 POST),则无法区分。
显式传递场景参数(最灵活)
在请求中增加一个隐藏字段或 GET 参数(如 _scenario),前端告诉后端“我是谁”。
代码示例:
<?php
function validateProduct($data, $scenario = 'create') {
$rules = [
'title' => 'required|max:200',
];
if ($scenario === 'create') {
// 新增:必须上传图片,SKU唯一
$rules['image'] = 'required|image';
$rules['sku'] = 'required|unique:products';
} elseif ($scenario === 'update') {
// 编辑:图片可选,SKU唯一性排除自身
$rules['image'] = 'sometimes|image';
$rules['sku'] = 'required|unique:products,sku,' . ($data['id'] ?? 0);
}
return performValidation($data, $rules);
}
// 前端调用
// validateProduct($_POST, $_POST['_scenario'] ?? 'create');
优点:完全由开发者控制,适应任何不标准的接口。 缺点:需要前后端约定好字段名,前端可能篡改。
根据 ID 参数区分(简单直接)
判断请求数据中是否存在“主键 ID”,如果存在则视为编辑,否则视为新增。
代码示例:
<?php
function validateArticle($data) {
$isUpdate = !empty($data['id']);
$rules = [
'content' => 'required|max:10000',
];
if ($isUpdate) {
// 编辑模式
$rules['title'] = 'required|max:100';
// 对唯一性验证排除当前记录
$rules['slug'] = 'required|unique:articles,slug,' . $data['id'];
} else {
// 新增模式
$rules['title'] = 'required|max:100|unique:articles'; // 标题全局唯一
$rules['slug'] = 'required|unique:articles'; // slug全局唯一
}
return performValidation($data, $rules);
}
优点:无需额外参数,代码自解释。 缺点:如果表单 ID 字段永远为空或不存在,则需谨慎处理。
使用验证器类封装(推荐用于复杂项目)
如果项目较大,建议将验证逻辑封装成类,通过方法名定义场景。
代码示例(实现一个简单的场景验证器):
<?php
class UserValidator {
protected $data;
protected $rules = [];
public function __construct($data) {
$this->data = $data;
}
// 设置场景:'create' 或 'update'
public function setScenario($scenario) {
switch ($scenario) {
case 'create':
$this->rules = [
'email' => 'required|email|unique:users',
'password' => 'required|min:8',
'name' => 'required',
];
break;
case 'update':
$this->rules = [
'email' => 'required|email|unique:users,email,' . ($this->data['id'] ?? 0),
'password' => 'sometimes|min:8',
'name' => 'required',
];
break;
default:
throw new \InvalidArgumentException("Invalid scenario: $scenario");
}
return $this;
}
public function validate() {
// 执行验证逻辑(如调用 PHP 原生 filter_var 或第三方库)
// ...
if (!filter_var($this->data['email'], FILTER_VALIDATE_EMAIL)) {
throw new \Exception('Email 格式错误');
}
// 实际项目中建议使用 symfony/validator 或 valitron
return true;
}
}
// 使用方式
$validator = new UserValidator($_POST);
$validator->setScenario('create')->validate(); // 新增时验证
优点:结构清晰,易于扩展,可测试性好。 缺点:需要额外编写类代码。
总结与建议
| 方案 | 适用场景 | 推荐指数 |
|---|---|---|
| 请求方法区分 | RESTful API,前后端分离 | ⭐⭐⭐⭐⭐ |
| 显式场景参数 | 旧系统改造,表单非标准 | ⭐⭐⭐⭐ |
| 根据 ID 区分 | 简单的 CRUD 操作 | ⭐⭐⭐⭐ |
| 验证器类封装 | 大型项目,逻辑复用要求高 | ⭐⭐⭐⭐⭐(强烈推荐) |
最佳实践建议:
- 优先选择方案 1 + 方案 3 结合:先判断 HTTP 方法,再辅以 ID 存在性判断。
- 使用验证库:不要自己写验证逻辑,推荐使用
symfony/validator(全功能)或vlucas/valitron(轻量)。 - 唯一性验证的特殊处理:
- 编辑场景下验证唯一字段(如 Email、手机号)时,必须排除当前记录自身,否则会报“该邮箱已存在”的错误。
- SQL 示例:
SELECT 1 FROM users WHERE email = ? AND id != ?
- 前后端约定:确保前端能正确传递表示“场景”的参数,或保持接口风格统一。