notfff 7 mēneši atpakaļ
vecāks
revīzija
068845a281
1 mainītis faili ar 279 papildinājumiem un 122 dzēšanām
  1. 279 122
      docs/Validation使用示例.md

+ 279 - 122
docs/Validation使用示例.md

@@ -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. **清晰的错误信息**: 提供有意义的错误提示
+记住:**预定义属性 + 类型声明 + 清晰注释 = 更好的代码**