在一些场景下我们会对数组进行验证。如果你之前使用过 Laravel 可能这块已经很熟悉了的,但 ThinkPHP6 现在是不支持的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public function index () { $data = [ 'list' => [ [ 'email' => 'hong@hongfs.cn', ], [ 'email1' => 'hong@hongfs.cn', 'value' => 'https://www.hongfs.cn/', ], ], ];
try { $this->validate($data, [ 'list' => 'require|array', 'list.*.email' => 'require|email', 'list.1.value' => 'require|url', ]); } catch (\think\exception\ValidateException $v) { return $v->getMessage(); }
return '验证成功'; }
|
运行后我们会得到 list.*.email不能为空
。通过追踪函数的执行,执行开始验证是位于 vendor/topthink/framework/src/think/Validate.php|check
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public function check(array $data, array $rules = []): bool { $this->error = [];
foreach ($rules as $key => $rule) { if (strpos($key, '|')) { [$key, $title] = explode('|', $key); } else { $title = $this->field[$key] ?? $key; }
$value = $this->getDataValue($data, $key);
if ($rule instanceof Closure) { $result = call_user_func_array($rule, [$value, $data]); } elseif ($rule instanceof ValidateRule) { $result = $this->checkItem($key, $value, $rule->getRule(), $data, $rule->getTitle() ?: $title, $rule->getMsg()); } else { $result = $this->checkItem($key, $value, $rule, $data, $title); } }
return true; }
|
通过上面的删减后的代码,可以了解到验证条件会通过 foreach 进行单独处理,然后获取这个验证条件需要的数据($this->getDataValue
)。打印 $value
的值,会发现我们验证不通过的值返回是 null
。
最上面的代码其实也明示我们要兼容的验证规则了。.*.
代表这个数组所有条数都需要验证,.1.
代表我们要验证数组中索引为 1
的(只支持数字索引)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
|
protected function getDataValue(array $data, $key) { if (is_numeric($key)) { $value = $key; } elseif (is_string($key) && substr_count($key, '.') === 2) {
$index = strpos($key, '.');
$list = $this->getDataValue($data, substr($key, 0, $index));
$value = [];
if(!is_array($list)) { } elseif (strpos($key, '.*.') !== false) { $_key = substr($key, strrpos($key, '.') + 1);
foreach ($list as $item_key => $item_value) { $value[$item_key] = $item_value[$_key] ?? null; }
unset($_key, $item_key, $item_value); } elseif (preg_match_all('/.\d./', $key)) { $_i = (int) explode('.', $key)[1];
$_key = substr($key, strrpos($key, '.') + 1);
$item = $list[$_i] ?? [];
$value = $item[$_key] ?? null;
unset($item, $_key, $_i); } else { } } elseif (is_string($key) && strpos($key, '.')) { foreach (explode('.', $key) as $key) { if (!isset($data[$key])) { $value = null; break; } $value = $data = $data[$key]; } } else { $value = $data[$key] ?? null; }
return $value; }
|
当验证条件存在 .*.
时,$this->getDataValue
返回的是一个一维数组,我们需要让 check
进行兼容处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public function check(array $data, array $rules = []): bool { foreach ($rules as $key => $rule) { $value = $this->getDataValue($data, $key);
if ($rule instanceof Closure) { $result = call_user_func_array($rule, [$value, $data]); } elseif ($rule instanceof ValidateRule) { $result = $this->checkItem($key, $value, $rule->getRule(), $data, $rule->getTitle() ?: $title, $rule->getMsg()); } elseif (strpos($key, '.*.') !== false) {
foreach ($value as $item_index => $item_value) { $result = $this->checkItem($key, $item_value, $rule, $data, $title);
if(true !== $result) { break; } } } else { $result = $this->checkItem($key, $value, $rule, $data, $title); } } }
|
这时如果重新执行代码,就能收到 验证成功
。
为了封装验证键值有问题,我们可能需要在循环的开始阻止一些非法的内容。
1 2 3 4 5 6 7 8
| public function check(array $data, array $rules = []): bool { foreach ($rules as $key => $rule) { if(strpos($key, '.*.') === 0) { throw new \Exception('验证规则异常'); } } }
|