在未映射的字段上添加自定义验证器,但是使用整个表单提交的上下文?

时间:2014-05-05 19:45:17

标签: symfony symfony-forms

TL; DR:

  1. 我需要一个自定义验证约束才能在未映射的表单字段上运行
  2. 我需要表单正在操作的对象的ID,以免考虑执行我的验证约束
  3. 将验证附加到表单本身或未映射的字段并没有给我足够的上下文来运行我的验证查询

  4. 我的Person实体表单上有一个未映射的字段,我需要对其进行验证。我已经关注this great article如何执行此操作,但我的用例略有不同,文章并未完全涵盖。

    我正在创建自己的Unique Constraint,需要运行自定义查询来确定唯一性。要运行查询,我需要访问已提交的字段值以及原始Person对象(如果它是更新操作,我可以获取它的ID )。如果没有Person对象,我就无法在唯一性查询中将其排除在考虑之外。

    如果我在PersonType类上应用验证器,如下所示:

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver
            ->setDefaults(array(
                'data_class' => 'CS\AcmeBundle\Entity\Person',
                'constraints' => array(
                    new MyUniqueValidator(),
                )
            ))
        ;
    }
    

    然后验证器将传递整个Person对象以执行验证。这对我没有帮助,因为提交的表单数据不会持久保存到Person对象(它是在控制器中调用$form->isValid()后我处理的未映射字段)。

    如果我直接将验证器应用于未映射的字段:

        $builder
            ->add('myUnmappedField', 'text', array(
                'mapped' => false,
                'constraints' => array(
                    new MyUniqueValidator(),
                  )
                ),
            ))
    

    然后我传递给验证器的对象只是独立的表单文本,没有别的。我没有要通过唯一性查询执行ID Person对象(如果它是更新操作)。

    希望我已经正确地解释了这一点。我有任何选择可以优雅地进行这种验证吗?

2 个答案:

答案 0 :(得分:6)

你说你有未映射的字段。如果你把它映射到Person实体会有帮助吗?只需使用getter和setter方法在Person类中创建一个新属性,但不要在ORM中创建一个新属性,因为您不希望它持久存在。

如果您不想对Person类进行轮询,您还可以创建另一个复合类,它将保存当前未映射的字段和Person对象(然后您将对其进行映射)。然后,您将设置data_class以匹配新对象的命名空间。

上述两种解决方案都应该与您拥有的上层代码一起使用。请让我知道它有帮助。

答案 1 :(得分:0)

您可以使用callback constraint with a closure来实现。

要访问您的Person实体,您将需要通过事件监听器添加该字段。

$builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) {

    $form = $event->getForm();
    $person = $event->getData();

    $form->add('myUnmappedField', TextType::class, [
        'mapped' => false,
        'constraints' => [
            new Symfony\Component\Validator\Constraints\Callback([
                'callback' => function ($value, ExecutionContextInterface $context) use ($person) {
                    // Here you can use $person->getId()
                    // $value is the value of the unmapped field
                }
            ])
        ],
    ]);
});