Bonsoir à tous (bonjour pour ceux qui liront ce post en journée).
Je suis confronté à un petit problème concernant l'ajout d'un EventListener sur mon formType dans mon prjet Symfony. J'ai suivi la doc ici (en anglais bien sûr), mais sans succès. J'ai aussi pas mal cherché sur d'autre forum, mais en vain.

Je vous explique mon problème:
Je dois créer un formulaire où les listes déroulantes se remplissent suivant nos choix dans les précédantes. Je pense que je ne suis pas le seul à vouloir faire et a avoir été confronté à ce problème.

J'ai plusieurs entités: Parcsimmobilier.php, Ensembles.php, Batiments.php et Zonestechnique.php.

Un Parcs peut avoir plusieurs ensembles (ManyToOne), un ensemble peut avoir plusieurs Batiments (ManyToOne) et un batiment peut avoir plusieurs Zonestechnique (ManyToOne). Enfin une ZonesTechniqus peut avoir plusieurs categories, et une categories peut avoir plusieurs ZonesTechnique (ManyToMany).

1- La route appel un formulaire qui fait apparaître dans un select la liste de tous les parcs immobiliers.

2- Je choisis mon parc et ensuitedans la liste suivante la liste des ensemble correspondant à ce parc.

3- Une fois l'ensemble sélectionné, deux choix s'offre à l'utilisateur. Il doit sélectionner le type de catégorie de la zone technique (il peut y en avoir plusieurs d'ailleurs). Toutefois, si la catégorie "extérieure" est sélectionnée dans ce "multiple select, alors la liste des bâtiments se fige sur une valeur "aucun bâtiment" (qui est une valeur null), mais il ne peut pas choisir un bâtiment car la catégorie est extérieure. Par contre si une autre catégorie est sélectionnée que "extérieure", alors la liste des bâtiments appartenant à l'ensemble auparavant choisi apparaît.

4- Une fois le bâtiment choisit (donc soit un bâtiment, soit la valeur "aucun"), le reste du formulaireest à nous.

Voici le code pour mes entités

//Parcsimmobilier.php
class Parcsimmobilier
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="nom", type="string", length=150, nullable=false)
     */
    private $nom;

     /**
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\OneToMany(targetEntity="Ensembles", mappedBy="parcsimmobilier")
     */
    private $ensembles

    //Ensembles.php
    class Ensembles
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

     /**
     * @var string
     *
     * @ORM\Column(name="nom", type="string", length=150, nullable=false)
     */
    private $nom;

     /**
     * @var \Parcsimmobilier
     *
     * @ORM\ManyToOne(targetEntity="Parcsimmobilier")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="parcsimmobilier_id", referencedColumnName="id")
     * })
     */
    private $parcsimmobilier;

//Batiments.php
   class Batiments
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="nom", type="string", length=150, nullable=true)
     */
    private $nom;

    /**
     * @var \Ensembles
     *
     * @ORM\ManyToOne(targetEntity="Ensembles")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="ensembles_id", referencedColumnName="id")
     * })
     */
    private $ensembles;

    class Zonestechnique
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="nom", type="string", length=150, nullable=false)
     */
    private $nom;

    /**
     * @var \Batiments
     *
     * @ORM\ManyToOne(targetEntity="Batiments")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="batiments_id", referencedColumnName="id")
     * })
     */
    private $batiments;

    /**
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\ManyToMany(targetEntity="Categorieszonestechnique")
     * @ORM\JoinTable(name="zonestechnique_categorieszonestechnique",
     *   joinColumns={
     *     @ORM\JoinColumn(name="zonestechnique_id", referencedColumnName="id")
     *   },
     *   inverseJoinColumns={
     *     @ORM\JoinColumn(name="categorieszonestechnique_id", referencedColumnName="id")
     *   }
     * )
     */

     private $categorieszonestechnique;

Bien sûr mes getter et setters sont implémentés dans leur classe respective, si je vous fournis tout le code il y en aurait pour un moment.

