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 :

Compréhension des Relations SQL


Sujet :

Symfony PHP

  1. #1
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2010
    Messages
    38
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mars 2010
    Messages : 38
    Points : 181
    Points
    181
    Par défaut Compréhension des Relations SQL
    Bonjour,
    j'espère ne pas me tromper en postant sur ce forum, alors voila j'utilise MySQL depuis un moment mais sans la partie relationnelle (je favorisais 4D que je maitrisais mieux pour cela) mais depuis peu j'ai décidé de m'intéresser à Symfony (oui toujours pas de rapport avec ce forum) évidemment je suis le tuto jobeet et là je me retrouve avec un fichier YAML de création de bases de données SQL (MySQL pour l'exemple) qui utilise des relations.
    Alors ma question n'est pas la compréhension du YAML mais les relations créées à partir de celui-ci dans la partie "JobeetAffiliate", autant pour la relation "JobeetJob" j'ai assimilé autant pour les 2 suivantes je suis perdu notamment avec l'utilisation des attributs "class" et "refClass".
    Quelqu'un pourrait-il m'expliquer le pourquoi du comment de cela et si jamais je n'ai pas posté dans le bon forum éventuellement m'aiguiller.

    Merci

    Voici le code (en gras ce qui me pose problème) :
    Le lien vers le site source
    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
    # config/doctrine/schema.yml
    JobeetCategory:
        actAs: { Timestampable: ~ }
        columns:
            name: { type: string(255), notnull:true, unique: true }
            
    JobeetJob:
        actAs: { Timestampable: ~ }
        columns:
            category_id:    { type: integer, notnull: true }
            type:           { type: string(255) }
            company:        { type: string(255), notnull: true }
            logo:           { type: string(255) }
            url:            { type: string(255) }
            position:       { type: string(255), notnull: true }
            location:       { type: string(255), notnull: true }
            description:    { type: string(4000), notnull: true }
            how_to_apply:   { type: string(4000), notnull: true }
            token:          { type: string(255), notnull: true, unique: true }
            is_public:      { type: boolean, notnull: true, default: 1 }
            is_activated:   { type: boolean, notnull: true, default: 0 }
            email:          { type: string(255), notnull: true }
            expires_at:     { type: timestamp, notnull: true }
        relations:
            JobeetCategory: { onDelete: CASCADE, local: category_id, foreign: id, foreignAlias: JobeetJobs }
            
    JobeetAffiliate:
        actAs: { Timestampable: ~ }
        columns:
            url:        { type: string(255), notnull: true }
            email:      { type: string(255), notnull: true }
            token:      { type: string(255), notnull: true }
            is_active:  { type: boolean, notnull: true, default: 0 }
        relations:
            class:          JobeetCategory
            refClass:       JobeetCategoryAffiliate
            local:          affiliate_id
            foreign:        category_id
            foreignAlias:   JobeetAffiliates
                
    JobeetCategoryAffiliate:
        columns:
            category_id:    { type: integer, primary: true }
            affiliate_id:   { type: integer, primary: true }
        relations:
            JobeetCategory: { onDelete: CASCADE, local: category_id, foreign: id }
            JobeetAffiliate: { onDelete: CASCADE, local: affiliate_id, foreign: id }

  2. #2
    Invité
    Invité(e)
    Par défaut
    En fait il te manque une ligne sur ta relation. Ca devrait plutôt être comme ça :
    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
     
    JobeetAffiliate: #[1]
        actAs: { Timestampable: ~ }
        columns:
            url:        { type: string(255), notnull: true }
            email:      { type: string(255), notnull: true }
            token:      { type: string(255), notnull: true }
            is_active:  { type: boolean, notnull: true, default: 0 }
        relations:
          Categories: #[2]
            class:          JobeetCategory #[3]
            refClass:       JobeetCategoryAffiliate #[4]
            local:          affiliate_id
            foreign:        category_id
            foreignAlias:   JobeetAffiliates
    C'est un exemple. Les nombres entre crochets sont là juste pour que indiquer de quoi je parle quand j'explique :
    [1] : Ça c'est le nom de la classe. Ça veut dire que dans le code PHP tu aura un objet de type JobeetAffiliate
    [2] : C'est le nom de ta relation. Ça peut être n'importe quoi.
    [3] : C'est le type d'objet lié. Il faut donc que ce soit un vraie type d'objet.
    [4] : Le refClass est utilisé uniquement pour les relations many to many. Ca te permet, plutôt que de devoir appeler deux relations de suite pour avoir ce que tu veux, d'en appeler une seule qui est consciente qu'elle va passer par une table intermédiaire.

    Typiquement dans ton exemple là tu aurais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    $obj = Doctrine::getTable('JobeetAffiliate')->find(1); // récupère l'enregistrement ayant pour ID 1 en supposant qu'il existe
    $categories = $obj->getCategories(); // Te donne une Doctrine_Collection qui contient des JobeetCategory.
    En fait il fait 2 étapes pour toi : Il va récupérer les JobeetCategoryAffiliate correspondant, et leurs JobeetCategory lié. Mais il ne te retourne que les JobeetCategory, puisqu'au final on se fou des JobeetCategoryAffiliate qui ne contiennent que deux FK

  3. #3
    Expert éminent
    Avatar de Michel Rotta
    Homme Profil pro
    DPO
    Inscrit en
    Septembre 2005
    Messages
    4 954
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : DPO
    Secteur : Distribution

    Informations forums :
    Inscription : Septembre 2005
    Messages : 4 954
    Points : 8 486
    Points
    8 486
    Par défaut
    C'est pas évident au départ. Et le gras n'est pas affiché dans le code

    Ce qu'il faut savoir c'est que le fichier shema.yml comporte des informations doctrines et non pas symfony. Tu trouveras sur le site de doctrine de nombreuses informations intéressantes.

    Il y a deux types de relations dans doctrine type 1 (1-1 ou 1-n) et type 2 (n-n), la deuxième est un peu plus compliquée à comprendre. Il y a d'autre type de relation tel que des réflectives, mais elles restent beaucoup plus rares.

    Pour le type 1, il faut bien voir que les relations ne sont déclarée que d'un des deux côtés de la relation, mais on va y déclarer des informations pour les deux côtés de la relation.

    Et pour tous simplifier doctrine travail beaucoup avec des conventions de nommage pour les id (identifiants unique d'un enregistrement) et les identifiants d'un enregistrement dans une autre table. Ce qui est très pratique quant on les utilises, 90% du code de la relation est automatiquement généré, mais qui va rendre les premières lectures obscures, vu que tous se passe en tâche de fond, sans qu'on ne voient la réalité de ce qui se déroule.

    Dernière automatisme qui va rendre les choses un peu obscures, quant on crée une table sans définir de "primary key" doctrine initie un champ "id" de type "integer 4" (attention, si l'on utilise dans une table "interger" pour un champ, il va créer un "interger 8".

    Explications :
    La première table "JobeetCategory" comporte en fait deux champs, "name" mais aussi "id" qui c'est auto-généré.

    De même sur la table "JobeetJob" il y aura un champ "id" qui est auto-généré.

    Pour les relations entres les tables JobeetCategory, JobeetJob et JobeetAffiliate (j'ignore volontairement JobeetCategoryAffiliate), nous avons une relation de type 1 (1-n) entre JobbetCategory et JobeetJob. et une relation de type 2 entre JobeetCategory et JobeetAffiliate (qui passe par JobeetCategoryAffiliate)


    Relation de type 1 entre JobbetCategory et JobeetJob.

    En général on défini les relations 1-N du côté N de la relation.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    JobeetJob:
      columns:
        ..
      relations:
        JobeetCategory:           (1)
          onDelete: CASCADE
          local: category_id        (2)
          foreign: id                   (3)
          foreignAlias: JobeetJobs   (4)
    (1) C'est le nom de l'alias de la relation entre la table JobeetJob et la table JobeetCategory. Par convention, si le paramètre class n'est pas définit, ce qui est le cas ici, le nom de la table est le même que le nom d'alias de la relation.
    Dans le code on accédera à l'enregistrement de la catégorie correspondante par la méthode "->JobeetCategory()"

    (2) Le nom du champs local qui sera mis en relation avec la clef de l'autre table. Il faut le déclarer ici, le nom par défaut aurait été JobeetCategory_id.

    (3) Le nom de la clef de l'autre côté de la relation, on aurait pu se passer de cette information, c'est le nom par défaut.

    (4) C'est le nom de l'alias du côté des catégories. On accéderas donc, depuis une catégories à la collection des Job avec la méthode "->JobeetJobs()" attention, on obtient ici une collection à parcourir avec un foreach par exemple. Le "s" en fin de nom signifie (convention) qu'il y a une collection d'objets dans la réponse.


    On a traité le cas simple, passons à l'autre.

    Relation de type 2 entre JobeetCategory et JobeetAffiliate.

    Pour une relation de type n-n on va passer par une table intermédiaire ici JobeetCategoryAffiliate. Cette table ne peu/doit déclarer que deux champs, deux champs déclaré en clef primaire, qui vont permettre de matérialiser la relation n-n.

    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
    JobeetCategoryAffiliate:
      columns:
        category_id:
          type: integer
          primary: true  (1)
        affiliate_id:
          type: integer
          primary: true
      relations:  (2)
        JobeetCategory:
          onDelete: CASCADE
          local: category_id
          foreign: id 
        JobeetAffiliate:
          onDelete: CASCADE
          local: affiliate_id
          foreign: id
    (1) les deux champs sont en "primary true" ce qui génère dans la base un index unique sur les deux champs.

    (2) les deux relations sont définie ici comme deux relations de type 1 (ce qui est matériellement le cas) vers les deux tables. (voir les remarques du point précédant).

    La relation n-n est finalisée dans la table JobeetAffiliate (mais elle aurait pu être définie du côté Category sans que cela ne change rien.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    JobeetAffiliate:
        actAs: { Timestampable: ~ }
        columns:   (1)
            url:        { type: string(255), notnull: true }
            email:      { type: string(255), notnull: true }
            token:      { type: string(255), notnull: true }
            is_active:  { type: boolean, notnull: true, default: 0 }
        relations:
          JobeetCategories: (3)
            class:          JobeetCategory (4)
            refClass:       JobeetCategoryAffiliate (5)
            local:          affiliate_id (6)
            foreign:        category_id (7)
            foreignAlias:   JobeetAffiliates (8)
    (1) on remarquera qu'il n'y a pas de champ "primary key", un champ id sera donc automatiquement généré.

    (3) Attention, cette information ne figure pas dans ton shema.yml (ce qui va rendre le fonctionnement de la relation impossible). C'est l'allias qui va permettre au affiliates de récupérer la collection des objets catégory correspondants (->getJobeetCategories).

    (4) un oeil attentif aura remarqué que l'alias définit au point (3) n'est pas le nom de la table. Donc il faut ici définir le nom de la table qui sera utilisé pour la relation.

    (5) la refClass est la classe qui permet la relation n-n et que nous avons analysé ci-dessus.

    (6) le champ clef dans la table courante

    (7) le champ clef dans la table distante (définie par class, voir le point 4)

    (8) L'allias de la table distante a utiliser ici. On remarquera que le nom de la table distante foreingClass n'est pas défini, en fait il l'est dans la table de liaison JobeetCategoryAffiliate.


    CQFD.

    Bonne chance


    @Tirkyth : Plus rapide !

  4. #4
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2010
    Messages
    38
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mars 2010
    Messages : 38
    Points : 181
    Points
    181
    Par défaut
    Merci beaucoup pour vos réponses ça m'a beaucoup éclairé, je m'empresse d'imprimer tout ça pour bien comprendre le fonctionnement (enfin du moins essayer), mais si j'ai à peu près compris le principe, doctrine via le fichier YAML fait plus que de créer la base de donnée.
    Concernant le type 2 oui ça me laisse dubitatif mais en même temps il faut bien apprendre à pécher un jour !!
    Je vais essayer de bien m'imprégner du scenario de jobeet pour avoir une vue globale du besoin et sans doute mieux comprendre les relations requises.
    Une fois encore merci beaucoup d'avoir pris le temps et d'avoir autant détaillé.

  5. #5
    Futur Membre du Club
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 4
    Points : 5
    Points
    5
    Par défaut
    Citation Envoyé par mimi68 Voir le message
    . Cette table ne peu/doit déclarer que deux champs,
    Cela veut dire qu'on peut pas avoir plus de deux relations n-n via une table intermédiaire? Comment on fait si notre nombre de relations n-n est supérieur a deux ?

  6. #6
    Invité
    Invité(e)
    Par défaut
    Si bien sûr, rien n'empêche d'en avoir plus de deux ^^

    En admettant que tu aies un job d'id 1 tu peux avoir autant de correspondances que tu veux avec des affliliés :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    job_id | affiliate_id
    1        1
    1        2
    1        3
    1        4
    1        6
    1        12
    etc...

    A moins que je comprenne mal ta question

  7. #7
    Futur Membre du Club
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 4
    Points : 5
    Points
    5
    Par défaut
    Tout d'abord merci d'avoir repondu.
    En fait, je voulais savoir si c'était possible de faire passer plusieurs relations n-n par la meme table intermédiaire, par exemple j'ai 3 tables épée, bouclier et soldat.
    A chaque soldat peut correspondre un ou plusieurs boucliers ainsi qu'une ou plusieurs épées. Une épée ou un bouclier peuvent egalement correspondre a un ou plusieurs soldats.
    J'aimerai donc faire passer les relations n-n de bouclier/soldat et épée/soldat par la meme table intermediaire "equipement", donc cette table intermédiaire aurait comme champs, soldat_id, epee_id et bouclier_id. Malheureusement ca a pas l'air de marcher, j'ai un probleme de clé etrangere qui est la suivante lorsque j'essaye d'ajouter un soldat avec un certain bouclier et une certaine épée :


    QLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails

    J'aimerai savoir si cette erreur provient du fait que symfony n'arrive pas a interpreter plusieurs relations n-n sur la meme table intermédiaire ou si cela na rien avoir?

  8. #8
    Invité
    Invité(e)
    Par défaut
    Non c'est possible, je suis sûr de l'avoir déjà fait. Tu as mis quoi dans ton schema.yml ?

  9. #9
    Futur Membre du Club
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 4
    Points : 5
    Points
    5
    Par défaut
    Voila le schema :
    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
     
    # config/doctrine/schema.yml
     
    soldat:
      actAs: { Timestampable: ~ }
      columns:
        name:         { type : string(255), unique: true, notnull: true}
      relations:
        epee:
          class: epee
          refClass: equipement
          local: soldat_id
          foreign: epee_id
     
        bouclier:
          class: bouclier
          refClass: equipement
          local: soldat_id
          foreign: bouclier_id
     
     
    epee:
      actAs: { Timestampable: ~ }
      columns:
        name:          { type : string(255), unique: true, notnull: true}
        description:   { type : string(4000)}
      relations:
         soldat:
          class: soldat
          refClass: equipement
          local: epee_id
          foreign: soldat_id
     
     
    bouclier:
      actAs: { Timestampable: ~ }
      columns:
        name:         { type : string(255), unique: true, notnull: true}
        description:  { type : string(4000)}
      relations:
        soldat:
          class: soldat
          refClass: equipement
          local: epee_id
          foreign: soldat_id
     
     
    equipement:
      actAs: { Timestampable: ~ }
      columns:
        soldat_id:   { type : integer, unique: true, notnull: true, primary: true}
        bouclier_id:     { type : integer, unique: true, notnull: true, primary: true}
        epee_id:   { type : integer, unique: true, notnull: true, primary: true}

  10. #10
    Expert éminent
    Avatar de Michel Rotta
    Homme Profil pro
    DPO
    Inscrit en
    Septembre 2005
    Messages
    4 954
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : DPO
    Secteur : Distribution

    Informations forums :
    Inscription : Septembre 2005
    Messages : 4 954
    Points : 8 486
    Points
    8 486
    Par défaut
    A ma connaissance ce n'est pas possible de faire passer deux relations n-n par une même table. Il te faut une table par relation.

    Il est possible de créer des tables héritées, mais je ne vois pas l'intérêt de le faire ici, et la lisibilité général s'en ressentirait.

  11. #11
    Invité
    Invité(e)
    Par défaut
    Si tu enlèves les "unique: true" sur ta table intermédiaire ça fait pas pile poil ce que tu veux ?

  12. #12
    Expert éminent
    Avatar de Michel Rotta
    Homme Profil pro
    DPO
    Inscrit en
    Septembre 2005
    Messages
    4 954
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : DPO
    Secteur : Distribution

    Informations forums :
    Inscription : Septembre 2005
    Messages : 4 954
    Points : 8 486
    Points
    8 486
    Par défaut
    Tu ne pourras pas définir les 4 relations nécessaires (2x2) sur la table de liaison.

    C'est pire qu'une usine atomique ukrainienne.

    Je ne vois pas l'intérêt de ne pas mettre simplement deux tables.

    Et en plus, ce n'est pas conforme aux formes normal.

Discussions similaires

  1. [MySQL] Requête SQL pour afficher des "Related Items"
    Par neoweiter dans le forum PHP & Base de données
    Réponses: 4
    Dernier message: 15/07/2009, 14h15
  2. Script SQL avec des EXIT SQL.SQLCODE
    Par fidififouille dans le forum Oracle
    Réponses: 14
    Dernier message: 23/04/2004, 16h45
  3. Recuperer les erreurs des requetes sql en asp
    Par emile13 dans le forum ASP
    Réponses: 3
    Dernier message: 01/04/2004, 13h49
  4. [FLASH MX] Prob de compréhension des bouttons
    Par WriteLN dans le forum Flash
    Réponses: 13
    Dernier message: 16/10/2003, 17h01
  5. Problème de compréhension des ensembles
    Par Cornell dans le forum Langage
    Réponses: 6
    Dernier message: 07/02/2003, 22h07

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