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.
This is a global response for some stackoverflow questions about current way to validate unbounded or non mapped field in forms.
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.
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 :
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.
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 !
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