Symfony Assert\Collection约束如何实现数组元素关联校验
Symfony 固定长度数组跨元素关联校验实现方案
Symfony Validator组件原生支持跨元素关联校验,Assert\Collection仅负责数组内单个元素的独立规则校验,涉及多元素关联判断可以用以下两种方案实现,无需引入第三方扩展。
方案1:Callback回调约束(快速实现,无需额外类)
如果这套校验规则只在单个场景使用,直接在Collection约束同级添加Assert\Callback回调约束,即可拿到完整数组做关联逻辑判断:
'constraints' => [ new Assert\Collection([ 'fields' => [ 0 => new Assert\Range([ 'min' => 0, 'max' => 1000, 'notInRangeMessage' => '第一个数值取值范围需在0-1000之间' ]), 1 => new Assert\Range([ 'min' => 0, 'max' => 1000, 'notInRangeMessage' => '第二个数值取值范围需在0-1000之间' ]), ] ]), // 跨元素关联校验逻辑 new Assert\Callback(function ($arr, $context) { // 数组结构不符合预期时直接返回,由Collection约束报结构错误 if (!is_array($arr) || count($arr) !== 2) { return; } [$firstNum, $secondNum] = $arr; // 校验第二个数大于第一个 if ($secondNum <= $firstNum) { $context->buildViolation('第二个数值必须大于第一个数值') ->atPath('[1]') // 错误信息挂载到第二个数组元素下 ->addViolation(); } // 校验两数差值不超过100 if (($secondNum - $firstNum) > 100) { $context->buildViolation('两个数值的差值不能超过100') ->atPath('[1]') ->addViolation(); } }) ]
方案2:自定义约束类(适合规则多场景复用)
如果这套两数区间校验逻辑需要在多个表单/校验场景复用,可以封装成独立的自定义约束,避免重复写回调逻辑。
- 首先创建约束注解类:
// src/Validator/Constraints/RangePair.php namespace App\Validator\Constraints; use Symfony\Component\Validator\Constraint; #[\Attribute] class RangePair extends Constraint { public string $secondSmallerMessage = '第二个数值必须大于第一个数值'; public string $diffExceededMessage = '两个数值的差值不能超过%maxDiff%'; public int $maxDiff = 100; public int $singleMin = 0; public int $singleMax = 1000; }
- 创建对应的校验逻辑实现类:
// src/Validator/Constraints/RangePairValidator.php namespace App\Validator\Constraints; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Exception\UnexpectedValueException; class RangePairValidator extends ConstraintValidator { public function validate($value, Constraint $constraint) { if (!$constraint instanceof RangePair) { throw new UnexpectedTypeException($constraint, RangePair::class); } if (null === $value || '' === $value) { return; } if (!is_array($value) || count($value) !== 2) { throw new UnexpectedValueException($value, '包含2个整数元素的数组'); } [$first, $second] = $value; if ($second <= $first) { $this->context->buildViolation($constraint->secondSmallerMessage) ->atPath('[1]') ->addViolation(); return; } if (($second - $first) > $constraint->maxDiff) { $this->context->buildViolation($constraint->diffExceededMessage) ->setParameter('%maxDiff%', $constraint->maxDiff) ->atPath('[1]') ->addViolation(); } } }
- 使用时直接调用自定义约束即可:
'constraints' => [ new Assert\Collection([ 'fields' => [ 0 => new Assert\Range(min: 0, max: 1000), 1 => new Assert\Range(min: 0, max: 1000), ] ]), new RangePair(maxDiff: 100) // 可根据场景自定义差值阈值 ]
校验效果验证
- 传入
[13, 64]:单值范围合规、第二个数大于第一个、差值51小于100,校验通过 - 传入
[140, 64]:触发「第二个数值必须大于第一个数值」错误,校验不通过 - 传入
[13, 340]:触发「两个数值的差值不能超过100」错误,校验不通过
内容的提问来源于stack exchange,提问作者Clem Mi
相关产品推荐
相关产品推荐

