J'ai un formulaire qui combine deux entités (utilisateur et profil).
La validation semble fonctionner sur la première partie du formulaire qui vient de l'entité utilisateur et constitue la base du formulaire.
Le ProfileType est inclus dans le UserType. Le formulaire s'affiche correctement et affiche les informations correctes, il semble donc qu'il soit correctement connecté à l'entité Profil. C'est juste la validation qui est cassée sur le ProfileType.
Une idée de pourquoi une partie validerait et l'autre non?
Code ci-dessous:
Validation.yml
DEMO\DemoBundle\Entity\User\Profile:
properties:
address1:
- NotBlank: { groups: [profile] }
name:
- NotBlank: { groups: [profile] }
companyName:
- NotBlank: { groups: [profile] }
DEMO\DemoBundle\Entity\User\User:
properties:
username:
- NotBlank:
groups: profile
message: Username cannot be left blank.
email:
- NotBlank:
groups: profile
message: Email cannot be left blank
- Email:
groups: profile
message: The email "{{ value }}" is not a valid email.
checkMX: true
password:
- MaxLength: { limit: 20, message: "Your password must not exceed {{ limit }} characters." }
- MinLength: { limit: 4, message: "Your password must have at least {{ limit }} characters." }
- NotBlank: ~
UserType.php
namespace DEMO\DemoBundle\Form\Type\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackValidator;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormError;
use DEMO\DemoBundle\Form\Type\User\ProfileType;
class UserType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('username');
$builder->add('email');
$builder->add('profile', new ProfileType());
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'DEMO\DemoBundle\Entity\User\User',
'validation_groups' => array('profile')
);
}
public function getName()
{
return 'user';
}
}
ProfileType.php
namespace DEMO\DemoBundle\Form\Type\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackValidator;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormError;
class ProfileType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('name');
$builder->add('companyName', null, array('label' => 'Company Name'));
$builder->add('address1', null, array('label' => 'Address 1'));
$builder->add('address2', null, array('label' => 'Address 2'));
$builder->add('city');
$builder->add('county');
$builder->add('postcode');
$builder->add('telephone');
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'DEMO\DemoBundle\Entity\User\Profile',
);
}
public function getName()
{
return 'profile';
}
}
Manette
$user = $this->get('security.context')->getToken()->getUser();
$form = $this->createForm(new UserType(), $user);
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
if ($form->isValid()) {
// Get $_POST data and submit to DB
$em = $this->getDoctrine()->getEntityManager();
$em->persist($user);
$em->flush();
// Set "success" flash notification
$this->get('session')->setFlash('success', 'Profile saved.');
}
}
return $this->render('DEMODemoBundle:User\Dashboard:profile.html.twig', array('form' => $form->createView()));
J'ai passé un certain temps à chercher et j'ai trouvé qu'il ajoutait 'cascade_validation' => true
Au tableau setDefaults()
dans la classe de mon type parent qui l'a corrigé (comme mentionné déjà dans le fil). Cela provoque la validation de la contrainte d'entité à déclencher dans les types enfants affichés dans le formulaire. par exemple.
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
...
'cascade_validation' => true,
));
}
Pour les collections, assurez-vous également d'ajouter 'cascade_validation' => true
Au tableau $options
Pour le champ de collecte du formulaire. par exemple.
$builder->add('children', 'collection', array(
'type' => new ChildType(),
'cascade_validation' => true,
));
Cela aura la validation UniqueEntity avoir lieu comme il se doit dans l'entité enfant utilisée dans la collection.
Une note pour ceux qui utilisent Symfony 3.0 et plus: le cascade_validation
option a été supprimée . Utilisez plutôt les éléments suivants pour les formulaires intégrés:
$builder->add('embedded_data', CustomFormType::class, array(
'constraints' => array(new Valid()),
));
Désolé d'avoir ajouté à cet ancien fil avec une réponse légèrement hors sujet (Symfony 3 contre 2), mais trouver ces informations ici m'aurait fait gagner quelques heures aujourd'hui.
Selon documentation du type de formulaire vous pouvez également utiliser la contrainte Valid
au lieu de cascade_validation
option.
$builder->add('children', 'collection', array(
'type' => new ChildType(),
'constraints' => array(new Valid()),
));
Exemple de l'entité propriétaire:
/**
* @var Collection
*
* @ORM\OneToMany(targetEntity="Child", ...)
* @Assert\Valid()
*/
private $children
Appartient à Symfony 2.3
Travailler avec des formulaires intégrés et des groupes de validation pourrait être assez pénible: l'annotation @Assert\Valid () ne fonctionne pas pour moi (sans groupes, c'est ok). Insérer 'cascade_validation' => true sur DefaultOptions est la clé. Vous n'avez pas besoin de répéter cela sur le -> add (). Attention: la validation HTML 5 ne fonctionne pas avec les groupes de validation.
Exemple:
Une collection de 2 adresses. Les deux 1: 1 unidirectionnel. Chacun avec un groupe de validation différent (!).
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;
//(...)
}
Entité d'adresse
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;
Type d'adresse - possibilité de modifier le groupe de validation
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,
));
}
(...)
Et durer le 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
));
}
//(...)
J'espère que ça aide..
tilisez-vous YML ou des annotations?
J'ai essayé d'appliquer le cascade_validation
option sur ma classe de formulaire parent, mais la validation n'était toujours pas en cours. Après avoir lu un peu de documentation, je suis allé à app/config/config.yml
et a constaté que enable_annotations
en dessous de framework->validation
a été défini sur vrai. Apparemment, si cela est vrai, le service de validation no loner ne lit aucun fichier validation.yml. Je viens donc de le changer en false, et maintenant le formulaire est en cours de validation.
Vous devez ajouter validation_groups
dans votre ProfiletType
également. La validation est effectuée dans chaque type de formulaire séparément en fonction de leur data_class
s'il existe.
Je cherchais exactement la même chose et voici ce que j'ai trouvé
http://symfony.com/doc/master/book/forms.html#forms-embedding-single-object
Vous devez dire à l'entité principale de valider ses sous-entités comme ceci:
/**
* @Assert\Type(type="AppBundle\Entity\Category")
* @Assert\Valid()
*/
private $subentity;
J'ai testé cela sur symfony 2.8 et cela fonctionne.
De mon contrôleur:
$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();
Quatrième paramètre dans :: create Named Builder - array('cascade_validation' => true))