|
|
@@ -1,54 +1,119 @@
|
|
|
# Validation 使用示例
|
|
|
|
|
|
-> 本文档展示如何正确使用 ValidationCore 和 Validator
|
|
|
+## 重要说明:动态属性规范
|
|
|
|
|
|
-## 1. 正确的 Validation 实现
|
|
|
+**从 PHP 8.2+ 开始,动态属性被弃用。为了代码的健壮性和可维护性,我们要求:**
|
|
|
|
|
|
-### 1.1 PetGetValidation 类
|
|
|
+### ❌ 不允许的做法
|
|
|
+```php
|
|
|
+// 错误:直接给未定义的属性赋值(会产生弃用警告)
|
|
|
+$this->validation->upgrade_config = $config;
|
|
|
+$this->validation->land = $landObject;
|
|
|
+```
|
|
|
+
|
|
|
+### ✅ 推荐的做法
|
|
|
+```php
|
|
|
+// 正确:在 Validation 类中预先定义属性并声明类型
|
|
|
+class LandUpgradeValidation extends ValidationCore
|
|
|
+{
|
|
|
+ // 预定义属性,声明类型
|
|
|
+ public ?FarmLandUpgradeConfig $upgrade_config = null;
|
|
|
+ public ?FarmLand $land = null;
|
|
|
+
|
|
|
+ // 验证规则...
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 规范要求
|
|
|
+
|
|
|
+1. **预定义属性**:所有需要在验证器间传递的数据都必须预先在 Validation 类中定义
|
|
|
+2. **类型声明**:所有属性都必须声明类型,使用 nullable 类型(`?Type`)允许初始值为 null
|
|
|
+3. **初始值**:为属性设置合理的初始值(通常为 `null`)
|
|
|
+4. **文档注释**:为属性添加清晰的注释说明其用途
|
|
|
+
|
|
|
+### 示例对比
|
|
|
+
|
|
|
+**旧的写法(不推荐):**
|
|
|
+```php
|
|
|
+// 在验证器中动态赋值
|
|
|
+$this->validation->land = $land;
|
|
|
+$this->validation->upgrade_config = $config;
|
|
|
+```
|
|
|
+
|
|
|
+**新的写法(推荐):**
|
|
|
+```php
|
|
|
+// 1. 在 Validation 类中预定义
|
|
|
+class LandUpgradeValidation extends ValidationCore
|
|
|
+{
|
|
|
+ /** @var FarmLand|null 土地对象,由 LandOwnershipValidator 设置 */
|
|
|
+ public ?FarmLand $land = null;
|
|
|
+
|
|
|
+ /** @var FarmLandUpgradeConfig|null 升级配置,由 LandUpgradePathValidator 设置 */
|
|
|
+ public ?FarmLandUpgradeConfig $upgrade_config = null;
|
|
|
+}
|
|
|
+
|
|
|
+// 2. 在验证器中安全赋值
|
|
|
+$this->validation->land = $land;
|
|
|
+$this->validation->upgrade_config = $config;
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 基础使用示例
|
|
|
+
|
|
|
+### 1. 创建 Validation 类
|
|
|
|
|
|
```php
|
|
|
<?php
|
|
|
|
|
|
-namespace App\Module\Pet\Validations;
|
|
|
+namespace App\Module\Farm\Validations;
|
|
|
|
|
|
-use App\Module\Pet\Validators\PetGetValidator;
|
|
|
+use App\Module\Farm\Models\FarmLand;
|
|
|
+use App\Module\Farm\Models\FarmLandUpgradeConfig;
|
|
|
+use App\Module\Farm\Validators\LandOwnershipValidator;
|
|
|
+use App\Module\Farm\Validators\LandUpgradeStatusValidator;
|
|
|
+use App\Module\Farm\Validators\LandUpgradePathValidator;
|
|
|
use UCore\ValidationCore;
|
|
|
|
|
|
/**
|
|
|
- * 宠物获取验证类
|
|
|
- *
|
|
|
- * 用于验证宠物获取操作的输入数据,包括用户ID和物品ID
|
|
|
+ * 土地升级验证类
|
|
|
*/
|
|
|
-class PetGetValidation extends ValidationCore
|
|
|
+class LandUpgradeValidation extends ValidationCore
|
|
|
{
|
|
|
+ /** @var FarmLand|null 土地对象,由 LandOwnershipValidator 设置 */
|
|
|
+ public ?FarmLand $land = null;
|
|
|
+
|
|
|
+ /** @var FarmLandUpgradeConfig|null 升级配置,由 LandUpgradePathValidator 设置 */
|
|
|
+ public ?FarmLandUpgradeConfig $upgrade_config = null;
|
|
|
+
|
|
|
/**
|
|
|
* 验证规则
|
|
|
- *
|
|
|
- * @param array $rules 自定义规则
|
|
|
- * @return array
|
|
|
*/
|
|
|
public function rules($rules = []): array
|
|
|
{
|
|
|
return [
|
|
|
+ // 基础验证
|
|
|
+ ['user_id,land_id', 'required'],
|
|
|
+ ['user_id,land_id', 'integer', 'min' => 1],
|
|
|
+
|
|
|
+ // 业务验证(按顺序执行)
|
|
|
[
|
|
|
- 'user_id,item_id', 'required'
|
|
|
+ 'land_id', new LandOwnershipValidator($this, ['user_id', 'land']),
|
|
|
+ 'msg' => '土地不存在或不属于当前用户'
|
|
|
],
|
|
|
[
|
|
|
- 'user_id,item_id', 'integer', 'min' => 1,
|
|
|
- 'msg' => '{attr}必须是大于0的整数'
|
|
|
+ 'land_id', new LandUpgradeStatusValidator($this, ['land']),
|
|
|
+ 'msg' => '土地状态不允许升级'
|
|
|
],
|
|
|
- // 验证宠物获取相关逻辑
|
|
|
[
|
|
|
- 'item_id', new PetGetValidator($this, ['user_id']),
|
|
|
- 'msg' => '宠物获取验证失败'
|
|
|
- ]
|
|
|
+ 'land_id', new LandUpgradePathValidator($this, ['user_id', 'upgrade_config']),
|
|
|
+ 'msg' => '升级路径验证失败'
|
|
|
+ ],
|
|
|
];
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 设置默认值
|
|
|
- *
|
|
|
- * @return array
|
|
|
+ * 默认值
|
|
|
*/
|
|
|
public function default(): array
|
|
|
{
|
|
|
@@ -57,35 +122,34 @@ class PetGetValidation extends ValidationCore
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-### 1.2 PetGetValidator 类
|
|
|
+### 2. 创建 Validator 类
|
|
|
|
|
|
```php
|
|
|
<?php
|
|
|
|
|
|
-namespace App\Module\Pet\Validators;
|
|
|
+namespace App\Module\Farm\Validators;
|
|
|
|
|
|
+use App\Module\Farm\Enums\LAND_STATUS;
|
|
|
+use App\Module\Farm\Models\FarmLand;
|
|
|
use UCore\Validator;
|
|
|
|
|
|
/**
|
|
|
- * 宠物获取验证器
|
|
|
- *
|
|
|
- * 验证宠物获取操作是否有效,包括物品拥有、宠物种类配置、数量限制等
|
|
|
+ * 土地归属验证器
|
|
|
*/
|
|
|
-class PetGetValidator extends Validator
|
|
|
+class LandOwnershipValidator extends Validator
|
|
|
{
|
|
|
/**
|
|
|
- * 验证宠物获取操作
|
|
|
+ * 验证土地归属
|
|
|
*
|
|
|
- * @param mixed $value 物品ID
|
|
|
+ * @param mixed $value 土地ID
|
|
|
* @param array $data 包含用户ID的数组
|
|
|
* @return bool 验证是否通过
|
|
|
*/
|
|
|
public function validate(mixed $value, array $data): bool
|
|
|
{
|
|
|
- $itemId = (int)$value;
|
|
|
-
|
|
|
- // 从 args 获取字段键名
|
|
|
+ // 从 args 获取参数
|
|
|
$userIdKey = $this->args[0] ?? 'user_id';
|
|
|
+ $landKey = $this->args[1] ?? 'land';
|
|
|
|
|
|
$userId = $data[$userIdKey] ?? null;
|
|
|
|
|
|
@@ -95,153 +159,246 @@ class PetGetValidator extends Validator
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
- // 验证用户是否拥有该物品
|
|
|
- if (!$this->validateUserHasItem($userId, $itemId)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+ // 查询土地
|
|
|
+ $land = FarmLand::where('id', $value)
|
|
|
+ ->where('user_id', $userId)
|
|
|
+ ->first();
|
|
|
|
|
|
- // 验证物品是否具有宠物种类属性
|
|
|
- if (!$this->validateItemPetAttribute($itemId)) {
|
|
|
+ if (!$land) {
|
|
|
+ $this->addError('土地不存在或不属于当前用户');
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- // 验证用户宠物数量限制
|
|
|
- if (!$this->validatePetCountLimit($userId)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+ // 将土地对象保存到验证对象中,供后续验证器使用
|
|
|
+ $this->validation->$landKey = $land;
|
|
|
|
|
|
return true;
|
|
|
} catch (\Exception $e) {
|
|
|
- $this->addError('验证宠物获取时发生错误: ' . $e->getMessage());
|
|
|
+ $this->addError('验证土地归属时发生错误: ' . $e->getMessage());
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- // 其他私有验证方法...
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-## 2. 关键差异说明
|
|
|
+### 3. 在 Handler 中使用
|
|
|
|
|
|
-### 2.1 继承的基类
|
|
|
-
|
|
|
-**错误方式**:
|
|
|
```php
|
|
|
-class PetGetValidation extends Validation // 错误
|
|
|
-```
|
|
|
+<?php
|
|
|
|
|
|
-**正确方式**:
|
|
|
-```php
|
|
|
-class PetGetValidation extends ValidationCore // 正确
|
|
|
-```
|
|
|
+namespace App\Module\Farm\Handlers;
|
|
|
|
|
|
-### 2.2 验证规则格式
|
|
|
+use App\Module\Farm\Validations\LandUpgradeValidation;
|
|
|
+use App\Module\Farm\Logics\LandLogic;
|
|
|
+use UCore\Handler;
|
|
|
|
|
|
-**错误方式**:
|
|
|
-```php
|
|
|
-protected function rules(): array
|
|
|
+/**
|
|
|
+ * 土地升级处理器
|
|
|
+ */
|
|
|
+class LandUpgradeHandler extends Handler
|
|
|
{
|
|
|
- return [
|
|
|
- 'user_id' => 'required|integer|min:1',
|
|
|
- 'item_id' => 'required|integer|min:1',
|
|
|
- ];
|
|
|
+ /**
|
|
|
+ * 处理土地升级
|
|
|
+ */
|
|
|
+ public function handle(array $data): array
|
|
|
+ {
|
|
|
+ // 1. 验证数据
|
|
|
+ $validation = new LandUpgradeValidation($data);
|
|
|
+ $validation->validated();
|
|
|
+
|
|
|
+ // 2. 获取验证后的数据和对象
|
|
|
+ $userId = $validation->getSafe('user_id');
|
|
|
+ $landId = $validation->getSafe('land_id');
|
|
|
+ $land = $validation->land; // 预定义的属性,类型安全
|
|
|
+ $upgradeConfig = $validation->upgrade_config; // 预定义的属性,类型安全
|
|
|
+
|
|
|
+ // 3. 执行业务逻辑
|
|
|
+ DB::transaction(function () use ($userId, $landId, $upgradeConfig) {
|
|
|
+ $landLogic = new LandLogic();
|
|
|
+ $landLogic->upgradeLand($userId, $landId, $upgradeConfig->to_type_id);
|
|
|
+ });
|
|
|
+
|
|
|
+ return ['success' => true, 'message' => '土地升级成功'];
|
|
|
+ }
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-**正确方式**:
|
|
|
+## 最佳实践
|
|
|
+
|
|
|
+### 1. 属性定义规范
|
|
|
+
|
|
|
```php
|
|
|
-public function rules($rules = []): array
|
|
|
+class ExampleValidation extends ValidationCore
|
|
|
{
|
|
|
- return [
|
|
|
- [
|
|
|
- 'user_id,item_id', 'required'
|
|
|
- ],
|
|
|
- [
|
|
|
- 'user_id,item_id', 'integer', 'min' => 1,
|
|
|
- 'msg' => '{attr}必须是大于0的整数'
|
|
|
- ],
|
|
|
- [
|
|
|
- 'item_id', new PetGetValidator($this, ['user_id']),
|
|
|
- 'msg' => '宠物获取验证失败'
|
|
|
- ]
|
|
|
- ];
|
|
|
+ // ✅ 正确:预定义属性,声明类型,添加注释
|
|
|
+ /** @var User|null 用户对象,由 UserValidator 设置 */
|
|
|
+ public ?User $user = null;
|
|
|
+
|
|
|
+ /** @var array<Item>|null 物品列表,由 ItemValidator 设置 */
|
|
|
+ public ?array $items = null;
|
|
|
+
|
|
|
+ /** @var Config|null 配置对象,由 ConfigValidator 设置 */
|
|
|
+ public ?Config $config = null;
|
|
|
+
|
|
|
+ // ❌ 错误:没有类型声明
|
|
|
+ // public $user;
|
|
|
+
|
|
|
+ // ❌ 错误:没有注释说明
|
|
|
+ // public ?User $user = null;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-### 2.3 Validator 的实现
|
|
|
+### 2. Validator 中的属性赋值
|
|
|
|
|
|
-**错误方式**:
|
|
|
```php
|
|
|
-class PetGetValidator extends Validator
|
|
|
+class ExampleValidator extends Validator
|
|
|
{
|
|
|
- public function validate(): bool // 错误的方法签名
|
|
|
+ public function validate(mixed $value, array $data): bool
|
|
|
{
|
|
|
- $userId = $this->data['user_id']; // 错误的数据访问方式
|
|
|
- // ...
|
|
|
+ // 获取数据
|
|
|
+ $user = User::find($value);
|
|
|
+
|
|
|
+ if (!$user) {
|
|
|
+ $this->addError('用户不存在');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ✅ 正确:赋值给预定义的属性
|
|
|
+ $this->validation->user = $user;
|
|
|
+
|
|
|
+ // ❌ 错误:动态属性赋值(会产生弃用警告)
|
|
|
+ // $this->validation->dynamic_property = $user;
|
|
|
+
|
|
|
+ return true;
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-**正确方式**:
|
|
|
+### 3. 验证器间数据传递
|
|
|
+
|
|
|
```php
|
|
|
-class PetGetValidator extends Validator
|
|
|
+class FirstValidator extends Validator
|
|
|
{
|
|
|
- public function validate(mixed $value, array $data): bool // 正确的方法签名
|
|
|
+ public function validate(mixed $value, array $data): bool
|
|
|
{
|
|
|
- $itemId = (int)$value; // 验证的值
|
|
|
- $userIdKey = $this->args[0] ?? 'user_id'; // 从args获取字段键名
|
|
|
- $userId = $data[$userIdKey] ?? null; // 从data数组获取其他数据
|
|
|
- // ...
|
|
|
+ $user = User::find($value);
|
|
|
+
|
|
|
+ // 第一个验证器设置用户对象
|
|
|
+ $this->validation->user = $user;
|
|
|
+
|
|
|
+ return true;
|
|
|
}
|
|
|
}
|
|
|
-```
|
|
|
|
|
|
-## 3. 使用方式
|
|
|
+class SecondValidator extends Validator
|
|
|
+{
|
|
|
+ public function validate(mixed $value, array $data): bool
|
|
|
+ {
|
|
|
+ // 第二个验证器使用第一个验证器设置的用户对象
|
|
|
+ $user = $this->validation->user;
|
|
|
|
|
|
-### 3.1 在 Handler 中使用
|
|
|
+ if (!$user) {
|
|
|
+ $this->addError('用户信息不存在,请先验证用户');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用用户对象进行验证...
|
|
|
+ return $user->isActive();
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 4. 类型安全的好处
|
|
|
|
|
|
```php
|
|
|
-// 先进行验证,避免不必要的事务开销
|
|
|
-$validation = new \App\Module\Pet\Validations\PetGetValidation([
|
|
|
- 'user_id' => $userId,
|
|
|
- 'item_id' => $itemId
|
|
|
-]);
|
|
|
+class LandUpgradeValidation extends ValidationCore
|
|
|
+{
|
|
|
+ /** @var FarmLand|null */
|
|
|
+ public ?FarmLand $land = null;
|
|
|
+
|
|
|
+ /** @var FarmLandUpgradeConfig|null */
|
|
|
+ public ?FarmLandUpgradeConfig $upgrade_config = null;
|
|
|
+}
|
|
|
|
|
|
-// 验证数据
|
|
|
+// 在 Handler 中使用
|
|
|
+$validation = new LandUpgradeValidation($data);
|
|
|
$validation->validated();
|
|
|
+
|
|
|
+// ✅ IDE 可以提供类型提示和自动完成
|
|
|
+$landType = $validation->land->land_type; // IDE 知道这是 FarmLand 对象
|
|
|
+$targetType = $validation->upgrade_config->to_type_id; // IDE 知道这是 FarmLandUpgradeConfig 对象
|
|
|
+
|
|
|
+// ✅ 静态分析工具可以检查类型错误
|
|
|
+// $validation->land->nonExistentProperty; // IDE 会警告属性不存在
|
|
|
```
|
|
|
|
|
|
-### 3.2 验证器参数传递
|
|
|
+### 5. 迁移现有代码
|
|
|
+
|
|
|
+如果你有使用动态属性的现有代码,按以下步骤迁移:
|
|
|
+
|
|
|
+**步骤 1:识别动态属性**
|
|
|
+```bash
|
|
|
+# 搜索可能的动态属性赋值
|
|
|
+grep -r "\$this->validation->" app/Module/*/Validators/
|
|
|
+```
|
|
|
|
|
|
+**步骤 2:在 Validation 类中定义属性**
|
|
|
```php
|
|
|
-// 在 Validation 中定义验证器时传递参数
|
|
|
-[
|
|
|
- 'item_id', new PetGetValidator($this, ['user_id']),
|
|
|
- 'msg' => '宠物获取验证失败'
|
|
|
-]
|
|
|
+// 在相应的 Validation 类中添加属性定义
|
|
|
+class YourValidation extends ValidationCore
|
|
|
+{
|
|
|
+ /** @var SomeModel|null 描述这个属性的用途 */
|
|
|
+ public ?SomeModel $property_name = null;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**步骤 3:更新验证器代码**
|
|
|
+```php
|
|
|
+// 验证器中的赋值保持不变,但现在是类型安全的
|
|
|
+$this->validation->property_name = $value;
|
|
|
+```
|
|
|
|
|
|
-// 在 Validator 中接收参数
|
|
|
-$userIdKey = $this->args[0] ?? 'user_id'; // 'user_id'
|
|
|
-$userId = $data[$userIdKey] ?? null;
|
|
|
+**步骤 4:测试验证**
|
|
|
+```bash
|
|
|
+# 运行测试确保没有破坏现有功能
|
|
|
+php artisan test
|
|
|
```
|
|
|
|
|
|
-## 4. 错误处理
|
|
|
+## 常见问题
|
|
|
|
|
|
-### 4.1 添加错误信息
|
|
|
+### Q: 为什么要禁用动态属性?
|
|
|
+A:
|
|
|
+1. **类型安全**:预定义属性可以声明类型,IDE 和静态分析工具可以提供更好的支持
|
|
|
+2. **代码可读性**:明确的属性定义让代码更容易理解和维护
|
|
|
+3. **错误预防**:避免因拼写错误导致的 bug
|
|
|
+4. **PHP 兼容性**:为 PHP 9.0 做准备(将完全禁止动态属性)
|
|
|
|
|
|
+### Q: 如何处理可选的验证器数据?
|
|
|
+A: 使用 nullable 类型和默认值:
|
|
|
```php
|
|
|
-// 在 Validator 中添加错误
|
|
|
-$this->addError('该物品不能获取宠物');
|
|
|
+/** @var OptionalData|null 可选数据,可能为 null */
|
|
|
+public ?OptionalData $optional_data = null;
|
|
|
```
|
|
|
|
|
|
-### 4.2 错误信息传递
|
|
|
+### Q: 如何处理数组类型的数据?
|
|
|
+A: 使用泛型注释:
|
|
|
+```php
|
|
|
+/** @var array<Item>|null 物品数组 */
|
|
|
+public ?array $items = null;
|
|
|
+
|
|
|
+/** @var array<string, mixed>|null 配置数组 */
|
|
|
+public ?array $config = null;
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 总结
|
|
|
|
|
|
-验证失败时,错误信息会自动传递到 ValidationCore,然后抛出 ValidateException。
|
|
|
+通过遵循这些规范和最佳实践,我们可以:
|
|
|
|
|
|
-## 5. 最佳实践
|
|
|
+1. **提高代码质量**:类型安全的属性定义让代码更健壮
|
|
|
+2. **改善开发体验**:IDE 可以提供更好的类型提示和错误检查
|
|
|
+3. **增强可维护性**:明确的属性定义让代码更容易理解和维护
|
|
|
+4. **保证兼容性**:为 PHP 9.0 的完全禁止动态属性做好准备
|
|
|
|
|
|
-1. **继承正确的基类**: ValidationCore 而不是 Validation
|
|
|
-2. **使用正确的规则格式**: 数组格式而不是字符串格式
|
|
|
-3. **正确实现 Validator**: 使用标准的方法签名和参数访问方式
|
|
|
-4. **合理的参数传递**: 通过 args 传递字段键名,通过 data 获取数据
|
|
|
-5. **清晰的错误信息**: 提供有意义的错误提示
|
|
|
+记住:**预定义属性 + 类型声明 + 清晰注释 = 更好的代码**
|