IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Symfony PHP Discussion :

Comment faire fonctionner Many-to-many dans les 2 sens avec les formulaires ?


Sujet :

Symfony PHP

  1. #1
    Membre régulier
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Septembre 2013
    Messages
    71
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Gers (Midi Pyrénées)

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux
    Secteur : Santé

    Informations forums :
    Inscription : Septembre 2013
    Messages : 71
    Points : 84
    Points
    84
    Par défaut Comment faire fonctionner Many-to-many dans les 2 sens avec les formulaires ?
    Bonjour,

    J'ai 2 entités :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?xml version="1.0" encoding="UTF-8"?>
    <!-- src/Common/UserBundle/Resources/config/doctrine/MUser.orm.xml -->
     
    <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
                      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                      xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
                                          http://doctrine-project.org/schemas/orm/doctrine-maping.xsd">
     
        <entity repository-class="Common\UserBundle\Repository\MUserRepository"
                name="Common\UserBundle\Entity\MUser"
                table="fos_user">
            <id name="id" type="integer" column="id">
                <generator strategy="AUTO"/>
            </id>
            <field name="lastname" type="string" length="255" />
            <many-to-many field="mgroups" inversed-by="musers" target-entity="Common\UserBundle\Entity\MGroup">
                <join-table name="fos_user_user_group">
                    <join-columns>
                        <join-column name="user_id" referenced-column-name="id" />
                    </join-columns>
                    <inverse-join-columns>
                        <join-column name="group_id" referenced-column-name="id" />
                    </inverse-join-columns>
                </join-table>
            </many-to-many>
        </entity>
    </doctrine-mapping>
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <?xml version="1.0" encoding="UTF-8"?>
    <!-- src/Common/UserBundle/Resources/config/doctrine/MGroup.orm.xml -->
     
    <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
                      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                      xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
                                          http://doctrine-project.org/schemas/orm/doctrine-maping.xsd">
     
        <entity repository-class="Common\UserBundle\Repository\MGroupRepository"
                name="Common\UserBundle\Entity\MGroup"
                table="fos_group">
            <id name="id" type="integer" column="id">
                <generator strategy="AUTO"/>
            </id>
            <many-to-many field="musers" mapped-by="mgroups" target-entity="Common\UserBundle\Entity\MUser">
                <join-table name="fos_user_user_group" />
            </many-to-many>
        </entity>
    </doctrine-mapping>
    Le doctrine:generate:entities m'a bien généré les deux fichiers entités et chacun possède bien les 3 fonctions qui permettent de jouer avec cette relation.

    J'ai un contrôleur et une vue dédiés à la gestion des utilisateurs et j'ai également un contrôleur et une vue dédiés à la gestion des groupes.

    Lorsque je veux afficher la liste des MUsers d'un MGroup, pas de problème, je vois tout le monde.
    Lorsque je veux afficher la liste des MGroups d'un MUser, pas de problème, je vois tous les groupes.
    Lorsque je veux mettre à jour via un formulaire la liste des MGroups d'un MUser, pas de problème, elle se met à jour.
    Lorsque je veux mettre à jour via un formulaire la liste des Musers d'un MGroup, rien ne se passe par contre, les autres champs du formulaire sont bien mis à jour en base de données.

    J'imagine que j'ai oublié quelque chose dans mes fichiers xml mais je n'arrive pas à voir quoi.

    J'ai vu plusieurs sites qui indiquaient que la relation ne devait être faite que sur un seul fichier et pourtant, tant que je n'avais mis <many-to-many field="musers" mapped-by="mgroups" target-entity="Common\UserBundle\Entity\MUser" />, doctrine:generate:entities ne créait pas les 3 fonctions dans l'entité MGroup et je ne pouvais pas lire les données.

    Ci-dessous, pour info, les 2 formulaires :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <?php
     
    // src/Common/UserBundle/Form/Type/MUserType.php
     
    namespace Common\UserBundle\Form\Type;
     
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilderInterface;
     
    class MUserType extends AbstractType
    {
     
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder->add('lastname', 'text')
                    ->add('mgroups', 'entity', array(
                        'class' => 'Common\UserBundle\Entity\MGroup',
                        'choice_label' => 'name',
                        'multiple' => true,
                        'expanded' => true))
                    ->add('save', 'submit');
        }
     
        public function getName()
        {
            return 'muser';
        }
     
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <?php
     
    // src/Common/UserBundle/Form/Type/MGroupType.php
     
    namespace Common\UserBundle\Form\Type;
     
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilderInterface;
     
    class MGroupType extends AbstractType
    {
     
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder->add('name', 'text')
                    ->add('musers', 'entity', array(
                        'class' => 'Common\UserBundle\Entity\MUser',
                        'choice_label' => 'username',
                        'multiple' => true,
                        'expanded' => true))
                    ->add('save', 'submit');
        }
     
        public function getName()
        {
            return 'mgroup';
        }
     
    }
    Les contrôleurs sont presque identiques. J'ai pratiquement fait un chercher/remplacer pour changer MUser (qui enregistre bien) par MGroup (qui n'enregistre pas).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    <?php
     
    // src/Common/UserBundle/Controller/GroupController.php
     
    namespace Common\UserBundle\Controller;
     
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Symfony\Component\HttpFoundation\Request;
     
    class MGroupController extends Controller
    {
     
        public function editAction($groupid, Request $request)
        {
            $group = $this->get('common.mgroup_manager')->getRepository()->findById($groupid);
     
            $form = $this->createForm('mgroup', $group[0]);
            $form->handleRequest($request);
     
            if ($form->isValid())
            {
                $this->get('common.mgroup_manager')->saveMGroup($group[0]);
                return $this->redirectToRoute('common_user_mgroup_show', array('groupid' => $group[0]->getId()));
            }
     
            return $this->render('CommonUserBundle:MGroup:edit.html.twig', array(
                        'group' => $group[0],
                        'form' => $form->createView()
            ));
        }
     
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <?php
     
    // src/Common/UserBundle/Controller/MUserController.php
     
    namespace Common\UserBundle\Controller;
     
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Symfony\Component\HttpFoundation\Request;
     
    class MUserController extends Controller
    {
        public function editAction($userid, Request $request)
        {
            $user = $this->get('common.muser_manager')->getRepository()->findById($userid);
     
            $form = $this->createForm('muser', $user[0]);
            $form->handleRequest($request);
     
            if ($form->isValid())
            {
                $this->get('common.muser_manager')->saveMUser($user[0]);
                return $this->redirectToRoute('common_user_muser_show', array('userid' => $user[0]->getId()));
            }
     
            return $this->render('CommonUserBundle:MUser:edit.html.twig', array(
                        'user' => $user[0],
                        'form' => $form->createView()
            ));
        }
     
    }
    Merci d'avance pour l'aide que vous pourrez m'apporter car techniquement, l'application fonctionne bien puisque je n'ai absolument aucun message d'erreur ce qui ne facilite bien sûr pas le debug.

  2. #2
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Salut,

    Ton problème vient de Doctrine et la manière dont il gère les relations bidirectionnelles.

    Pour te la faire courte, il y a un côté de la relation qui est appelé le Owning Side : c'est lui le boss, et quand tu le flushes, sa collection est flushée aussi. L'autre est appelé Inverse Side : lui il fait pas la loi parce que c'est pas lui qui porte la culotte dans leur relation. Du coup s'il veut se faire flusher, il se fait flusher tout seul.


    Dans ton cas, le boss c'est le User, parce que c'est comme ça que tu l'as défini dans ton fichier xml.
    Pour obtenir le comportement que tu souhaites, il faut que tu updates la collection dans le inverse side lorsque tu fais un add().

    Ça donnerait un truc comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    // Entity Group.php
     
    public function addUser(User $user)
    {
        $user->add($this); // synchronously updating inverse side
        $this->users[] = $user;
    }
    Tu peux jeter un oeil à la doc officielle de Doctrine si tu veux des explications plus précises.

    http://doctrine-orm.readthedocs.org/...ny-association

    /!\ Attention cependant, cette technique fonctionne si tu flush tout l'entity manager, c'est à dire que tu appelles la méthode flush() sans paramètre. Si tu lui passe une entité en faisant un $em->flush($group);, ben là ça va pas marcher parce que ton user sera pas flushé pour autant. Du coup tu l'auras dans l'cubitus et tu devras flusher ton user à la mano.

  3. #3
    Membre régulier
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Septembre 2013
    Messages
    71
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Gers (Midi Pyrénées)

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux
    Secteur : Santé

    Informations forums :
    Inscription : Septembre 2013
    Messages : 71
    Points : 84
    Points
    84
    Par défaut
    Merci beaucoup pour cette réponse très complète. Je n'arrive malheureusement pas à l'appliquer dans un form.

    Dans mon entité MGroup, j'ai bien modifié le AddMuser() comme suit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
        public function addMuser(\Common\UserBundle\Entity\Muser $musers)
        {
            $musers->addMGroup($this);
            $this->musers[] = $musers;
     
            return $this;
        }
    En revanche, lorsque je valide mon formulaire, je m'aperçois qu'à aucun moment il ne rentre dans cette fonction car je peux écrire n'importe quoi (par exemple : $musers->addNimporteQuoi($this)), il ne me met aucun message d'erreur.

    Ci-dessous, le code du contrôleur qui valide le formulaire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
            if ($form->isValid())
            {
                $em = $this->getDoctrine()->getManager();
                $em->persist($group[0]);
                $em->flush();
     
                return $this->redirectToRoute('common_user_mgroup_show', array('groupid' => $group[0]->getId()));
            }
    Faut-il rajouter à la main dans le controleur le $mgroup->addMUser($user) ?

    Si oui, ça me parait compliqué à réaliser car ça oblige à :
    - récupérer le contenu du formulaire
    - si un ou plusieurs utilisateurs ont été cochés, boucler sur la liste et faire un add pour chacun (ça à la limite c'est facile)
    - si un ou plusieurs utilisateurs qui appartenaient au groupe sont retirés, boucler sur la liste des utilisateurs décochés pour les remove (là c'est chaud)

    Je pense que je fais fausse route car ça ne me parait pas simple du tout.

  4. #4
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Est-ce que tu passes dans la méthode setMusers à la place ?

  5. #5
    Membre régulier
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Septembre 2013
    Messages
    71
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Gers (Midi Pyrénées)

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux
    Secteur : Santé

    Informations forums :
    Inscription : Septembre 2013
    Messages : 71
    Points : 84
    Points
    84
    Par défaut
    La méthode setMusers n'existe pas dans mon entité, je viens de la créer manuellement sur le modèle d'autres fonctions set :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
        public function setMusers($musers)
        {
            $this->musers = $musers;
     
            return $this;
        }
    J'ai modifié le controleur pour qu'il en tienne compte :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
        public function editAction($groupid, Request $request)
        {
            $group = $this->get('common.mgroup_manager')->getRepository()->findById($groupid);
            $form = $this->createForm('mgroup', $group[0]);
            $form->handleRequest($request);
     
            if ($form->isValid())
            {
     
                $data = $form->getData();
                $users = $data->getMusers();
                $group[0]->setMusers($users);
                $em = $this->getDoctrine()->getManager();
                $em->persist($group[0]);
                $em->flush();
     
                return $this->redirectToRoute('common_user_mgroup_show', array('groupid' => $group[0]->getId()));
            }
    Aucune erreur mais aucun changement non plus.

    Le champ texte name du group est bien modifiée mais pas la liste des users.

    J'ai une solution palliative en passant par des routes avec en paramètres l'action ajout ou suppression, le user et le groupe mais maintenant que j'ai bloqué sur ce problème, culturellement j'aimerai bien trouver la solution

  6. #6
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Ce qui est bizarre c'est qu'il ne passe pas dans la méthode addMuser().

    À mon avis c'est ton point de départ, ou plutôt d'arrivée, au delà de ça ce n'est pas la peine de chercher à débugger le flush ou quoique ce soit d'autre. Il faut que tu comprennes pourquoi il ne passe pas dans cette méthode. XDebug pas à pas pour comprendre un peu le cheminement du process.

    Regarde les données qui ont été soumises, et l'état de ton objet après le handleRequest pour comprendre pourquoi la collection ne change pas.

    Oh et accessoirement tu peux la virer la méthode setMusers, j'ai pensé qu'il y en avait peut-être une générée automatiquement.

Discussions similaires

  1. comment faire fonctionner de l'ajax dans un gridview?
    Par igorzup dans le forum Framework .NET
    Réponses: 0
    Dernier message: 04/06/2009, 16h56
  2. Réponses: 1
    Dernier message: 15/04/2009, 14h54
  3. comment faire fonctionner les contextes tomcat5
    Par avengeur dans le forum Tomcat et TomEE
    Réponses: 2
    Dernier message: 06/01/2007, 09h55
  4. comment faire fonctionner les macros d'un modèle Word (.dot)?
    Par chtibreizh62 dans le forum VBA Word
    Réponses: 2
    Dernier message: 18/12/2006, 14h18

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo