我有几个复杂的域对象。我使用 Symfony 表单来验证对它们的 REST 请求。假设,我有域Banner。而且我有用于通过 ID 使用BannerFormType创建/更新具体横幅的端点。横幅有限制:每日和总计。业务规则是“两个限制都是必需的,但每天应该小于或等于总数”。所以我们有一种情况,我们应该使用自定义类约束来验证两个相互链接的字段。
所以,我的表格看起来像:
<?php
declare(strict_types=1);
namespace App;
class BannerFormType extends \Symfony\Component\Form\AbstractType
{
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder, array $options): void
{
$builder
->add('daily_limit', \Symfony\Component\Form\Extension\Core\Type\IntegerType::class, [
'constraints' => [
new \Symfony\Component\Validator\Constraints\NotBlank(),
],
])
->add('total_limit', \Symfony\Component\Form\Extension\Core\Type\IntegerType::class, [
'constraints' => [
new \Symfony\Component\Validator\Constraints\NotBlank(),
],
])
;
}
public function configureOptions(\Symfony\Component\OptionsResolver\OptionsResolver $resolver): void
{
parent::configureOptions($resolver);
$resolver->setDefaults([
'data_class' => Banner::class,
'constraints' => [
new BannerLimits(),
],
]);
}
}
我们该约束的代码如下所示:
<?php
declare(strict_types=1);
namespace App;
class BannerLimitsValidator extends \Symfony\Component\Validator\ConstraintValidator
{
public function validate($banner, \Symfony\Component\Validator\Constraint $constraint): void
{
if (!$banner instanceof Banner) {
throw new \Symfony\Component\Validator\Exception\UnexpectedTypeException($banner, Banner::class);
}
if (!$constraint instanceof BannerLimits) {
throw new \Symfony\Component\Validator\Exception\UnexpectedTypeException($constraint, BannerLimits::class);
}
/** @var \Symfony\Component\Form\FormInterface $form */
$form = $this->context->getObject();
$totalLimit = $form->get('total_limit')->getNormData();
$dailyLimit = $form->get('daily_limit')->getNormData();
if ($totalLimit < $dailyLimit) {
$this->context
->buildViolation('Daily limit should be less or equal than total.')
->atPath('daily_limit')
->addViolation();
}
}
}
一切正常,除了我们有一个非常耦合的验证器。如果我们想让它重用于其他形式,就会出现问题。例如,我想添加批量端点来更新横幅,因此它将使用BannerListFormType。
当然,我可以通过选项在约束中传递字段数据,但是对于嵌套表单来说很难做到。另外,我可以直接在表单事件中调用验证器,但它看起来很奇怪。
此外,使用我的方法,由于与表单结构的耦合约束,很难使用\Symfony\Component\Validator\Test\ConstraintValidatorTestCase对验证器进行单元测试。
你能给我什么建议?你们是怎么解决这样的问题的?