Bonjour,
Bonjour à tous,

Je développe en ce moment une application web pour la gestion d'un établissement. J'aimerais réaliser un formulaire (qui n'est pas lié à une entité) dynamique me permettant d'afficher les étudiants d'une classe. Pour cela, l'utilisateur devra sélectionner la faculté, puis la filière et enfin la classe. Concernant la relation entre ces trois entités, nous avons : Une faculté (oneToMany) contient plusieurs filières(ManyToOne), une filière (oneToMany) contient plusieurs classes (ManyToOne). Voici le code de mes entités :
Voila je rencontre un petit problème avec mon code.

Ce que je fais

Voici mes entités

ENTITE FACULTE

 <?php
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
 * Faculte
 *
 * @ORM\Table(name="faculte")
 * @ORM\Entity(repositoryClass="Catho\AdminBundle\Entity\FaculteRepository")
 */
class Faculte
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

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

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

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

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

    /**
     * @var ArrayCollection filieres
     * @ORM\OneToMany(targetEntity="Filiere", mappedBy="faculte")
     */
    private $filieres;

    public function __construct()
    {
        $this->filieres = new ArrayCollection();
    }
    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set code
     *
     * @param string $code
     *
     * @return Faculte
     */
    public function setCode($code)
    {
        $this->code = $code;

        return $this;
    }

    /**
     * Get code
     *
     * @return string
     */
    public function getCode()
    {
        return $this->code;
    }

    /**
     * Set nom
     *
     * @param string $nom
     *
     * @return Faculte
     */
    public function setNom($nom)
    {
        $this->nom = $nom;

        return $this;
    }

    /**
     * Get nom
     *
     * @return string
     */
    public function getNom()
    {
        return $this->nom;
    }

    /**
     * Set bp
     *
     * @param string $bp
     *
     * @return Faculte
     */
    public function setBp($bp)
    {
        $this->bp = $bp;

        return $this;
    }

    /**
     * Get bp
     *
     * @return string
     */
    public function getBp()
    {
        return $this->bp;
    }

    /**
     * Set ville
     *
     * @param string $ville
     *
     * @return Faculte
     */
    public function setVille($ville)
    {
        $this->ville = $ville;

        return $this;
    }

    /**
     * Get ville
     *
     * @return string
     */
    public function getVille()
    {
        return $this->ville;
    }

    /**
     * Add filiere
     *
     * @param \Catho\AdminBundle\Entity\Filiere $filiere
     *
     * @return Faculte
     */
    public function addFiliere(\Catho\AdminBundle\Entity\Filiere $filiere)
    {
        $this->filieres[] = $filiere;

        return $this;
    }

    /**
     * Remove filiere
     *
     * @param \Catho\AdminBundle\Entity\Filiere $filiere
     */
    public function removeFiliere(\Catho\AdminBundle\Entity\Filiere $filiere)
    {
        $this->filieres->removeElement($filiere);
    }

    /**
     * Get filieres
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getFilieres()
    {
        return $this->filieres;
    }
}

ENTITE FILIERE

<?php

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * Filiere
 *
 * @ORM\Table(name="filiere", indexes={@ORM\Index(name="fk_faculte_filiere", columns={"faculte_id"})})
 * @ORM\Entity(repositoryClass="Catho\AdminBundle\Entity\FiliereRepository")
 */
class Filiere
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

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

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

    /**
     * @var Faculte
     *
     * @ORM\ManyToOne(targetEntity="Faculte", inversedBy="filieres")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="faculte_id", referencedColumnName="id")
     * })
     */
    private $faculte;

    /**
     * @var ArrayCollection
     * @ORM\OneToMany(targetEntity="Classe", mappedBy="filiere")
     */
    private $classes;

    public function __construct()
    {
        $this->classes = new ArrayCollection();
    }

    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set code
     *
     * @param string $code
     *
     * @return Filiere
     */
    public function setCode($code)
    {
        $this->code = $code;

        return $this;
    }

    /**
     * Get code
     *
     * @return string
     */
    public function getCode()
    {
        return $this->code;
    }

    /**
     * Set nom
     *
     * @param string $nom
     *
     * @return Filiere
     */
    public function setNom($nom)
    {
        $this->nom = $nom;

        return $this;
    }

    /**
     * Get nom
     *
     * @return string
     */
    public function getNom()
    {
        return $this->nom;
    }

    /**
     * Set faculte
     *
     * @param \Catho\AdminBundle\Entity\Faculte $faculte
     *
     * @return Filiere
     */
    public function setFaculte(\Catho\AdminBundle\Entity\Faculte $faculte = null)
    {
        $this->faculte = $faculte;

        return $this;
    }

    /**
     * Get faculte
     *
     * @return \Catho\AdminBundle\Entity\Faculte
     */
    public function getFaculte()
    {
        return $this->faculte;
    }

    /**
     * Add class
     *
     * @param \Catho\AdminBundle\Entity\Classe $class
     *
     * @return Filiere
     */
    public function addClass(\Catho\AdminBundle\Entity\Classe $class)
    {
        $this->classes[] = $class;

        return $this;
    }

    /**
     * Remove class
     *
     * @param \Catho\AdminBundle\Entity\Classe $class
     */
    public function removeClass(\Catho\AdminBundle\Entity\Classe $class)
    {
        $this->classes->removeElement($class);
    }

    /**
     * Get classes
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getClasses()
    {
        return $this->classes;
    }
}

ENTITTE CLASSE

<?php

use Doctrine\ORM\Mapping as ORM;

/**
 * Classe
 *
 * @ORM\Table(name="classe", indexes={@ORM\Index(name="fk_filiere_classe", columns={"filiere_id"})})
 * @ORM\Entity(repositoryClass="Catho\AdminBundle\Entity\ClasseRepository")
 */
class Classe
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

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

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

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

    /**
     * @var Filiere
     *
     * @ORM\ManyToOne(targetEntity="Filiere", inversedBy="classes")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="filiere_id", referencedColumnName="id")
     * })
     */
    private $filiere;

    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set code
     *
     * @param string $code
     *
     * @return Classe
     */
    public function setCode($code)
    {
        $this->code = $code;

        return $this;
    }

    /**
     * Get code
     *
     * @return string
     */
    public function getCode()
    {
        return $this->code;
    }

    /**
     * Set nom
     *
     * @param string $nom
     *
     * @return Classe
     */
    public function setNom($nom)
    {
        $this->nom = $nom;

        return $this;
    }

    /**
     * Get nom
     *
     * @return string
     */
    public function getNom()
    {
        return $this->nom;
    }

    /**
     * Set campus
     *
     * @param string $campus
     *
     * @return Classe
     */
    public function setCampus($campus)
    {
        $this->campus = $campus;

        return $this;
    }

    /**
     * Get campus
     *
     * @return string
     */
    public function getCampus()
    {
        return $this->campus;
    }

    /**
     * Set filiere
     *
     * @param \Catho\AdminBundle\Entity\Filiere $filiere
     *
     * @return Classe
     */
    public function setFiliere(\Catho\AdminBundle\Entity\Filiere $filiere = null)
    {
        $this->filiere = $filiere;

        return $this;
    }

    /**
     * Get filiere
     *
     * @return \Catho\AdminBundle\Entity\Filiere
     */
    public function getFiliere()
    {
        return $this->filiere;
    }
}

