I have a form that combines two entities (User and Profile).
Validation seems to work on the first part of the form that comes form the User Entity and is the basis
are you using YML or Annotations?
I tried applying the cascade_validation
option on my parent form class, but validation was still not occurring. After reading a little documentation, I went to app/config/config.yml
and found that enable_annotations
under framework->validation
was set to true. Apparently, if this is true, the validation service no loner reads any validation.yml files. So I just changed it to false, and now the form is validating fine.
You have to add validation_groups
in your ProfiletType
also. Validation is done in each form type separately based on their data_class
if exists.
Belongs to Symfony 2.3
Working with embedded forms and validation groups could be quite painful: Annotation @Assert\Valid() doesen't work for me (without groups it is ok). Insert 'cascade_validation' => true on the DefaultOptions is the key. You do not need to repeat this on the ->add(). Take care: HTML 5 validation do not work together with validation groups.
Example:
A Collection of 2 Addresses. Both 1:1 unidirectional. Each with a different (!) validation group.
class TestCollection{
//(...)
/**
* @var string
* @Assert\NotBlank(groups={"parentValGroup"})
* @ORM\Column(name="name", type="string", length=255, nullable=true)
*/
protected $name;
/**
* @var \Demo\Bundle\Entity\TestAddress
* @Assert\Type(type="Demo\Bundle\Entity\TestAddress")
* @ORM\OneToOne(targetEntity="TestAddress",cascade={"persist","remove"},orphanRemoval=true)
* @ORM\JoinColumn(name="billing_address__id", referencedColumnName="id")
*/
protected $billingAddress;
/**
* @var \Demo\Bundle\Entity\TestAddress
* @Assert\Type(type="Demo\Bundle\Entity\TestAddress")
* @ORM\OneToOne(targetEntity="TestAddress",cascade={"persist","remove"}, orphanRemoval=true)
* @ORM\JoinColumn(name="shipping_address__id", referencedColumnName="id")
*/
protected $shippingAddress;
//(...)
}
Address Entity
class TestAddress {
/**
* @var string
* @Assert\NotBlank(groups={"firstname"})
* @ORM\Column(name="firstname", type="string", length=255, nullable=true)
*/
private $firstname;
/**
* @var string
* @Assert\NotBlank(groups={"lastname"})
* @ORM\Column(name="lastname", type="string", length=255, nullable=true)
*/
private $lastname;
/**
* @var string
* @Assert\Email(groups={"firstname","lastname"})
* @ORM\Column(name="email", type="string", length=255, nullable=true)
*/
private $email;
Address type - ability to change validation group
class TestAddressType extends AbstractType {
protected $validation_group=['lastname'];//switch group
public function __construct($validation_group=null) {
if($validation_group!=null) $this->validation_group=$validation_group;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
//disable html5 validation: it suchs with groups
$builder
->add('firstname',null,array('required'=>false))
->add('lastname',null,array('required'=>false))
->add('email',null,array('required'=>false))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Demo\Bundle\Entity\TestAddress',
'validation_groups' => $this->validation_group,
));
}
(...)
And last the CollectionType
class TestCollectionType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{ $builder
->add('name')
->add('billingAddress', new TestAddressType(['lastname','firstname']))
->add('shippingAddress', new TestAddressType(['firstname']))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Demo\Bundle\Entity\TestCollection',
'validation_groups' => array('parentValGroup'),
'cascade_validation' => true
));
}
//(...)
Hope it helps..
I spent an age searching and found that it was adding 'cascade_validation' => true
to the setDefaults()
array in my parent type's class that fixed it (as mentioned already in the thread). This causes the entity constraint validation to trigger in the child types shown in the form. e.g.
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
...
'cascade_validation' => true,
));
}
For collections, also make sure to add 'cascade_validation' => true
to the $options
array for the collection field on the form. e.g.
$builder->add('children', 'collection', array(
'type' => new ChildType(),
'cascade_validation' => true,
));
This will have the UniqueEntity validation take place as it should in the child entity used in the collection.
From my controller:
$form = $this->get('form.factory')
->createNamedBuilder('form_data', 'form', $item, array('cascade_validation' => true))
->add('data', new ItemDataType())
->add('assets', new ItemAssetsType($this->locale))
->add('contact', new ItemContactType())
->add('save', 'submit',
array(
'label' => 'Save',
'attr' => array('class' => 'btn')
)
)
->getForm();
Fourth parametr in ::createNamedBuilder - array('cascade_validation' => true))
A note to those using Symfony 3.0 and up: the cascade_validation
option has been removed. Instead, use the following for embedded forms:
$builder->add('embedded_data', CustomFormType::class, array(
'constraints' => array(new Valid()),
));
Sorry for adding to this old thread with a slightly off-topic answer (Symfony 3 vs. 2), but finding this information here would have saved me a few hours today.