Relation OneToMany et ManyToOne des entités Doctrine

Logo DoctrineSuite 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

Shchéma merise d'une relation OneToMany ManyToOneUne 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 Waldo\RelationBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Table(name="Region")
 * @ORM\Entity
 */
class Region
{
    /**
     * @var decimal $identifiantRegion
     *
     * @ORM\Column(name="idRegion", type="bigint", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $identifiantRegion;

    /**
     * @var text $nom
     *
     * @ORM\Column(name="nom", type="string", length=60, nullable=true)
     */
    private $nom;

     /**
     * @var ArrayCollection $departements
     *
     * @ORM\OneToMany(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 Waldo\RelationBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Table(name="Departement")
 * @ORM\Entity
 */
class Departement
{
    /**
     * @var decimal $identifiantDepartement
     *
     * @ORM\Column(name="idDepartement", type="bigint", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $identifiantDepartement;

    /**
     * @var text $nom
     *
     * @ORM\Column(name="nom", type="string", length=60, nullable=true)
     */
    private $nom;

    /**
     * @var Region $region
     *
     * @ORM\ManyToOne(targetEntity="Region", cascade={"persist", "remove", "merge"})
     * @ORM\JoinColumns({
     *  @ORM\JoinColumn(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
*
* @ORM\OneToMany(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
*
* @ORM\ManyToOne(targetEntity="Region", cascade={"persist", "remove", "merge"})
* @ORM\JoinColumns({
*  @ORM\JoinColumn(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.

Cette entrée a été publiée dans Doctrine, PHP, Symfony 2. Vous pouvez la mettre en favoris avec ce permalien.

Une réponse à Relation OneToMany et ManyToOne des entités Doctrine

  1. sebastien dit :

    J’adore votre article :)

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>