如何动态控制表单的验证



我在Symfony的表单验证处理中遇到了一些问题。我想根据其数据验证绑定到实体的表单。有相当多的信息如何动态修改表单字段使用FormEvents。关于这个主题,我缺少的是如何控制/修改验证。

我的简化用例是:

  1. 用户可以添加一个事件到日历。
  2. 验证检查是否已经存在事件。
  3. 如果发生碰撞,验证将抛出错误。
  4. 用户现在应该可以忽略这个错误/警告。

验证是作为Validator实现的,Constraint::CLASS_CONSTRAINT是目标(因为它考虑了更多的东西)。

I tried to:

  • 绕过验证组,但无法找到访问实体范围验证器的权限。
  • Hack周围的FormEvents和添加一个额外的字段,如"忽略日期警告"。绕过提交按钮,将其更改为"强制提交"之类的内容。

…但从来没有找到一个有效的解决方案。即使是使用基于单个属性的验证器的更简单的方法也行不通。(

是否有一个Symfony的方式来动态控制验证?

编辑:我的代码看起来像这样:
use DoctrineORMMapping as ORM;
use AcmeBundleValidatorConstraints as AcmeAssert;
/**
 * Appointment
 *
 * @ORMEntity
 * @AcmeAssertDateIsValid
 */
class Appointment
{
  /**
   * @ORMColumn(name="title", type="string", length=255)
   *
   * @var string
   */
  protected $title;
  /**
   * @ORMColumn(name="date", type="date")
   *
   * @var DateTime
   */
  protected $date;
}

作为服务使用的验证器:

use SymfonyComponentValidatorConstraint;
use SymfonyComponentValidatorConstraintValidator;
/**
 * Validates the date of an appointment.
 */
class DateIsValidValidator extends ConstraintValidator
{
    /**
     * {@inheritdoc}
     */
    public function validate($appointment, Constraint $constraint)
    {
        if (null === $date = $appointment->getDate()) {
            return;
        }
        /* Do some magic to validate date */
        if (!$valid) {
            $this->context->addViolationAt('date', $constraint->message);
        }
    }
}

对应的Constraint类设置为针对实体类。

use SymfonyComponentValidatorConstraint;
/**
 * @Annotation
 */
class DateIsValid extends Constraint
{
    public $message = 'The date is not valid!';
    /**
     * {@inheritdoc}
     */
    public function getTargets()
    {
        return self::CLASS_CONSTRAINT;
    }
    /**
     * {@inheritdoc}
     */
    public function validatedBy()
    {
        return 'acme.validator.appointment.date';
    }
}
编辑2:尝试与FormEvents…我还尝试了所有不同的项目。
$form = $formFactory->createBuilder()
    ->add('title', 'text')
    ->add('date', 'date')
    ->addEventListener(FormEvents::WHICHONE?,  function(FormEvent $event) {
        $form = $event->getForm();
        // WHAT TO DO HERE?
        $form->getErrors(); // Is always empty as all events run before validation?
        // I need something like
        if (!$dateIsValid) {
            $form->setValidationGroup('ignoreWarning');
        }
    });

编辑3:约束声明正确。这不是问题所在:

services:
    validator.acme.date:
        class: AcmeBundleValidatorConstraintsDateValidator
        arguments: ["@acme.other_service"]
        tags:
            - { name: validator.constraint_validator, alias: acme.validator.appointment.date }

验证是在实体上完成的,所有表单所做的就是执行对象的验证。您可以根据提交的数据选择组

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'validation_groups' => function(FormInterface $form) {
            $data = $form->getData();
            if (EntityClient::TYPE_PERSON == $data->getType()) {
                return array('person');
            } else {
                return array('company');
            }
        },
    ));
}

我在嵌入式表单上使用这种方法时遇到了问题&&cascade-validation

编辑:使用flash来确定是否必须进行验证

// service definition
    <service id="app.form.type.callendar" class="%app.form.type.callendar.class%">
        <argument type="service" id="session" />
        <tag name="form.type" alias="my_callendar" />
    </service>

// some controller
public function somAvtion() 
{
    $form = $this->get('app.form.type.callendar');
    ...
}
// In the form
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'validation_groups' => function(FormInterface $form) {
            $session = $form->getSession();
            if ($session->getFlashBag()->get('callendar_warning', false)) {
                return array(false);
            } else {
                return array('Validate_callendar');
            }
        },
    ));
}

用户如何与应用程序交互以告诉它忽略警告?有什么额外的按钮吗?在这种情况下,您可以简单地检查用于提交表单的按钮或添加某种隐藏字段(ignore_validation)等。无论你最终从哪里获得用户输入(flash和依赖注入,基于提交的数据等),我都会使用验证组和闭包来确定要验证的内容(就像juanmf在他的回答中解释的那样)。

对于第二种方法(表单事件),你可以给事件监听器添加一个优先级:正如你在Symfony的表单验证事件监听器中看到的,它们使用FormEvents::POST_SUBMIT来启动验证过程。如果你只是添加一个事件监听器,它会在验证监听器之前被调用,所以验证还没有发生。如果您为侦听器添加负优先级,您应该还能够访问表单验证错误:

$builder->addEventListener(FormEvents::POST_SUBMIT, function(){...}, -900);

老问题但是…

我首先会在表单中添加一个字段(acceptCollision),正如你和上面的其他答案所建议的。

那么验证器就可以这样做:

public function validate($appointment, Constraint $constraint)
    {
    if (null === $date = $appointment->getDate()) {
        return;
    }
    if ($appointment->getAcceptCollision()) {
       $valid = true;
       } elseif (
                 // Check Unicity of the date (no collision)
                 ) {
                    $valid = true;
       } else {
         $valid = false;
       }
    if (!$valid) {
        $this->context->addViolationAt('date', $constraint->message);
    }
}

我认为你遇到了问题,因为你使用了错误的概念。应该运行哪个验证的决定属于控制器,而不是验证器。

所以我只需在控制器中检查提交按钮被按下(或者是否有一个复选框被选中)并切换验证组。然而,表单在视觉上应该是不同的,所以我可能会为这两种状态创建两个表单(都扩展一个基本表单或一个使用选项的表单类型)。

相关内容

  • 没有找到相关文章

最新更新