Quelle est la différence entre inversedBy et mappedBy?

102

Je développe mon application en utilisant Zend Framework 2 et Doctrine 2.

En écrivant des annotations, je ne peux pas comprendre la différence entre mappedByet inversedBy.

Quand dois-je utiliser mappedBy?

Quand dois-je utiliser inversedBy?

Quand dois-je utiliser ni l'un ni l'autre?

Voici un exemple:

 /**
 *
 * @ORM\OneToOne(targetEntity="\custMod\Entity\Person", mappedBy="customer")
 * @ORM\JoinColumn(name="personID", referencedColumnName="id")
 */
protected $person;

/**
 *
 * @ORM\OneToOne(targetEntity="\Auth\Entity\User")
 * @ORM\JoinColumn(name="userID", referencedColumnName="id")
 */
protected $user;

/**
 *
 * @ORM\ManyToOne (targetEntity="\custMod\Entity\Company", inversedBy="customer")
 * @ORM\JoinColumn (name="companyID", referencedColumnName="id")
 */
protected $company;

J'ai fait une recherche rapide et j'ai trouvé ce qui suit, mais je suis toujours confus:

Développeur
la source

Réponses:

159
  • mappedBy doit être spécifié du côté inversé d'une association (bidirectionnelle)
  • inversedBy doit être spécifié du côté propriétaire d'une association (bidirectionnelle)

à partir de la documentation de la doctrine:

  • ManyToOne est toujours le côté propriétaire d'une association bidirectionnelle.
  • OneToMany est toujours le côté inverse d'une association bidirectionnelle.
  • Le côté propriétaire d'une association OneToOne est l'entité avec la table contenant la clé étrangère.

Voir https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/unitofwork-associations.html

Andreas Linden
la source
1
Curieusement, le documenteur de Doctrine a décidé de laisser de côté l'exemple yaml d'un mappage bidirectionnel plusieurs-à-un, probablement le plus couramment utilisé!
Peter Wooster
4
@PeterWooster, la meilleure pratique consiste à utiliser les annotations, car vous avez toutes les informations sur l'entité au même endroit!
Andreas Linden
Cela s'applique également à de nombreuses relations. Pour ceux-ci: vous pouvez choisir vous-même le côté propriétaire d'une association plusieurs à plusieurs.
11 Mo
5
@AndreasLinden largement utilisé ne signifie pas une meilleure pratique. Quelque chose qui utilise des commentaires pour écrire du code à la volée ne peut jamais être considéré comme une meilleure pratique, ce n'est pas natif de PHP et même pas inclus par défaut dans tous les frameworks. Avoir toutes les informations sur une entité au même endroit est un anti-argument. Depuis quand regrouper tout votre code en un seul endroit est une bonne chose? C'est une douleur à écrire, une douleur à maintenir et à réduire l'organisation de votre projet. meilleur entrainement ? Duh.
JesusTheHun
4
@JesusTheHun vous comparez des pommes et des poires. "tout le code" est très très différent de "toutes les informations sur une entité";)
Andreas Linden
55

Les réponses ci-dessus n'étaient pas suffisantes pour que je comprenne ce qui se passait, donc après y avoir approfondi davantage, je pense que j'ai un moyen de l'expliquer qui aura du sens pour les personnes qui ont lutté comme moi pour comprendre.

inversedBy et mappedBy sont utilisés par le moteur INTERNAL DOCTRINE pour réduire le nombre de requêtes SQL qu'il doit effectuer pour obtenir les informations dont vous avez besoin. Pour être clair si vous n'ajoutez pas inversedBy ou mappedBy, votre code fonctionnera toujours mais ne sera pas optimisé .

Par exemple, regardez les classes ci-dessous:

class Task
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="task", type="string", length=255)
     */
    private $task;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="dueDate", type="datetime")
     */
    private $dueDate;

    /**
     * @ORM\ManyToOne(targetEntity="Category", inversedBy="tasks", cascade={"persist"})
     * @ORM\JoinColumn(name="category_id", referencedColumnName="id")
     */
    protected $category;
}

class Category
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255)
     */
    private $name;

    /**
     * @ORM\OneToMany(targetEntity="Task", mappedBy="category")
     */
    protected $tasks;
}

Ces classes si vous exécutez la commande pour générer le schéma (par exemple bin/console doctrine:schema:update --force --dump-sql), vous remarquerez que la table Category ne contient pas de colonne pour les tâches. (c'est parce qu'il n'a pas d'annotation de colonne)

La chose importante à comprendre ici est que les tâches variables sont uniquement là pour que le moteur de doctrine interne puisse utiliser la référence au-dessus de lui qui indique sa catégorie mappedBy. Maintenant ... ne soyez pas confondu ici comme je l'étais ... La catégorie ne fait pas référence au nom de la classe, elle fait référence à la propriété de la classe Task appelée 'protected $ category'.