Suivant le doc, j'ai donc créer un formulaire, qui n'est pas lié à une entité de base. Voici le code:

// appel des entités
...
// FormType Components
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;

class AjaxZonestechniqueType extends AbstractType
{

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
          ->add('parcsimmobilier', 'entity', array(
                                    'class'    => 'DatabaseBundle:Parcsimmobilier',
                                    'property' => 'nom',
                                    'multiple' => false,
                                    'required' => true,
                                    'empty_value' => '-- sélectionner un parc --',
                                    'label'    => 'Choisir le parc immobilier : ',
                                    ))
         ->add('categorieszonestechnique')
         ->add('batiments')
         ->add('nom')
          ->add('localisation')
          ->add('commentaire')
          ;
          $formModifier = function (FormInterface $form, Parcsimmobilier $parcsimmobilier = null) {
                $ensembles = null === $parcsimmobilier ? array() : $parcsimmobilier->getEnsembles();

                $form->add('ensembles', 'entity', array(
                                    'class'    => 'DatabaseBundle:Ensembles',
                                    'multiple' => false,
                                    'required' => true,
                                    'mapped' => true,
                                    'empty_value' => '-- sélectionner un ensemble --',
                                    'label'    => 'Choisir l\'ensemble : ',
                ));
            };

            $builder->addEventListener(
                FormEvents::PRE_SET_DATA,
                function (FormEvent $event) use ($formModifier) {
                    // this would be your entity, i.e. SportMeetup
                    $data = $event->getData();

                    $formModifier($event->getForm(), $data->getParcsimmobilier());
                }
            );

            $builder->get('parcsimmobilier')->addEventListener(
                FormEvents::POST_SUBMIT,
                function (FormEvent $event) use ($formModifier) {
                    // It's important here to fetch $event->getForm()->getData(), as
                    // $event->getData() will get you the client data (that is, the ID)
                    $parcsimmobilier = $event->getForm()->getData();

                    // since we've added the listener to the child, we'll have to pass on
                    // the parent to the callback functions!
                    $formModifier($event->getForm()->getParent(), $parcsimmobilier);
                }
            );

Bien sûr j'ai une belle erreur quand j'affiche ma page:

Error: Call to a member function getParcsimmobilier() on a non-object
500 Internal Server Error - FatalErrorException

Mon controller est tout ce qu'il y a de plus basic, je lui demande faire apparaître le formulaire dans ma twig, et ma twig l'affiche (enfin pas vraiment d'où l'erreur).

J'ai fait un var_dump($data), et bien sûr $data est null.

Voilà je patauge correctement donc si vous avez des suggestions je suis preneur. J'ai fait énormément de recherche sur internet, et pour l'instant aucun forum n'a pu vraiment m'aider, je vous avouerai ne pas totalement comprendre le fonctionnement des Event Listener sur Symfony.

Merci d'avance.

15 réponses


LordSpock
Réponse acceptée
            ->add('author', 'entity', array(
                'class' => 'FrontEndUserBundle:User',
                'query_builder' => function(EntityRepository $er) {
                    return $er->createQueryBuilder('u')
                        ->where('u.roles LIKE :roles')
                        ->setParameter('roles', '%"ROLE_SUPER_ADMIN"%');
                },
            ))

Je suis sur un projet, et donc, je récupère sur mon form de manière dynamique des informations là si ça peut t'aider.
à partir de là, tu peux modifier ton affichage avec du jQuery, par contre je suis au taff, donc je peux pas regarder niveau code comment le faire..

Bonne continuation et bon courage pour ton projet qui ne m'a pas l'air très simple sur le point dont on discute ^^

"Je suis confronté à un petit problème concernant l'ajout d'un EventListener sur mon formType dans mon prjet Symfony. J'ai suivi la doc ici (en anglais bien sûr), mais sans succès. J'ai aussi pas mal cherché sur d'autre forum, mais en vain.

Je vous explique mon problème:
Je dois créer un formulaire où les listes déroulantes se remplissent suivant nos choix dans les précédantes. Je pense que je ne suis pas le seul à vouloir faire et a avoir été confronté à ce problème."

Quel rapport entre un formulaire dynamique et un eventlistener, je saisis pas exactement ce que tu souhaites mettre en place, car tu balances tes entitées ok, mais où réside exactemment le soucis ? ^^

le rapport? Tout simplement j'essaye de créer un formulaire dynamique en utilisant la doc sur les ajouts des EventListener dans un formType dans Symfony, afin d'avoir un formulaire en Ajax où les dropdownlist se remplissent suivant les champs select précédant. C'est un peu comme si j'essayais d'avoir une liste rempli de ville suivant ma sélection de Région, suivant ma sélection de Pays (je ne sais pas si c'est clair).

