Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony validate form with mapped false form fields

I've got a form with extra fields added with the option mapped to false. But when I try to validate my form, it won't pass indicating "this value is not valid" above these specific form fields. Isn't this option supposed to bypass validation?

These form fields are only useful for populate other fields and I don't need to save or even check them.

The only solution I found is to remove all extra fields with js on a submit button click.

like image 528
kzrdt Avatar asked Oct 16 '12 09:10

kzrdt


2 Answers

This post is not up to date with Symfony 2.3

read comments bellow

A new version is comming !

Validating non mapped fields in Form (Symfony 2.1.2)

This is a global response for some stackoverflow questions about current way to validate unbounded or non mapped field in forms.

  • validation on unbound form field
  • how to add validation to unbound form field in symfony2
  • a more....

The rich Symfony 2 ecosystem makes our framework of choice a fast evolving tool.
Symfony 2.1 version brings a lot of deprecations. This means that what is working with Symfony 2.0 to 2.1.2 will no longer work in Symfony 2.3. For more information about this, read UPGRADE FROM Symfony 2.0 to 2.1 and read @deprecated comments in Symfony code.

Unbound fields

When building a form, you usually use Entities, and your validation can be made in the Entity itself tanks to Validation annotations.

namespace Dj\TestBundle\Entity;  use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert;  /**      * Dj\TestBundle\Entity\Post  *  * @ORM\Table()  * @ORM\Entity(repositoryClass="Dj\TestBundle\Entity\PostRepository")  */ class Post {     // ... some code      /**     * @var string $title     * @ORM\Column(name="title", type="string", length=200, nullable=false)     * @Assert\NotBlank()     */     private $title;      // .. getters and setters } 

But sometimes (often) you need to insert some fields in your form that are not mapped to the model.

Our model example is like this :

namespace Dj\TestBundle\Entity;  use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert;  /**  * Dj\TestBundle\Entity\Post  *  * @ORM\Table()  * @ORM\Entity(repositoryClass="Dj\TestBundle\Entity\PostRepository")  */ class Post {     /**      * @var integer $id      *      * @ORM\Column(name="id", type="integer")      * @ORM\Id      * @ORM\GeneratedValue(strategy="AUTO")      */     private $id;      /**ghjkl      * @var string $title      * @ORM\Column(name="title", type="string", length=200, nullable=false)      * @Assert\NotBlank()      */     private $title;      // ... getters and setters } 

If we want to add an extra field called myExtraField to our Form we do :

class PostType extends AbstractType {     public function buildForm(FormBuilderInterface $builder, array $options)     {         $builder->add('title')                 ->add('myExtraField', 'choice', array(                         'label' => 'myExtraField option :',                         'choices' => array(                             1 => 'Option One',                             2 => 'Option Wat !'                         ),                         'expanded' => true,                         'mapped' => false                    ));     }     // other methods } 

Note :

  • mapped replaces property_path that will be deprecated in Symfony 2.3
  • you can add a default selected value to myExtraField by adding a 'data' => 1 entry in your options array.

Example code :

$builder->add('title')     ->add('myExtraField', 'choice', array(         'label' => 'myExtraField option :',         'choices' => array(             1 => 'Option One',             2 => 'Option Wat !'         ),         'data' => 1, // default selected option         'expanded' => true,         'mapped' => false )); 

If you want to validate myExtraField field you can't do it in the Post Entity annotations, you have to do it in your form.

Validation non mapped field - the Symfony 2.0 way

The 2.0 way was to add a validator to the form builder ($builder->addValidator(..)), but this method is deprecated !

namespace Dj\TestBundle\Form;  use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface;  // needed namespaces for 2.0 validation use Symfony\Component\Form\CallbackValidator; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormError;  class PostType extends AbstractType {     public function buildForm(FormBuilderInterface $builder, array $options)     {         // ... $builder->add()          // VALIDATING NON MAPPED FIELD Symfony 2.0 way         /** @var Symfony\Component\Form\CallbackValidator $myExtraFieldValidator **/         $myExtraFieldValidator = new CallbackValidator(function(FormInterface $form){           $myExtraField = $form->get('myExtraField')->getData();             if (empty($myExtraField)) {               $form['myExtraField']->addError(new FormError("myExtraField must not be empty"));             }         });         // adding the validator to the FormBuilderInterface         $builder->addValidator($myExtraFieldValidator);     }     // ... other methods } 

This is currently validating the myExtraField field, BUT $builder->addValidator will die in Symfony 2.3 !

The Forward Compatible code

As stated in UPGRADE FROM Symfony 2.0 to 2.1, as the FormValidatorInterface is deprecated, we now have to pass our validation closure function to an event listener bound to FormEvents::POST_BIND event.

This is the code.

namespace Dj\TestBundle\Form;  use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface;  // needed namespaces for 2.1 validation use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormError;  class PostType extends AbstractType {     public function buildForm(FormBuilderInterface $builder, array $options)     {         // ... $builder->add()          // VALIDATING NON MAPPED FIELD Symfony 2.1.2 way (and forward)         /** @var \closure $myExtraFieldValidator **/         $myExtraFieldValidator = function(FormEvent $event){             $form = $event->getForm();             $myExtraField = $form->get('myExtraField')->getData();             if (empty($myExtraField)) {               $form['myExtraField']->addError(new FormError("myExtraField must not be empty"));             }         };          // adding the validator to the FormBuilderInterface         $builder->addEventListener(FormEvents::POST_BIND, $myExtraFieldValidator);     }     // ... other methods } 

This can certainly be improved with some Sf gurus help, but for now it's validates unbound form field in a forward compatible way.

Hope that helps unstuck some of us.

David

like image 129
David Jacquel Avatar answered Oct 11 '22 16:10

David Jacquel


As I mentionned in a question on a similar topic, since Symfony 2.1, you should use the 'constraints' option to add validation to your un-mapped fields:

use Symfony\Component\Validator\Constraints\MinLength; use Symfony\Component\Validator\Constraints\NotBlank;  $builder     ->add('firstName', 'text', array(         'constraints' => new MinLength(3),     ))     ->add('lastName', 'text', array(         'constraints' => array(             new NotBlank(),             new MinLength(3),         ),     )) ; 

Hope it will help anybody like me who lost some time on this...

like image 27
Żabojad Avatar answered Oct 11 '22 15:10

Żabojad