web-dev-qa-db-fra.com

Symfony2 - La validation ne fonctionne pas pour le type de formulaire intégré

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()));
72
Mr Pablo

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.

114
daftv4der

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.

67
cg.

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
35
supernova

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..

4
Hauke

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.

4
targnation

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.

3
Mun Mun Das

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.

3
nacholibre

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))

0
d3uter