Question

tl;dr:

  1. I need a custom validation constraint to run on an unmapped form field
  2. I need the ID of the object the form is manipulating to eliminate it from consideration doing my validation constraint
  3. Attaching the validation to the form itself or the unmapped field doesn't give me enough context to run my validation query

I have an unmapped field on my Person entity form that I need to run a validation on. I've followed this great article on how to do this, but my use case is slightly different and not entirely covered by the article.

I am making my own Unique Constraint that needs to run a custom query to determine the uniqueness. To run the query, I need access to the field value that was submitted, as well as the original Person object (so I can get it's ID if it's an update operation). Without also having the that Person object I won't be able to eliminate it from consideration during the uniqueness query.

If I apply the validator on the PersonType class like so:

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

Then the validator gets passed the entire Person object to perform the validation on. This doesn't help me, because the submitted form data is not persisted to the Person object (it's an unmapped field that I handle after $form->isValid() is called in the controller).

If I apply the validator to the unmapped field directly instead:

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

Then the object I get passed to the validator is just the standalone form text, and nothing else. I don't have the ID Person object (if it was an update operation) to perform by uniqueness query.

Hopefully I've explained this properly. Do I have any options to do this sort of validation gracefully?

Was it helpful?

Solution

You say you have unmapped field. Would it help, if you make it mapped to the Person entity? Just make a new property in the Person class with getter and setter methods, but not to the ORM, since you don't want it persisted.

If you do not want to polute your Person class, you can also make another composite class, which will hold your currently unmapped field and a Person object (you will then make it mapped). Ofcourse you will then set data_class to match the new object's namespace.

Both above solutions should work with the upper code you have there. Please let me know it it helped.

OTHER TIPS

You can achieve this by using a callback constraint with a closure.

To access your Person entity, you will need to add the field via an event listener.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top