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

Doctrine2 PHP Discussion :

Ordonner une relation ManyToMany avec un champs supplémentaire


Sujet :

Doctrine2 PHP

  1. #1
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    265
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 265
    Points : 281
    Points
    281
    Par défaut Ordonner une relation ManyToMany avec un champs supplémentaire
    Bonjour,

    J'ai un objet Group faisant référence à lui-même dans le cadre d'une relation parent-enfant, donc voici le schéma simplifié :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Table group
    id | parent_id | name
    Étant donné que l'arbre d'héritage peut être assez important, j'utilise une autre table pour y enregistrer toutes les relations de hiérarchie entre les groupes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Table group_group
    ancestor_id | descendant_id | depth
    Ainsi, si j'ai par exemple cet ensemble de données :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Table group
    id | parent_id | name
    1  | null      | Europe
    2  | 1         | France
    3  | 2         | Paris
     
    Table group_group
    ancestor_id | descendant_id | depth
    1           | 2             | 1
    2           | 3             | 1
    1           | 3             | 2
    Je peux récupérer directement tous les ancêtres ou les descendants d'un groupe particulier.

    Actuellement, ma classe Group contient le code de mapping suivant :
    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
    /**
         * @ManyToMany(targetEntity="Group", mappedBy="descendants")
         *
         * @var ArrayCollection Group[]
         */
        private $ancestors;
     
        /**
         * @ManyToMany(targetEntity="Group", inversedBy="ancestors")
         * @JoinTable(name="group_group",
         *      joinColumns={@JoinColumn(name="ancestor_id", referencedColumnName="id")},
         *      inverseJoinColumns={@JoinColumn(name="descendant_id", referencedColumnName="id")}
         * )
         *
         * @var ArrayCollection Group[]
         */
        private $descendants;
    J'aimerais savoir s'il est possible d'ajouter des paramètres à ces relations, ou encore mieux d'écrire soi-même le DQL de la relation à effectuer, comme ceci par exemple pour retourner les ancêtres sur 3 niveaux d'un groupe spécifié :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT g.* FROM `group` g JOIN group_group gg ON g.id = gg.ancestor_id  WHERE gg.descendant_id = $groupId AND gg.depth BETWEEN 1 AND 3 ORDER BY gg.depth
    Merci de votre aide.

  2. #2
    Membre éprouvé
    Homme Profil pro
    Inscrit en
    Juin 2011
    Messages
    725
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Juin 2011
    Messages : 725
    Points : 1 050
    Points
    1 050
    Par défaut
    Bonjour,

    J'ai pas tout compris à ta question, mais il existe l'extension tree pour Doctrine afin de gérer ce type de mapping:
    https://github.com/l3pp4rd/DoctrineE...er/doc/tree.md
    http://gediminasm.org/article/tree-n...for-doctrine-2
    et son Bundle pour l'intégrer à Symfony:
    https://github.com/stof/StofDoctrine.../doc/index.rst

  3. #3
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    265
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 265
    Points : 281
    Points
    281
    Par défaut
    Merci mais ça ne correspond pas vraiment à ce que je recherche, je préfère me rapprocher du SQL plutôt que d'ajouter un niveau d'abstraction que je devrais de toute façon modifier pour mes besoins.

    N'est-il pas possible via Doctrine de spécifier une fonction à appeler pour créer le mapping des relations ?

  4. #4
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2009
    Messages
    383
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2009
    Messages : 383
    Points : 658
    Points
    658

  5. #5
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    265
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 265
    Points : 281
    Points
    281
    Par défaut
    Non, cet ordre se fait sur un champs de la table distante, alors que je cherche à le faire sur la table intermédiaire, mais je n'ai trouvé aucune option dans Doctrine permettant de manipuler cette table.

    Pour contourner le problème, je passe par l'EntityRepository plutôt que l'entité en elle-même, avec cette fonction :
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    private function _getGroupRelatives($group, $relation, $options)
        {
            // Hash of opposite values for relation table, also used to check input validity
            $opposite = array(
                'ancestor' => 'descendant',
                'descendant' => 'ancestor'
            );
     
            // Only accepts relations previouly defined
            if (!isset($opposite[ $relation ])) {
                throw new \Exception('Invalid relation type "'. $relation .'" in _getGroupRelatives()');
            }
     
            // Only accepts Group entity or ID
            if ($group instanceof \Entities\Group) {
                $group = $group->getId();
            }
            if (!is_numeric($group)) {
                throw new \Exception('Invalid group parameter, expecting ID or Group entity');
            }
     
            $sql = 'SELECT * FROM `group` g
                JOIN group_group gh ON gh.'. $relation .'_id = g.id
                WHERE gh.'. $opposite[$relation] .'_id = :id';
            $params = array('id' => $group);
     
            /*
             * Available options:
             * false (default): get relatives without current group
             * true: get relatives WITH current group (useful when entity is not yet loaded)
             * integer: max depth of relations
             * array(min, max): min and max depth of relations
             */
            if ($options === false) {
                // Relatives ONLY, do not include current group
                $sql .= ' AND gh.depth > 0';
            }
            else if (is_int($options)) {
                // Max depth
                $sql .= ' AND gh.depth <= :max';
                $params['max'] = $options;
            }
            else if (is_array($options) && count($options) === 2) {
                // Min and max depth
                $sql .= ' AND gh.depth BETWEEN :min AND :max';
                $params['min'] = $options[isset($options['min']) ? 'min' : 0];
                $params['max'] = $options[isset($options['max']) ? 'max' : 1];
            }
     
            // Mapping used for Entity, should be useful to put elsewere in the class or in the entity itself
            $rsm = new ResultSetMapping;
            $rsm->addEntityResult('\Entities\Group', 'g');
            $rsm->addFieldResult('g', 'id', 'id');
            $rsm->addFieldResult('g', 'name', 'name');
     
            // Order by ascending depth by default
            $sql .= ' ORDER BY gh.depth';
            $query = $this->_em
                ->createNativeQuery($sql, $rsm)
                ->setParameters($params);
     
            return new ArrayCollection($query->getResult());
        }
    La fonction me donne le résultat escompté, le problème c'est que je ne peux pas y accéder directement depuis l'entité.
    Du coup je vais essayer de faire communiquer l'entité avec son repository via le gestionnaire d'évènements, si vous avez des exemples de codes utilisant cette technique je suis preneur.

    Merci de votre aide.

  6. #6
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2009
    Messages
    383
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2009
    Messages : 383
    Points : 658
    Points
    658
    Par défaut
    Ok j ai compris. Effectivement.

    Une solution est de ne pas utiliser la jointable en "automatique".

    - Dans ton entité, tu fais une relation OneToMany sur ta table de join avec un orderBy sur ton champ.
    - Dans ta table de join, tu fais un autre manyToone vers l'entité visée à la base.

    C'est relativement lourd mais au moins tu as acces à tous les champs de la table de jointure; vu que c'est une entité à part entière!

  7. #7
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    265
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 265
    Points : 281
    Points
    281
    Par défaut
    Oui c'est faisable comme ça mais ça perd quand même en intérêt. N'est-il pas possible d'étendre Doctrine pour y ajouter un type de relation dont le mapping serait personnalisé ?

  8. #8
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2009
    Messages
    383
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2009
    Messages : 383
    Points : 658
    Points
    658
    Par défaut
    Alors tu dois modifier le code de Doctrine... et c'est une mauvaise idée je pense... surtout lors des mises à jours.

    Tu peux toujours creer un ticket pour proposer la fonctionnalité. Tu seras bloqué en attendant... donc ma solution est plus lourde mais fonctionnelle tout de suite.

  9. #9
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    265
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 265
    Points : 281
    Points
    281
    Par défaut
    Sinon comme alternative j'ai pensé à utiliser l'évènement postLoad dans le Repository afin d'injecter les relations dans l'entité, mais ça fait perdre l'intérêt de pouvoir définir le niveau de profondeur, et ça oblige à faire ces requêtes même lorsque ces relations sont inutilisées dans le code.

    Je pense qu'au final je vais continuer à utiliser le Repository, ça fait peut-être moins naturel à l'utilisation mais en même temps ça évite la perte inutile de performances.

    Merci pour les infos !

Discussions similaires

  1. [2.x] [Form] Type pour une relation ManyToMany avec attributs
    Par FadeToBlack dans le forum Symfony
    Réponses: 10
    Dernier message: 09/08/2014, 03h46
  2. Réponses: 2
    Dernier message: 11/02/2013, 13h22
  3. [2.x] [Formulaire] Créer une entité avec une relation ManyToMany
    Par SalutAVous dans le forum Symfony
    Réponses: 3
    Dernier message: 30/10/2012, 22h13
  4. Problème pour requeter avec une relation manyToMany
    Par fab76000 dans le forum Hibernate
    Réponses: 0
    Dernier message: 26/04/2011, 17h32
  5. Réponses: 8
    Dernier message: 25/01/2011, 16h45

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