FICHIER DE DESCRIPTION DU FORMULAIRE AVEC LES DIFFERENTES CLASSES EVENTLISTNER

<?php

class FiltreEtudiantType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $factory = $builder->getFormFactory();

        $builder->addEventSubscriber(new AddClasseFieldSubscriber($factory));

        $builder->addEventSubscriber(new AddFiliereFieldSubscriber($factory));

        $builder->addEventSubscriber(new AddFaculteFieldSubscriber($factory));

        $builder
                ->add('annee', EntityType::class, array(
                    'mapped' => false,
                    'class' => 'Catho\AdminBundle\Entity\Annee',
                    'choice_label' => 'anneeAca',
                    'label' => 'Année',
                    'required' => true,
                    'constraints' => new Assert\NotBlank(),
                    'expanded' => false,
                    'multiple' => false,
                ))
        ;

    }

    public function getName()
    {
        return 'filtreEtudiant';
    }

}

<?php

class AddClasseFieldSubscriber implements EventSubscriberInterface
{
    private $factory;

    public function __construct(FormFactoryInterface $factory)
    {

        $this->factory = $factory;
    }
    public static function getSubscribedEvents()
    {
        // TODO: Implement getSubscribedEvents() method.
        return array(
            FormEvents::PRE_SET_DATA => 'preSetData',
            FormEvents::PRE_SUBMIT => 'preSubmit'
        );
    }

    public function addClasseForm(FormInterface $form, Filiere $filiere)
    {

        $form->add($this->factory->createNamed('classe', EntityType::class, null, array(
            'class' => 'CathoAdminBundle:Classe',
            'placeholder' => 'Choose an option',
            'mapped' => false,
            'query_builder' => function(EntityRepository $repository) use ($filiere){
                $qb = $repository->createQueryBuilder('classe')
                    ->innerJoin('classe.filiere', 'filiere');
                if($filiere instanceof Filiere){
                    $qb->where('classe.filiere = :filiere')
                       ->setParameter('filiere', $filiere);
                }elseif(is_numeric($filiere)){
                    $qb->where('filiere.id = :filiere')
                       ->setParameter('filiere', $filiere);
                }else{
                    $qb->where('filiere.nom = :filiere')
                        ->setParameter('filiere', null);
                }
                return $qb;
            }
        )));
    }

    public function preSetData(FormEvent $event)
    {
        $data = $event->getData();
        $form = $event->getForm();

        if(null === $data){
            return;
        }

        $filiere = ($data->classe) ? $data->classe->getFiliere() : null;

        $this->addClasseForm($form, $filiere);
    }

    public function preSubmit(FormEvent $event)
    {
        $data = $event->getData();
        $form = $event->getForm();

        if (null === $data) {
            return;
        }
        $filiere = array_key_exists('filiere', $data) ? $data['filiere'] : null;
        $this->addClasseForm($form, $filiere);
    }
}

<?php

class AddFiliereFieldSubscriber implements EventSubscriberInterface
{

    private $factory;

    public function __construct(FormFactoryInterface $factory)
    {

        $this->factory = $factory;
    }

    /**
     * Returns an array of event names this subscriber wants to listen to.
     *
     * The array keys are event names and the value can be:
     *
     *  * The method name to call (priority defaults to 0)
     *  * An array composed of the method name to call and the priority
     *  * An array of arrays composed of the method names to call and respective
     *    priorities, or 0 if unset
     *
     * For instance:
     *
     *  * array('eventName' => 'methodName')
     *  * array('eventName' => array('methodName', $priority))
     *  * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
     *
     * @return array The event names to listen to
     */
    public static function getSubscribedEvents()
    {
        // TODO: Implement getSubscribedEvents() method.
        return array(
            FormEvents::PRE_SET_DATA => 'preSetData',
            FormEvents::PRE_SUBMIT => 'preSubmit'
        );
    }

    public function addFiliereForm(FormInterface $form, Filiere $filiere, Faculte $faculte)
    {

        $form->add($this->factory->createNamed('filiere', EntityType::class, $filiere,  array(
            'class' => 'CathoAdminBundle:Filiere',
            'placeholder' => 'Choose an option',
            'mapped' => false,
            'query_builder' => function(EntityRepository $repository) use ($faculte){
                $qb = $repository->createQueryBuilder('filiere')
                    ->innerJoin('filiere.faculte', 'faculte');
                if($faculte instanceof Faculte){
                    $qb->where('filiere.faculte = :faculte')
                        ->setParameter('faculte', $faculte);
                }elseif(is_numeric($faculte)){
                    $qb->where('faculte.id = :faculte')
                        ->setParameter('faculte', $faculte);
                }else{
                    $qb->where('faculte.nom = :faculte')
                        ->setParameter('faculte', null);
                }
                return $qb;
            }
        )));
    }

    public function preSetData(FormEvent $event)
    {
        $data = $event->getData();
        $form = $event->getForm();

        if(null === $data){
            return;
        }

        $filiere = ($data->classe) ? $data->classe->getFiliere() : null;
        $faculte = ($filiere) ? $filiere->getFaculte() : null;

        $this->addFiliereForm($form, $filiere, $faculte);
    }

    public function preSubmit(FormEvent $event)
    {
        $data = $event->getData();
        $form = $event->getForm();

        if (null === $data) {
            return;
        }
        $filiere = array_key_exists('filiere', $data) ? $data['filiere'] : null;
        $faculte = array_key_exists('faculte', $data) ? $data['faculte'] : null;

        $this->addFiliereForm($form, $filiere, $faculte);
    }

}

<?php

class AddFaculteFieldSubscriber implements EventSubscriberInterface
{

    private $factory;

    public function __construct(FormFactoryInterface $factory)
    {

        $this->factory = $factory;
    }

    /**
     * Returns an array of event names this subscriber wants to listen to.
     *
     * The array keys are event names and the value can be:
     *
     *  * The method name to call (priority defaults to 0)
     *  * An array composed of the method name to call and the priority
     *  * An array of arrays composed of the method names to call and respective
     *    priorities, or 0 if unset
     *
     * For instance:
     *
     *  * array('eventName' => 'methodName')
     *  * array('eventName' => array('methodName', $priority))
     *  * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
     *
     * @return array The event names to listen to
     */
    public static function getSubscribedEvents()
    {
        // TODO: Implement getSubscribedEvents() method.
        return array(
            FormEvents::PRE_SET_DATA => 'preSetData',
            FormEvents::PRE_SUBMIT => 'preSubmit'
        );
    }