L'exemple sur ce lien expliquera mieux ce que je cherche à faire.
Dans mon cas, j'ai une liste déroulante où sont tous mes parcs. En sélectionnant un parc, dans la seconde liste déroulant des ensembles doit s'afficher la liste des ensembles correspondant à ce parc. Ensuite en choisissant un ensemble, dans la liste déroulante suivante doit s'afficher les bâtiments correspondant à l'ensemble préalablement sélectionné. Enfin, suivant ma catégorie de zones technique, si je choisis la catégorie ayant pour valeur "extérieure", la liste des bâtiments doit avoir une valeur "null".

Je cherche à créer ce formulaire pour l'ajout d'une nouvelle zone technique. Je suis en train de suivre ce tuto pour le moment.

Enfin pour répondre à ta dernière question, quand j'affiche ma twig où est le formulaire j'ai l'erreur mentionné dans mon premier post. En fait, j'ai un peu de mal avec la doc pour cette fonctionnalité car je n'arrive pas à mettre en place un formulaire dynamique avec plusieurs listes déroulante liées entre elles.

Mais tu es obligé d'avoir un eventlistener, car tu n'as qu'une sauvegarde après execution de ton formulaire on est d'accord ?
Et donc, passer par un formulaire avec un listage en field entity qui change en fonction du choix précédent, donc que tu couples à ton form en JQuery, ça serait plus simple non ?

