Relation OneToMany et ManyToOne des entités Doctrine
Suite du tutoriel sur la création des relations entre entités pour leur persistance en base de données avec Doctrine.
Pour les précédent tutoriels :
Dans cette partie nous traiterons de OneToMany et sa réciproque ManyToOne
Une région possède zéro ou plusieurs départements.
Un département appartient à une et une seul région.
Voici le SQL correspondant :
TABLE `Region` ( `idRegion` bigint(20) NOT NULL AUTO_INCREMENT, `nom` varchar(60) DEFAULT NULL, PRIMARY KEY (`idRegion`)); TABLE `Departement` ( `idDepartement` INT NOT NULL , `nom` VARCHAR(60) NULL , `Region_idRegion` INT NOT NULL , PRIMARY KEY (`idDepartement`, `Region_idRegion`) , INDEX `fk_Departement_Region` (`Region_idRegion` ASC) , CONSTRAINT `fk_Departement_Region` FOREIGN KEY (`Region_idRegion` ) REFERENCES `Region` (`idRegion` ) ) ;
Voici les entités correspondante :
namespace WaldoRelationBundleEntity; use DoctrineORMMapping as ORM; use DoctrineCommonCollectionsArrayCollection; /** * @ORMTable(name="Region") * @ORMEntity */ class Region { /** * @var decimal $identifiantRegion * * @ORMColumn(name="idRegion", type="bigint", nullable=false) * @ORMId * @ORMGeneratedValue(strategy="IDENTITY") */ private $identifiantRegion; /** * @var text $nom * * @ORMColumn(name="nom", type="string", length=60, nullable=true) */ private $nom; /** * @var ArrayCollection $departements * * @ORMOneToMany(targetEntity="Departement", mappedBy="region", cascade={"persist", "remove", "merge"}) */ private $departements; /** * @return decimal $identifiantRegion */ public function getIdentifiantRegion() { return $this->identifiantRegion; } /** * @param text $nom */ public function setNom($nom) { $this->nom = $nom; } /** * @return text $nom */ public function getNom() { return $this->nom; } /** * @param BDepartement $departements */ public function addDepartement(Departement $departement) { $departement->setRegion($this); // Si l'objet fait déjà partie de la collection on ne l'ajoute pas if (!$this->departements->contains($departement)) { $this->departements->add($departement); } } /** * @return ArrayCollection $departements */ public function getDepartements() { return $this->departements; } public function __construct() { $this->departements = new ArrayCollection(); } }
namespace WaldoRelationBundleEntity; use DoctrineORMMapping as ORM; /** * @ORMTable(name="Departement") * @ORMEntity */ class Departement { /** * @var decimal $identifiantDepartement * * @ORMColumn(name="idDepartement", type="bigint", nullable=false) * @ORMId * @ORMGeneratedValue(strategy="IDENTITY") */ private $identifiantDepartement; /** * @var text $nom * * @ORMColumn(name="nom", type="string", length=60, nullable=true) */ private $nom; /** * @var Region $region * * @ORMManyToOne(targetEntity="Region", inversedBy="departements", cascade={"persist", "merge"}) * @ORMJoinColumns({ * @ORMJoinColumn(name="Region_idRegion", referencedColumnName="idRegion") * }) */ private $region; /** * @return decimal $identifiantDepartement */ public function getIdentifiantDepartement() { return $this->identifiantDepartement; } /** * @param text $nom */ public function setNom($nom) { $this->nom = $nom; } /** * @return text $nom */ public function getNom() { return $this->nom; } /** * @param BRegion $region */ public function setRegion(Region $region) { $this->region = $region; } /** * @return BRegion $region */ public function getRegion() { return $this->region; } }
Avec ce code on pourra faire ce genre de chose :
$coteDOr = new Departement(); $coteDOr->setNom("Côte d'Or"); $ain = new Departement(); $ain->setNom("Ain"); $region = new Region(); $region->setNom("Bourgogne"); $region->addDepartement($coteDOr); $region->addDepartement($ain); //ou, mais dans ce cas il faut faire attention à l'ordre de persistance $coteDOr->setRegion($region); $ain->setRegion($region);
Explication :
/** * Extrait de la classe Region * @var Collection $departements * * @ORMOneToMany(targetEntity="Departement", mappedBy="region", cascade={"persist", "remove", "merge"}) */ private $departements;
L’annotation OneToMany
définie que la propriété $departements
peut contenir un ou plusieurs objets, $departements
est de type ArrayCollection
.
L’attribut targetEntity
détermine de quelle classe dépendent les objets que $departements
peut contenir.
L’attribut mappedBy
détermine le nom de la propriété présente dans la classe visée (Departement) qui sert de lien.
cascade={"persist", "remove", "merge"})
: permet de définir le comportement lors de la persistance de l’objet Client.
/** * Extrait de la classe Departement * @var Region $region * * @ORMManyToOne(targetEntity="Region", cascade={"persist", "remove", "merge"}) * @ORMJoinColumns({ * @ORMJoinColumn(name="Region_idRegion", referencedColumnName="idRegion") * }) */ private $region;
L’annotation ManyToOne
définie que la propriété $region
est liée uniquement à un objet de type Region.
L’attribut targetEntity
définit la classe visée.
cascade={"persist", "remove", "merge"})
: permet de définir le comportement lors de la persistance de l’objet Client.
L’annotation JoinColumns
va permettre de définir quels champs de la table sont utilisés pour lier les deux entités.
L’annotation JoinColumn
permet de déterminer quels champs de la base de données permettent la relation.
L’attribut name
correspond au champ de la table Departement qui permet la relation.
L’attribut referencedColumnName
est le nom du champ de la table visée qui permet la relation.
Attention les valeur de name
et referencedColumnName
sont les noms des champs qui vont apparaître dans le SQL, alors que l’attribut targetEntity
ou mappedBy
prend le nom de l’entité (la classe) et non le nom de la table.
J’adore votre article :)
Merci pour les explications ça m’aide un petit peu.
Mais comment faire pour définir directement id_Region pour Departement ?
Par exemple :
En fait j’ai ce problème avec les Status pour des Articles avec une relation OneToMany pour les Status. Actuellement je suis obligé de récupérer en bdd pour set la foreign key. Exemple :
Sauf que j’ai besoin de le définir directement … Bref, je vais continuer à chercher
+1 par rapport au commentaire précédent.
J’ai le même problème que PH, sauf que je ne sais pas du tout comment le résoudre (débutant inside).
pourquoi l’ID de la région ne s’enregistre pas dans le département ?
Merci pour toute aide.
Le problème que tu soulève, je le comprend bien, je suis aussi par là.
@PH et toi pensé de manière SQL, et non objet.
Avec Doctrine vous gérez des Objets et non plus de simple lignes dans une BDD.
Ce code est donc non correcte avec l’utilisation de Doctrine:
Je te conseil plutôt de faire de cette manière :
Merci pour cette réponse et votre tutoriel, cela m’a grandement aidé !
Bonjour à vous, j’ai besoin de votre aide.
J’ai suivi votre tuto qui est tout simplement super.
Seulement j’ai un problème et je bloque depuis un moment dessus.
En fait, lors de l’ajout en BDD, doctrine me recrée une nouvelle entrée Région et le departement pointe maintenant sur l’id de cette nouvelle entrée.
J’aurais voulu qu’il pointe sur l’id region sans créer de doublons….
HELP please save my day :)
salut j’ai suivi ton tutoriel, et je fais usage d’un formulaire imbriguer pour inserer les departement et meme temps que les regions. tout fonctionne bien sauf au niveau de
idRegion qui reste toujours a une valeur par defaut NULL.
pourrais tu me donner un coup de main
merci d’avance
Humm,
difficile de répondre comme cela. Cela dépend de tellement de chose. Si tu as du code sur github ou autre, je veux bien y jeter un oeuil.
avec quelle version de symfony2 ?
Bonjour
avec quelle version de symfony2 ?
Merci
J’utilise ce système depuis la version 2.3.
Avec la version 2.7 de Symfony, cela fonctionne toujours.
tu peux me donner la requête pour donner la liste des des département par région
( select * FROM Departement where Region_idRegion = $id ) ?
Merci .
Quelque chose dans le genre :
Bonjour j’ai lu votre article il est très claire mais je n’est pas pu résoudre mon problème, est ce que c’est possible d’utiliser OneToOne d’un coté (entité A) et OneToMany d’un autre coté (entité B)
-entité B: contient un tableau d’entités A amis
/**
* @var ArrayCollection $amis
*
* @ORMOneToMany(targetEntity= »A », mappedBy= »B », cascade={« persist », « remove », « merge »})
*/
private $amis;
-entité A doit être en relation avec l’entité b pour récupérer la liste des amis
/**
* @ORM\OneToOne(targetEntity= »B »,mappedBy= »A », cascade={« persist », »remove »})
*
* @ORM\JoinColumn(nullable=true)
*/
private $amis;
Merci