# Validation 使用示例 ## 重要说明:动态属性规范 **从 PHP 8.2+ 开始,动态属性被弃用。为了代码的健壮性和可维护性,我们要求:** ### ❌ 不允许的做法 ```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 1], // 业务验证(按顺序执行) [ 'land_id', new LandOwnershipValidator($this, ['user_id', 'land']), 'msg' => '土地不存在或不属于当前用户' ], [ 'land_id', new LandUpgradeStatusValidator($this, ['land']), 'msg' => '土地状态不允许升级' ], [ 'land_id', new LandUpgradePathValidator($this, ['user_id', 'upgrade_config']), 'msg' => '升级路径验证失败' ], ]; } /** * 默认值 */ public function default(): array { return []; } } ``` ### 2. 创建 Validator 类 ```php args[0] ?? 'user_id'; $landKey = $this->args[1] ?? 'land'; $userId = $data[$userIdKey] ?? null; if (!$userId) { $this->addError('用户ID不能为空'); return false; } try { // 查询土地 $land = FarmLand::where('id', $value) ->where('user_id', $userId) ->first(); if (!$land) { $this->addError('土地不存在或不属于当前用户'); return false; } // 将土地对象保存到验证对象中,供后续验证器使用 $this->validation->$landKey = $land; return true; } catch (\Exception $e) { $this->addError('验证土地归属时发生错误: ' . $e->getMessage()); return false; } } } ``` ### 3. 在 Handler 中使用 ```php 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 class ExampleValidation extends ValidationCore { // ✅ 正确:预定义属性,声明类型,添加注释 /** @var User|null 用户对象,由 UserValidator 设置 */ public ?User $user = null; /** @var array|null 物品列表,由 ItemValidator 设置 */ public ?array $items = null; /** @var Config|null 配置对象,由 ConfigValidator 设置 */ public ?Config $config = null; // ❌ 错误:没有类型声明 // public $user; // ❌ 错误:没有注释说明 // public ?User $user = null; } ``` ### 2. Validator 中的属性赋值 ```php class ExampleValidator extends Validator { public function validate(mixed $value, array $data): bool { // 获取数据 $user = User::find($value); if (!$user) { $this->addError('用户不存在'); return false; } // ✅ 正确:赋值给预定义的属性 $this->validation->user = $user; // ❌ 错误:动态属性赋值(会产生弃用警告) // $this->validation->dynamic_property = $user; return true; } } ``` ### 3. 验证器间数据传递 ```php class FirstValidator extends Validator { public function validate(mixed $value, array $data): bool { $user = User::find($value); // 第一个验证器设置用户对象 $this->validation->user = $user; return true; } } class SecondValidator extends Validator { public function validate(mixed $value, array $data): bool { // 第二个验证器使用第一个验证器设置的用户对象 $user = $this->validation->user; if (!$user) { $this->addError('用户信息不存在,请先验证用户'); return false; } // 使用用户对象进行验证... return $user->isActive(); } } ``` ### 4. 类型安全的好处 ```php 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 会警告属性不存在 ``` ### 5. 迁移现有代码 如果你有使用动态属性的现有代码,按以下步骤迁移: **步骤 1:识别动态属性** ```bash # 搜索可能的动态属性赋值 grep -r "\$this->validation->" app/Module/*/Validators/ ``` **步骤 2:在 Validation 类中定义属性** ```php // 在相应的 Validation 类中添加属性定义 class YourValidation extends ValidationCore { /** @var SomeModel|null 描述这个属性的用途 */ public ?SomeModel $property_name = null; } ``` **步骤 3:更新验证器代码** ```php // 验证器中的赋值保持不变,但现在是类型安全的 $this->validation->property_name = $value; ``` **步骤 4:测试验证** ```bash # 运行测试确保没有破坏现有功能 php artisan test ``` ## 常见问题 ### Q: 为什么要禁用动态属性? A: 1. **类型安全**:预定义属性可以声明类型,IDE 和静态分析工具可以提供更好的支持 2. **代码可读性**:明确的属性定义让代码更容易理解和维护 3. **错误预防**:避免因拼写错误导致的 bug 4. **PHP 兼容性**:为 PHP 9.0 做准备(将完全禁止动态属性) ### Q: 如何处理可选的验证器数据? A: 使用 nullable 类型和默认值: ```php /** @var OptionalData|null 可选数据,可能为 null */ public ?OptionalData $optional_data = null; ``` ### Q: 如何处理数组类型的数据? A: 使用泛型注释: ```php /** @var array|null 物品数组 */ public ?array $items = null; /** @var array|null 配置数组 */ public ?array $config = null; ``` --- ## 总结 通过遵循这些规范和最佳实践,我们可以: 1. **提高代码质量**:类型安全的属性定义让代码更健壮 2. **改善开发体验**:IDE 可以提供更好的类型提示和错误检查 3. **增强可维护性**:明确的属性定义让代码更容易理解和维护 4. **保证兼容性**:为 PHP 9.0 的完全禁止动态属性做好准备 记住:**预定义属性 + 类型声明 + 清晰注释 = 更好的代码**