( J'ai jamais été confronté à ça, donc je vais essayer de porter mon aide autant que faire ce peu, mais ça restera du "consulting" )

et bien écoute, j'ai trouvé pas mal de topic sur cette même fonctionnalité, certains passe par jQuery et Ajax, d'autre par les EventListener de Symfony, mais chaque cas est particulié, du moins certaines circonstances dans notre dev et nos relations entre entités font que l'adaptation est différente. Suivant la doc de Symfony, on doit faire les EventListener pour ajouter des champs dynamiquement. Et personnellment je n'y arrive pas. Merci pour tes interventions. De mon côté je continu de chercher une solution bien sûr.

Merci pour ton aide. Je vais regarder ça et tenter ta suggestion. Oui effectivement, cette partie devient non seulement frustrante (car sans succès pour le moment) mais reste difficile à implémenter pour un débutant sur Symfony. Je suis aussi au taff, hésite pas si tu as des suggestions, je me doute bien que tu dois avoir des choses à faire.

En jQuery, je suppose que je dois utiliser la fonction onChange, n'est ce pas?

D'après moi oui, mais je suis développeur backend, et j'évite tout ce qui est jQuery en général :D
[http://www.bernardquevillier.fr/toposnew/onchange.htm]() tiens si ça peut t'aider. La logique voudrait que, en fonction du champ selectionné tu charges une autre liste ou pour te simplifier la vie, tu affiches seulement une autre liste.

Et aussi sinon, créer plusieurs formulaires qui se s'enchaine, [http://www.thelin.net/laurent/labo/js/listederoulante.html](), du style, tu fais un formulaire comme ça avec tes options ça va sauver la requete, puis passer à une page avec la suite du formulaire en fait, comme ça tu pourras découper la récupération des informations.
Si tu prends cette solution, regardes à bien gérer tes redirections de contrôleur, ou la sauvegarde provisoir d'une page à l'autre (je pense que tu peux trouver des tuto sur ça, un formulaire en plusieurs pages).

Je pense que je vais continuer sur tes suggestions.

Voici mon formType avec des querybuilder:

public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('parcsimmobilier', 'entity', array(
                                    'class'    => 'Bundle:Parcsimmobilier',
                                    'property' => 'nom',
                                    'empty_value' => '-- sélectionner un parc --',
                                    'label'    => 'Choisir le parc immobilier : ',
                                    ))
            ->add('ensembles', 'entity', array(
                                    'class'    => 'Bundle:Ensembles',
                                    'query_builder' => function(EntityRepository $er) {
                                        return $er->createQueryBuilder('e')
                                                  ->join('e.parcsimmobilier','p');
                                    },
                                    'property' => 'nom',
                                    'empty_value' => '-- sélectionner un ensemble --',
                                    'label'    => 'Choisir l\'ensemble : ',
                                    ))
            ->add('batiments','entity', array(
                                    'class'    => 'Bundle:Batiments',
                                    'query_builder' => function(EntityRepository $er) {
                                        return $er->createQueryBuilder('b')
                                                  ->join('b.ensembles','e');
                                    },
                                    'property' => 'nom',
                                    'empty_value' => '-- sélectionner un batiment --',
                                    'label'    => 'Choisir un batiment : ',
                                    ))
            ->add('categorieszonestechnique')
            ->add('nom')
            ->add('localisation')
            ->add('commentaire')
            ;
    }

Aucune erreur ne m'est renvoyé pour le moment, donc je pense que mes requêtes pour mes champs sont bonnes. Me reste plus qu'a mettre en place le jQuery. Je me penche sur la doc car pour l'instant je n'ai encore jamais fait ça.

Merci de ton aide en tout cas et n'hésite pas si tu as des pistes. Je vais aussi bien regardé tes liens.

Au plaisir de t'avoir aidé, par contre je n'irai pas m'engager sur la voix du jquery, j'aime aps trop :D

je comprends, mais pour cette fonctionnalité je n'ai pas vraiment beaucoup le choix vu que je n'arrive pas à implémenter les EventListener de la doc de Symfony.

Tu n'utilises pas jQuery sur tes formuliares? par exemple pour le bout de code que tu m'as donné?

J'utilise bootstrap donc les solutions sont prè-existantes pour mes besoins ou facilement modifiables et je n'ai jamais eu à faire ton style de form.

De plus mes formulaires sont statiques en général, enfin, pas d'évenements lors de tel ou tel action ! :)

Pour le bout de code que je t'ai donné, il permet juste de donner des options pour mon champs (user avec role admin pour selection).

ça marche. Je vais regarder jQuery de plus près. Sache que tes interventions m'ont permis d'envisager une autre solution. Merci en tout cas.

C'est comme en anglais, quand tu perds un mot, tu cherches à combler avec un autre moyen pour l'exprimer, quand notre seuil de compétence est dépassé, on met une chose en place qui évoluera par après en fonction de ce que l'on peut avoir à découvrir entre temps ! :)
Du moins je fonctionne comme ça, car bloquer pendant des heures je ne peux plus l'envisager, je préfère utiliser tout le temps que j'ai pour avancer et améliorer ce qui bloquait par après. (je laisse rien de dégueulasse pour autant)

Je comprends, je pense qu'on essaye tous de rendre du code propre sans trop perdre de temps, après tous on va pas réinventer ce qui existe déjà. Mais parfois, quand on bloque, on peut avoir du mal à voir d'autres solutions. Toutefois, il faut toujours penser à une solution et non au problème en lui même.

J'ai choisi de mettre cette question/ce topic en résolu, pour la simple et bonne raison que l'on m'a aiguillé sur quelque chose d'autre, une autre méthode que plusieurs d'entre nous ont déjà utilisé auparavant. Vous pouvez voir ce topic pour plus d'informations. Les eventListener de la doc de Symfony sont difficile de compréhension, mon cas est de plus particulier.