14

Symfony のフォーム検証処理に問題があります。データに基づいてエンティティにバインドされたフォームを検証したいと思います。FormEventsを使用してフォームフィールドを動的に変更する方法については、非常に多くの情報があります。このトピックで欠けているのは、検証を制御/変更する方法です。

私の単純化されたユースケースは次のとおりです。

  1. ユーザーはカレンダーにイベントを追加できます。
  2. 検証では、イベントが既に存在するかどうかがチェックされます。
  3. 競合がある場合、検証でエラーがスローされます。
  4. ユーザーは、このエラー/警告を無視できるようになりました。

検証は、ターゲットとしてValidatorwithConstraint::CLASS_CONSTRAINTとして実装されます (さらにいくつかの要素が考慮されているため)。

私がしようとしました:

  • 検証グループをハックしましたが、エンティティ全体のバリデーターへのアクセスが見つかりませんでした。
  • をハックして、FormEvents「日付の警告を無視する」などのフィールドを追加します。
  • 送信ボタンをハックして、「強制送信」のようなものに変更します。

...しかし、実用的な解決策は見つかりませんでした。単一のプロパティー・ベースのバリデーターを使用したさらに単純なハックはうまくいきませんでした。:(

検証を動的に制御する 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 }
4

4 に答える 4

2

検証はエンティティに対して行われ、フォームが行うのはオブジェクトの検証を実行することだけです。提出されたデータに基づいてグループを選択できます

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

埋め込みフォームでこのアプローチを使用すると問題が発生しました&&カスケード検証

編集:フラッシュを使用して、検証を行う必要があるかどうかを判断します。

// 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');
            }
        },
    ));
}
于 2014-07-12T00:32:22.127 に答える
1

ユーザーはどのようにアプリケーションとやり取りして、警告を無視するように指示しますか? 追加のボタンのようなものはありますか?その場合、フォームの送信に使用されるボタンをチェックするか、ある種の非表示フィールド ( ignore_validation) などを追加するだけで済みます。送信されたデータに基づくフラッシュと依存性注入など) からそのユーザー入力を取得する場所はどこでも、私は次に、検証グループとクロージャーを使用して、何を検証するかを決定します(juanmfが回答で説明したように)。

2 番目のアプローチ (フォーム イベント) では、イベント リスナーに優先度を追加できます。Symfony のフォーム検証イベント リスナーFormEvents::POST_SUBMITでわかるように、検証プロセスの開始に使用します。したがって、イベント リスナーを追加しただけでは、検証リスナーの前に呼び出されるため、検証はまだ行われていません。リスナーに負の優先度を追加すると、フォーム検証エラーにもアクセスできるはずです。

$builder->addEventListener(FormEvents::POST_SUBMIT, function(){...}, -900);
于 2014-09-30T09:28:19.487 に答える