Подтвердить что ты не робот

Как динамически контролировать проверку формы?

У меня есть некоторые проблемы с обработкой проверки формы Symfony. Я хотел бы проверить форму, привязанную к сущности на основе ее данных. Существует довольно много информации о том, как динамически изменять поля с помощью FormEvents. То, что мне не хватает в этом разделе, - это управление/изменение проверки.

Мой упрощенный вариант использования:

  • Пользователь может добавить событие в календарь.
  • Проверка проверяет, есть ли уже событие.
  • Если есть столкновение, проверка приведет к ошибке.
  • Пользователь должен теперь игнорировать эту ошибку/предупреждение.

Валидация реализуется как Validator с Constraint::CLASS_CONSTRAINT в качестве цели (поскольку она учитывает некоторые другие вещи).

Я попытался:

  • Взломайте группы проверки, но не смогли найти доступ к валидаторам сущности.
  • Взломайте FormEvents и добавьте дополнительное поле, например "Предупреждение о предупреждении даты".
  • Взломайте кнопку отправки, чтобы изменить ее на что-то вроде "Force submit".

... но так и не нашел рабочего решения. Даже более простые хаки с единственным средством проверки подлинности на основе свойств не сработали.: (

Есть ли способ Symfony для динамического управления проверкой?

Изменить: мой код выглядит следующим образом:

use Doctrine\ORM\Mapping as ORM;
use Acme\Bundle\Validator\Constraints as AcmeAssert;

/**
 * Appointment
 *
 * @ORM\Entity
 * @AcmeAssert\DateIsValid
 */
class Appointment
{
  /**
   * @ORM\Column(name="title", type="string", length=255)
   *
   * @var string
   */
  protected $title;

  /**
   * @ORM\Column(name="date", type="date")
   *
   * @var \DateTime
   */
  protected $date;
}

Валидатор, используемый в качестве службы:

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
 * 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 Symfony\Component\Validator\Constraint;

/**
 * @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: AcmeBundle\Validator\Constraints\DateValidator
        arguments: ["@acme.other_service"]
        tags:
            - { name: validator.constraint_validator, alias: acme.validator.appointment.date }
4b9b3361

Ответ 1

Валидация выполняется над сущностью, все формы - это выполнение проверки объекта. Вы можете выбирать группы на основе представленных данных

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

У меня возникли проблемы при использовании этого подхода на встроенных формах && & cascade-validation

Изменить: использование флэш-памяти для определения того, должна ли выполняться проверка.

// 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');
            }
        },
    ));
}

Ответ 2

Как ваш пользователь взаимодействует с приложением, чтобы сообщить ему игнорировать предупреждение? Есть ли какая-то дополнительная кнопка? В этом случае вы можете просто проверить кнопку, используемую для отправки формы, или добавить какое-то скрытое поле (ignore_validation) и т.д. Везде, где вы в конечном итоге получаете этот пользовательский ввод (вставка флэш-памяти и зависимостей, основанная на представленных данных и т.д.), Я бы затем использовал группы проверки и закрытие, чтобы определить, что проверять (как juanmf объясняется в его ответе).

Повторите свой второй подход (события формы), вы можете добавить приоритет для прослушивателей событий: как вы можете видеть в Symfony Form Validation Event Listener, они используют FormEvents::POST_SUBMIT для начала процесса проверки. Поэтому, если вы просто добавляете прослушиватель событий, он вызывается перед прослушивателем валидации, поэтому проверки еще не произошли. Если вы добавите отрицательный приоритет вашему слушателю, вы также сможете получить доступ к ошибкам проверки формы:

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

Ответ 3

Старый вопрос, но...

Я бы сначала добавил поле (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);
    }
}

Ответ 4

Я думаю, что вы столкнулись с проблемой, потому что используете неправильную концепцию. Решение, проверка которого должна выполняться, принадлежит контроллеру, а не валидатору.

Итак, я просто проверил бы контроллер, на который нажата кнопка отправки (или там, где установлен флажок), и переключайте группы проверки. Однако форма должна быть визуально различной, поэтому я, вероятно, создаю 2 формы для обоих состояний (оба расширяют базовый или один тип формы, которые используют параметры).