    public function addFaculteForm(FormInterface $form, Faculte $faculte )
    {
        $form->add($this->factory->createNamed('faculte', EntityType::class, $faculte, array(
            'class'         => 'CathoAdminBundle:Faculte',
            'placeholder' => 'Choose an option',
            'query_builder' => function (EntityRepository $repository) {
                $qb = $repository->createQueryBuilder('faculte');

                return $qb;
            }
        )));
    }

    public function preSetData(FormEvent $event)
    {
        $data = $event->getData();
        $form = $event->getForm();

        if (null === $data) {
            return;
        }

        $faculte = ($data->classe) ? $data->classe->getFiliere()->getFaculte() : null ;
        $this->addFaculteForm($form, $faculte);
    }

    public function preSubmit(FormEvent $event)
    {
        $data = $event->getData();
        $form = $event->getForm();

        if (null === $data) {
            return;
        }

        $faculte = array_key_exists('faculte', $data) ? $data['faculte'] : null;
        $this->addFaculteForm($form, $faculte);
    }

}

Mon CONTROLEUR ET QUELQUES ACTIONS.

Mon Contôleur

/**
    * @Route("/filieres", name="select_filieres")
    */
   public function filieresAction(Request $request)
   {
       $faculte_id = $request->request->get('faculte_id');
       $em = $this->getDoctrine()->getManager();
       $filieres = $em->getRepository('CathoAdminBundle:Filiere')->findByFaculteId($faculte_id);
       return new JsonResponse($filieres);
   }
   /**
    * @Route("/classes", name="select_classes")
    */
   public function classesAction(Request $request)
   {
       $filiere_id = $request->request->get('filiere_id');
       $em = $this->getDoctrine()->getManager();
       $classes = $em->getRepository('ActhoAdminBundle:Classe')->findByFiliereId($filiere_id);
       return new JsonResponse($classes);
   }

   /**
    * @Route("/classe-etudiant/list", name="admin_list_classe_etudiant")
    * @Method({"POST","GET"})
    * @param Request $request
    * @return \Symfony\Component\HttpFoundation\Response
    */
   public function listClasseEtudiantAction(Request $request)
   {
       $form = $this->createForm(FiltreEtudiantType::class);

       if($request->isMethod('POST')){

           $form->handleRequest($request);

           if($form->isSubmitted() && $form->isValid())
           {
               $annee = $form->get('annee')->getData();
               $classe = $form->get('classe')->getData();

               $listeEtudiants = $this->getDoctrine()->getManager()->getRepository('CathoAdminBundle:ClasseEtudiant')->findStudent($annee, $classe);

               return $this->render('CathoAdminBundle:Admin:_include/etudiant_classes.html.twig', array(
                   'form' => $form->createView(),
                   'listeEtudiants' => $listeEtudiants
               ));
           }
       }
       return $this->render('CathoAdminBundle:Admin:_include/etudiant_classes.html.twig', array(
           'form' => $form->createView()
       ));
   }

ET POUR TERMINER Le FORMULAIRE TWIG. Bon Le code Ajax a été omis


    fichier filtre.html.twig

<div class="well research_form">

    <form method="post" action="{{ path('admin_list_classe_etudiant') }}" class="form-inline" role="form">

        {{ form_errors(form) }}

        <div class="form-group">
            {{ form_row(form.annee) }}
        </div>

        <div class="form-group">
            {{ form_row(form.faculte) }}
        </div>

        <div class="form-group">
            {{ form_row(form.filiere) }}
        </div>

        <div class="form-group">
            {{ form_row(form.classe) }}
        </div>
            {{ form_rest(form) }}

        <input type="submit" value="rechercher" class="btn btn-primary" />

    </form>

</div>

Désole pour la longueur du Code. J'ai vraiment besoin de votre aide

Le message d'erreur est le suivant :
Method "faculte" for object "Symfony\Component\Form\FormView" does not exist in CathoAdminBundle:Admin:_include\filtre.html.twig at line 12

Besoin de votre aide, pour la résolution de ce problème

Merci d'avance.

4 réponses


nico41
Réponse acceptée

J'ai plus ou moins reproduit ton cas de figure, je te laisse explorer le depot Github :
https://github.com/Grosloup/formevents

basé sur la doc symfony : http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-submitted-data

et sur une recherche dans stackoverflow : http://stackoverflow.com/questions/29415352/symfony2-double-nested-dynamic-form-fields

peux tu séparer le controller, le formulaire, l'entité et le template pour que je puisse mieux voir ton code, c'est vraiment indigeste

patrickg
Auteur

OK Je pense avoir revu la présentation du code comme demandé. Merci d'avance pour votre

Hello,
En espérant que sa aide je t'invite à faire un tour sur ce code en ligne qui pourrait t'aider.

https://github.com/ccvf2s/symfony-selects-dependants

Bien à toi.