De même, dans la classe Tasks, la propriété $ category mentionne qu'elle est inversedBy = "tasks", notez que c'est au pluriel, ce n'est PAS LE PLURIEL DU NOM DE LA CLASSE , mais simplement parce que la propriété est appelée 'protected $ tasks' dans la catégorie classe.

Une fois que vous avez compris cela, il devient très facile de comprendre ce que font inversedBy et mappedBy et comment les utiliser dans cette situation.

Le côté qui fait référence à la clé étrangère comme `` tâches '' dans mon exemple obtient toujours l'attribut inversedBy car il a besoin de savoir quelle classe (via la commande targetEntity) et quelle variable (inversedBy =) sur cette classe pour `` travailler en arrière '' pour parler et obtenir les informations sur la catégorie. Un moyen facile de s'en souvenir est que la classe qui aurait le Foreignkey_id est celle qui doit avoir inversedBy.

Où, comme pour la catégorie et sa propriété $ tasks (qui n'est pas sur la table, rappelez-vous, seule une partie de la classe à des fins d'optimisation) est MappedBy `` tâches '', cela crée officiellement la relation entre les deux entités afin que la doctrine puisse maintenant en toute sécurité utilisez des instructions SQL JOIN au lieu de deux instructions SELECT séparées. Sans mappedBy, le moteur de doctrine ne saurait à partir de l'instruction JOIN qu'il créera quelle variable dans la classe 'Task' pour mettre les informations de catégorie.

J'espère que cela explique un peu mieux.

Joseph Astrahan
la source
6
Ceci est très bien expliqué et merci pour vos efforts. Je suis venu de Laravel Eloquent à la doctrine et c'était difficile pour moi de comprendre la logique ici. Bien joué. Category is NOT referring TO THE CLASS NAME, its referring to the property on the Task class called 'protected $category'tout ce dont j'avais besoin. Cela n'a pas seulement résolu mon problème, mais cela m'a aidé à comprendre. La meilleure réponse IMO :-)
The Alpha
1
Moi aussi, je venais d'Eloquent, cela m'a beaucoup aidé. mon seul point de friction maintenant est de savoir comment définir le setter / getter pour cela, j'apprends toujours les ficelles du
métier
1
Oui, c'est vraiment facile et même automatisé avec certains outils, commencez une conversation avec moi plus tard et je peux vous aider
Joseph Astrahan
1
Vérifiez cette réponse, stackoverflow.com/questions/16629397/…
Joseph Astrahan
1
symfony.com/doc/current/doctrine/associations.html , il vaut mieux en fait apprendre, symfony utilise la doctrine donc il vous apprendra la même chose.
Joseph Astrahan
21

En relation bidirectionnelle a à la fois un côté propriétaire et un côté inverse

mappedBy : mettre dans Le côté inverse d'une relation bidirectionnelle Pour faire référence à son côté propriétaire

inversedBy : mettre dans Le côté propriétaire d'une relation bidirectionnelle Pour faire référence à son côté inverse

ET

Attribut mappedBy utilisé avec la déclaration de mappage OneToOne, OneToMany ou ManyToMany.

inversedBy attribut utilisé avec le OneToOne, ManyToOne ou ManyToMany déclaration de mise en correspondance.

Remarque : le côté propriétaire d'une relation bidirectionnelle est le côté qui contient la clé étrangère.

il y a deux références sur inversedBy et mappedBy dans Doctrine Documentation: First Link , Second Link

ahmed hamdy
la source
1
les liens sont morts?
Scaramouche
2

5.9.1. Propriétaire et côté inverse

Pour les associations plusieurs-à-plusieurs, vous pouvez choisir quelle entité est propriétaire et quel côté inverse. Il existe une règle sémantique très simple pour décider quel côté est le plus approprié pour être le côté propriétaire du point de vue des développeurs. Il vous suffit de vous demander quelle entité est responsable de la gestion de la connexion et de la choisir comme propriétaire.

Prenons un exemple de deux entités Article et Tag. Chaque fois que vous souhaitez connecter un article à un tag et vice-versa, c'est surtout l'article qui est responsable de cette relation. Chaque fois que vous ajoutez un nouvel article, vous souhaitez le connecter avec des balises existantes ou nouvelles. Votre formulaire de création d'article supportera probablement cette notion et permettra de spécifier directement les balises. C'est pourquoi vous devez choisir l'article comme côté propriétaire, car cela rend le code plus compréhensible:

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html

Micheal Mouner Mikhail Youssif
la source