12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145 |
- Association Mapping
- ===================
- This chapter introduces association mappings which are used to explain
- references between objects and are mapped to a relational database using
- foreign keys.
- Instead of working with the foreign keys directly you will always work with
- references to objects:
- - A reference to a single object is represented by a foreign key.
- - A collection of objects is represented by many foreign keys pointing to the object holding the collection
- This chapter is split into three different sections.
- - A list of all the possible association mapping use-cases is given.
- - :ref:`association_mapping_defaults` are explained that simplify the use-case examples.
- - :ref:`collections` are introduced that contain entities in associations.
- To master associations you should also learn about :doc:`owning and inverse sides of associations <unitofwork-associations>`
- One-To-One, Unidirectional
- --------------------------
- A unidirectional one-to-one association is very common. Here is an
- example of a ``Product`` that has one ``Shipping`` object
- associated to it. The ``Shipping`` side does not reference back to
- the ``Product`` so it is unidirectional.
- .. configuration-block::
- .. code-block:: php
- <?php
- /** @Entity **/
- class Product
- {
- // ...
- /**
- * @OneToOne(targetEntity="Shipping")
- * @JoinColumn(name="shipping_id", referencedColumnName="id")
- **/
- private $shipping;
- // ...
- }
- /** @Entity **/
- class Shipping
- {
- // ...
- }
- .. code-block:: xml
- <doctrine-mapping>
- <entity class="Product">
- <one-to-one field="shipping" target-entity="Shipping">
- <join-column name="shipping_id" referenced-column-name="id" />
- </one-to-one>
- </entity>
- </doctrine-mapping>
- .. code-block:: yaml
- Product:
- type: entity
- oneToOne:
- shipping:
- targetEntity: Shipping
- joinColumn:
- name: shipping_id
- referencedColumnName: id
- Note that the @JoinColumn is not really necessary in this example,
- as the defaults would be the same.
- Generated MySQL Schema:
- .. code-block:: sql
- CREATE TABLE Product (
- id INT AUTO_INCREMENT NOT NULL,
- shipping_id INT DEFAULT NULL,
- UNIQUE INDEX UNIQ_6FBC94267FE4B2B (shipping_id),
- PRIMARY KEY(id)
- ) ENGINE = InnoDB;
- CREATE TABLE Shipping (
- id INT AUTO_INCREMENT NOT NULL,
- PRIMARY KEY(id)
- ) ENGINE = InnoDB;
- ALTER TABLE Product ADD FOREIGN KEY (shipping_id) REFERENCES Shipping(id);
- One-To-One, Bidirectional
- -------------------------
- Here is a one-to-one relationship between a ``Customer`` and a
- ``Cart``. The ``Cart`` has a reference back to the ``Customer`` so
- it is bidirectional.
- .. configuration-block::
- .. code-block:: php
- <?php
- /** @Entity **/
- class Customer
- {
- // ...
- /**
- * @OneToOne(targetEntity="Cart", mappedBy="customer")
- **/
- private $cart;
- // ...
- }
- /** @Entity **/
- class Cart
- {
- // ...
- /**
- * @OneToOne(targetEntity="Customer", inversedBy="cart")
- * @JoinColumn(name="customer_id", referencedColumnName="id")
- **/
- private $customer;
- // ...
- }
- .. code-block:: xml
- <doctrine-mapping>
- <entity name="Customer">
- <one-to-one field="cart" target-entity="Cart" mapped-by="customer" />
- </entity>
- <entity name="Cart">
- <one-to-one field="customer" target-entity="Customer" inversed-by="cart">
- <join-column name="customer_id" referenced-column-name="id" />
- </one-to-one>
- </entity>
- </doctrine-mapping>
- .. code-block:: yaml
- Customer:
- oneToOne:
- cart:
- targetEntity: Cart
- mappedBy: customer
- Cart:
- oneToOne:
- customer:
- targetEntity: Customer
- inversedBy: cart
- joinColumn:
- name: customer_id
- referencedColumnName: id
- Note that the @JoinColumn is not really necessary in this example,
- as the defaults would be the same.
- Generated MySQL Schema:
- .. code-block:: sql
- CREATE TABLE Cart (
- id INT AUTO_INCREMENT NOT NULL,
- customer_id INT DEFAULT NULL,
- PRIMARY KEY(id)
- ) ENGINE = InnoDB;
- CREATE TABLE Customer (
- id INT AUTO_INCREMENT NOT NULL,
- PRIMARY KEY(id)
- ) ENGINE = InnoDB;
- ALTER TABLE Cart ADD FOREIGN KEY (customer_id) REFERENCES Customer(id);
- See how the foreign key is defined on the owning side of the
- relation, the table ``Cart``.
- One-To-One, Self-referencing
- ----------------------------
- You can easily have self referencing one-to-one relationships like
- below.
- .. code-block:: php
- <?php
- /** @Entity **/
- class Student
- {
- // ...
- /**
- * @OneToOne(targetEntity="Student")
- * @JoinColumn(name="mentor_id", referencedColumnName="id")
- **/
- private $mentor;
- // ...
- }
- Note that the @JoinColumn is not really necessary in this example,
- as the defaults would be the same.
- With the generated MySQL Schema:
- .. code-block:: sql
- CREATE TABLE Student (
- id INT AUTO_INCREMENT NOT NULL,
- mentor_id INT DEFAULT NULL,
- PRIMARY KEY(id)
- ) ENGINE = InnoDB;
- ALTER TABLE Student ADD FOREIGN KEY (mentor_id) REFERENCES Student(id);
- One-To-Many, Unidirectional with Join Table
- -------------------------------------------
- A unidirectional one-to-many association can be mapped through a
- join table. From Doctrine's point of view, it is simply mapped as a
- unidirectional many-to-many whereby a unique constraint on one of
- the join columns enforces the one-to-many cardinality.
- .. note::
- One-To-Many uni-directional relations with join-table only
- work using the @ManyToMany annotation and a unique-constraint.
- The following example sets up such a unidirectional one-to-many association:
- .. configuration-block::
- .. code-block:: php
- <?php
- /** @Entity **/
- class User
- {
- // ...
- /**
- * @ManyToMany(targetEntity="Phonenumber")
- * @JoinTable(name="users_phonenumbers",
- * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
- * inverseJoinColumns={@JoinColumn(name="phonenumber_id", referencedColumnName="id", unique=true)}
- * )
- **/
- private $phonenumbers;
- public function __construct()
- {
- $this->phonenumbers = new \Doctrine\Common\Collections\ArrayCollection();
- }
- // ...
- }
- /** @Entity **/
- class Phonenumber
- {
- // ...
- }
- .. code-block:: xml
- <doctrine-mapping>
- <entity name="User">
- <many-to-many field="phonenumbers" target-entity="Phonenumber">
- <join-table name="users_phonenumbers">
- <join-columns>
- <join-column name="user_id" referenced-column-name="id" />
- </join-columns>
- <inverse-join-columns>
- <join-column name="phonenumber_id" referenced-column-name="id" unique="true" />
- </inverse-join-columns>
- </join-table>
- </many-to-many>
- </entity>
- </doctrine-mapping>
- .. code-block:: yaml
- User:
- type: entity
- manyToMany:
- phonenumbers:
- targetEntity: Phonenumber
- joinTable:
- name: users_phonenumbers
- joinColumns:
- user_id:
- referencedColumnName: id
- inverseJoinColumns:
- phonenumber_id:
- referencedColumnName: id
- unique: true
- Generates the following MySQL Schema:
- .. code-block:: sql
- CREATE TABLE User (
- id INT AUTO_INCREMENT NOT NULL,
- PRIMARY KEY(id)
- ) ENGINE = InnoDB;
- CREATE TABLE users_phonenumbers (
- user_id INT NOT NULL,
- phonenumber_id INT NOT NULL,
- UNIQUE INDEX users_phonenumbers_phonenumber_id_uniq (phonenumber_id),
- PRIMARY KEY(user_id, phonenumber_id)
- ) ENGINE = InnoDB;
- CREATE TABLE Phonenumber (
- id INT AUTO_INCREMENT NOT NULL,
- PRIMARY KEY(id)
- ) ENGINE = InnoDB;
- ALTER TABLE users_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES User(id);
- ALTER TABLE users_phonenumbers ADD FOREIGN KEY (phonenumber_id) REFERENCES Phonenumber(id);
- Many-To-One, Unidirectional
- ---------------------------
- You can easily implement a many-to-one unidirectional association
- with the following:
- .. configuration-block::
- .. code-block:: php
- <?php
- /** @Entity **/
- class User
- {
- // ...
- /**
- * @ManyToOne(targetEntity="Address")
- * @JoinColumn(name="address_id", referencedColumnName="id")
- **/
- private $address;
- }
- /** @Entity **/
- class Address
- {
- // ...
- }
- .. code-block:: xml
- <doctrine-mapping>
- <entity name="User">
- <many-to-one field="address" target-entity="Address">
- <join-column name="address_id" referenced-column-name="id" />
- </many-to-one>
- </entity>
- </doctrine-mapping>
- .. code-block:: yaml
- User:
- type: entity
- manyToOne:
- address:
- targetEntity: Address
- joinColumn:
- name: address_id
- referencedColumnName: id
- .. note::
- The above ``@JoinColumn`` is optional as it would default
- to ``address_id`` and ``id`` anyways. You can omit it and let it
- use the defaults.
- Generated MySQL Schema:
- .. code-block:: sql
- CREATE TABLE User (
- id INT AUTO_INCREMENT NOT NULL,
- address_id INT DEFAULT NULL,
- PRIMARY KEY(id)
- ) ENGINE = InnoDB;
- CREATE TABLE Address (
- id INT AUTO_INCREMENT NOT NULL,
- PRIMARY KEY(id)
- ) ENGINE = InnoDB;
- ALTER TABLE User ADD FOREIGN KEY (address_id) REFERENCES Address(id);
- One-To-Many, Bidirectional
- --------------------------
- Bidirectional one-to-many associations are very common. The
- following code shows an example with a Product and a Feature
- class:
- .. configuration-block::
- .. code-block:: php
- <?php
- /** @Entity **/
- class Product
- {
- // ...
- /**
- * @OneToMany(targetEntity="Feature", mappedBy="product")
- **/
- private $features;
- // ...
- public function __construct() {
- $this->features = new \Doctrine\Common\Collections\ArrayCollection();
- }
- }
- /** @Entity **/
- class Feature
- {
- // ...
- /**
- * @ManyToOne(targetEntity="Product", inversedBy="features")
- * @JoinColumn(name="product_id", referencedColumnName="id")
- **/
- private $product;
- // ...
- }
- .. code-block:: xml
- <doctrine-mapping>
- <entity name="Product">
- <one-to-many field="features" target-entity="Feature" mapped-by="product" />
- </entity>
- <entity name="Feature">
- <many-to-one field="product" target-entity="Product" inversed-by="features">
- <join-column name="product_id" referenced-column-name="id" />
- </many-to-one>
- </entity>
- </doctrine-mapping>
- .. code-block:: yaml
- Product:
- type: entity
- oneToMany:
- features:
- targetEntity: Feature
- mappedBy: product
- Feature:
- type: entity
- manyToOne:
- product:
- targetEntity: Product
- inversedBy: features
- joinColumn:
- name: product_id
- referencedColumnName: id
- Note that the @JoinColumn is not really necessary in this example,
- as the defaults would be the same.
- Generated MySQL Schema:
- .. code-block:: sql
- CREATE TABLE Product (
- id INT AUTO_INCREMENT NOT NULL,
- PRIMARY KEY(id)
- ) ENGINE = InnoDB;
- CREATE TABLE Feature (
- id INT AUTO_INCREMENT NOT NULL,
- product_id INT DEFAULT NULL,
- PRIMARY KEY(id)
- ) ENGINE = InnoDB;
- ALTER TABLE Feature ADD FOREIGN KEY (product_id) REFERENCES Product(id);
- One-To-Many, Self-referencing
- -----------------------------
- You can also setup a one-to-many association that is
- self-referencing. In this example we setup a hierarchy of
- ``Category`` objects by creating a self referencing relationship.
- This effectively models a hierarchy of categories and from the
- database perspective is known as an adjacency list approach.
- .. configuration-block::
- .. code-block:: php
- <?php
- /** @Entity **/
- class Category
- {
- // ...
- /**
- * @OneToMany(targetEntity="Category", mappedBy="parent")
- **/
- private $children;
- /**
- * @ManyToOne(targetEntity="Category", inversedBy="children")
- * @JoinColumn(name="parent_id", referencedColumnName="id")
- **/
- private $parent;
- // ...
- public function __construct() {
- $this->children = new \Doctrine\Common\Collections\ArrayCollection();
- }
- }
- .. code-block:: xml
- <doctrine-mapping>
- <entity name="Category">
- <one-to-many field="children" target-entity="Category" mapped-by="parent" />
- <many-to-one field="parent" target-entity="Category" inversed-by="children" />
- </entity>
- </doctrine-mapping>
- .. code-block:: yaml
- Category:
- type: entity
- oneToMany:
- children:
- targetEntity: Category
- mappedBy: parent
- manyToOne:
- parent:
- targetEntity: Category
- inversedBy: children
- Note that the @JoinColumn is not really necessary in this example,
- as the defaults would be the same.
- Generated MySQL Schema:
- .. code-block:: sql
- CREATE TABLE Category (
- id INT AUTO_INCREMENT NOT NULL,
- parent_id INT DEFAULT NULL,
- PRIMARY KEY(id)
- ) ENGINE = InnoDB;
- ALTER TABLE Category ADD FOREIGN KEY (parent_id) REFERENCES Category(id);
- Many-To-Many, Unidirectional
- ----------------------------
- Real many-to-many associations are less common. The following
- example shows a unidirectional association between User and Group
- entities:
- .. configuration-block::
- .. code-block:: php
- <?php
- /** @Entity **/
- class User
- {
- // ...
- /**
- * @ManyToMany(targetEntity="Group")
- * @JoinTable(name="users_groups",
- * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
- * inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id")}
- * )
- **/
- private $groups;
- // ...
- public function __construct() {
- $this->groups = new \Doctrine\Common\Collections\ArrayCollection();
- }
- }
- /** @Entity **/
- class Group
- {
- // ...
- }
- .. code-block:: xml
- <doctrine-mapping>
- <entity name="User">
- <many-to-many field="groups" target-entity="Group">
- <join-table name="users_groups">
- <join-columns>
- <join-column name="user_id" referenced-column-name="id" />
- </join-columns>
- <inverse-join-columns>
- <join-column name="group_id" referenced-column-name="id" />
- </inverse-join-columns>
- </join-table>
- </many-to-many>
- </entity>
- </doctrine-mapping>
- .. code-block:: yaml
- User:
- type: entity
- manyToMany:
- groups:
- targetEntity: Group
- joinTable:
- name: users_groups
- joinColumns:
- user_id:
- referencedColumnName: id
- inverseJoinColumns:
- group_id:
- referencedColumnName: id
- Generated MySQL Schema:
- .. code-block:: sql
- CREATE TABLE User (
- id INT AUTO_INCREMENT NOT NULL,
- PRIMARY KEY(id)
- ) ENGINE = InnoDB;
- CREATE TABLE users_groups (
- user_id INT NOT NULL,
- group_id INT NOT NULL,
- PRIMARY KEY(user_id, group_id)
- ) ENGINE = InnoDB;
- CREATE TABLE Group (
- id INT AUTO_INCREMENT NOT NULL,
- PRIMARY KEY(id)
- ) ENGINE = InnoDB;
- ALTER TABLE users_groups ADD FOREIGN KEY (user_id) REFERENCES User(id);
- ALTER TABLE users_groups ADD FOREIGN KEY (group_id) REFERENCES Group(id);
- .. note::
- Why are many-to-many associations less common? Because
- frequently you want to associate additional attributes with an
- association, in which case you introduce an association class.
- Consequently, the direct many-to-many association disappears and is
- replaced by one-to-many/many-to-one associations between the 3
- participating classes.
- Many-To-Many, Bidirectional
- ---------------------------
- Here is a similar many-to-many relationship as above except this
- one is bidirectional.
- .. configuration-block::
- .. code-block:: php
- <?php
- /** @Entity **/
- class User
- {
- // ...
- /**
- * @ManyToMany(targetEntity="Group", inversedBy="users")
- * @JoinTable(name="users_groups")
- **/
- private $groups;
- public function __construct() {
- $this->groups = new \Doctrine\Common\Collections\ArrayCollection();
- }
- // ...
- }
- /** @Entity **/
- class Group
- {
- // ...
- /**
- * @ManyToMany(targetEntity="User", mappedBy="groups")
- **/
- private $users;
- public function __construct() {
- $this->users = new \Doctrine\Common\Collections\ArrayCollection();
- }
- // ...
- }
- .. code-block:: xml
- <doctrine-mapping>
- <entity name="User">
- <many-to-many field="groups" inversed-by="users" target-entity="Group">
- <join-table name="users_groups">
- <join-columns>
- <join-column name="user_id" referenced-column-name="id" />
- </join-columns>
- <inverse-join-columns>
- <join-column name="group_id" referenced-column-name="id" />
- </inverse-join-columns>
- </join-table>
- </many-to-many>
- </entity>
- <entity name="Group">
- <many-to-many field="users" mapped-by="groups" target-entity="User"/>
- </entity>
- </doctrine-mapping>
- .. code-block:: yaml
- User:
- type: entity
- manyToMany:
- groups:
- targetEntity: Group
- inversedBy: users
- joinTable:
- name: users_groups
- joinColumns:
- user_id:
- referencedColumnName: id
- inverseJoinColumns:
- group_id:
- referencedColumnName: id
- Group:
- type: entity
- manyToMany:
- users:
- targetEntity: User
- mappedBy: groups
- The MySQL schema is exactly the same as for the Many-To-Many
- uni-directional case above.
- Picking Owning and Inverse Side
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- For Many-To-Many associations you can chose which entity is the
- owning and which the inverse side. There is a very simple semantic
- rule to decide which side is more suitable to be the owning side
- from a developers perspective. You only have to ask yourself, which
- entity is responsible for the connection management and pick that
- as the owning side.
- Take an example of two entities ``Article`` and ``Tag``. Whenever
- you want to connect an Article to a Tag and vice-versa, it is
- mostly the Article that is responsible for this relation. Whenever
- you add a new article, you want to connect it with existing or new
- tags. Your create Article form will probably support this notion
- and allow to specify the tags directly. This is why you should pick
- the Article as owning side, as it makes the code more
- understandable:
- .. code-block:: php
- <?php
- class Article
- {
- private $tags;
- public function addTag(Tag $tag)
- {
- $tag->addArticle($this); // synchronously updating inverse side
- $this->tags[] = $tag;
- }
- }
- class Tag
- {
- private $articles;
- public function addArticle(Article $article)
- {
- $this->articles[] = $article;
- }
- }
- This allows to group the tag adding on the ``Article`` side of the
- association:
- .. code-block:: php
- <?php
- $article = new Article();
- $article->addTag($tagA);
- $article->addTag($tagB);
- Many-To-Many, Self-referencing
- ------------------------------
- You can even have a self-referencing many-to-many association. A
- common scenario is where a ``User`` has friends and the target
- entity of that relationship is a ``User`` so it is self
- referencing. In this example it is bidirectional so ``User`` has a
- field named ``$friendsWithMe`` and ``$myFriends``.
- .. code-block:: php
- <?php
- /** @Entity **/
- class User
- {
- // ...
- /**
- * @ManyToMany(targetEntity="User", mappedBy="myFriends")
- **/
- private $friendsWithMe;
- /**
- * @ManyToMany(targetEntity="User", inversedBy="friendsWithMe")
- * @JoinTable(name="friends",
- * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
- * inverseJoinColumns={@JoinColumn(name="friend_user_id", referencedColumnName="id")}
- * )
- **/
- private $myFriends;
- public function __construct() {
- $this->friendsWithMe = new \Doctrine\Common\Collections\ArrayCollection();
- $this->myFriends = new \Doctrine\Common\Collections\ArrayCollection();
- }
- // ...
- }
- Generated MySQL Schema:
- .. code-block:: sql
- CREATE TABLE User (
- id INT AUTO_INCREMENT NOT NULL,
- PRIMARY KEY(id)
- ) ENGINE = InnoDB;
- CREATE TABLE friends (
- user_id INT NOT NULL,
- friend_user_id INT NOT NULL,
- PRIMARY KEY(user_id, friend_user_id)
- ) ENGINE = InnoDB;
- ALTER TABLE friends ADD FOREIGN KEY (user_id) REFERENCES User(id);
- ALTER TABLE friends ADD FOREIGN KEY (friend_user_id) REFERENCES User(id);
- .. _association_mapping_defaults:
- Mapping Defaults
- ----------------
- Before we introduce all the association mappings in detail, you
- should note that the @JoinColumn and @JoinTable definitions are
- usually optional and have sensible default values. The defaults for
- a join column in a one-to-one/many-to-one association is as
- follows:
- ::
- name: "<fieldname>_id"
- referencedColumnName: "id"
- As an example, consider this mapping:
- .. configuration-block::
- .. code-block:: php
- <?php
- /** @OneToOne(targetEntity="Shipping") **/
- private $shipping;
- .. code-block:: xml
- <doctrine-mapping>
- <entity class="Product">
- <one-to-one field="shipping" target-entity="Shipping" />
- </entity>
- </doctrine-mapping>
- .. code-block:: yaml
- Product:
- type: entity
- oneToOne:
- shipping:
- targetEntity: Shipping
- This is essentially the same as the following, more verbose,
- mapping:
- .. configuration-block::
- .. code-block:: php
- <?php
- /**
- * @OneToOne(targetEntity="Shipping")
- * @JoinColumn(name="shipping_id", referencedColumnName="id")
- **/
- private $shipping;
- .. code-block:: xml
- <doctrine-mapping>
- <entity class="Product">
- <one-to-one field="shipping" target-entity="Shipping">
- <join-column name="shipping_id" referenced-column-name="id" />
- </one-to-one>
- </entity>
- </doctrine-mapping>
- .. code-block:: yaml
- Product:
- type: entity
- oneToOne:
- shipping:
- targetEntity: Shipping
- joinColumn:
- name: shipping_id
- referencedColumnName: id
- The @JoinTable definition used for many-to-many mappings has
- similar defaults. As an example, consider this mapping:
- .. configuration-block::
- .. code-block:: php
- <?php
- class User
- {
- //...
- /** @ManyToMany(targetEntity="Group") **/
- private $groups;
- //...
- }
- .. code-block:: xml
- <doctrine-mapping>
- <entity class="User">
- <many-to-many field="groups" target-entity="Group" />
- </entity>
- </doctrine-mapping>
- .. code-block:: yaml
- User:
- type: entity
- manyToMany:
- groups:
- targetEntity: Group
- This is essentially the same as the following, more verbose,
- mapping:
- .. configuration-block::
- .. code-block:: php
- <?php
- class User
- {
- //...
- /**
- * @ManyToMany(targetEntity="Group")
- * @JoinTable(name="User_Group",
- * joinColumns={@JoinColumn(name="User_id", referencedColumnName="id")},
- * inverseJoinColumns={@JoinColumn(name="Group_id", referencedColumnName="id")}
- * )
- **/
- private $groups;
- //...
- }
- .. code-block:: xml
- <doctrine-mapping>
- <entity class="User">
- <many-to-many field="groups" target-entity="Group">
- <join-table name="User_Group">
- <join-columns>
- <join-column id="User_id" referenced-column-name="id" />
- </join-columns>
- <inverse-join-columns>
- <join-column id="Group_id" referenced-column-name="id" />
- </inverse-join-columns>
- </join-table>
- </many-to-many>
- </entity>
- </doctrine-mapping>
- .. code-block:: yaml
- User:
- type: entity
- manyToMany:
- groups:
- targetEntity: Group
- joinTable:
- name: User_Group
- joinColumns:
- User_id:
- referencedColumnName: id
- inverseJoinColumns:
- Group_id:
- referencedColumnName: id
- In that case, the name of the join table defaults to a combination
- of the simple, unqualified class names of the participating
- classes, separated by an underscore character. The names of the
- join columns default to the simple, unqualified class name of the
- targeted class followed by "\_id". The referencedColumnName always
- defaults to "id", just as in one-to-one or many-to-one mappings.
- If you accept these defaults, you can reduce the mapping code to a
- minimum.
- .. _collections:
- Collections
- -----------
- In all the examples of many-valued associations in this manual we
- will make use of a ``Collection`` interface and a corresponding
- default implementation ``ArrayCollection`` that are defined in the
- ``Doctrine\Common\Collections`` namespace. Why do we need that?
- Doesn't that couple my domain model to Doctrine? Unfortunately, PHP
- arrays, while being great for many things, do not make up for good
- collections of business objects, especially not in the context of
- an ORM. The reason is that plain PHP arrays can not be
- transparently extended / instrumented in PHP code, which is
- necessary for a lot of advanced ORM features. The classes /
- interfaces that come closest to an OO collection are ArrayAccess
- and ArrayObject but until instances of these types can be used in
- all places where a plain array can be used (something that may
- happen in PHP6) their usability is fairly limited. You "can"
- type-hint on ``ArrayAccess`` instead of ``Collection``, since the
- Collection interface extends ``ArrayAccess``, but this will
- severely limit you in the way you can work with the collection,
- because the ``ArrayAccess`` API is (intentionally) very primitive
- and more importantly because you can not pass this collection to
- all the useful PHP array functions, which makes it very hard to
- work with.
- .. warning::
- The Collection interface and ArrayCollection class,
- like everything else in the Doctrine namespace, are neither part of
- the ORM, nor the DBAL, it is a plain PHP class that has no outside
- dependencies apart from dependencies on PHP itself (and the SPL).
- Therefore using this class in your domain classes and elsewhere
- does not introduce a coupling to the persistence layer. The
- Collection class, like everything else in the Common namespace, is
- not part of the persistence layer. You could even copy that class
- over to your project if you want to remove Doctrine from your
- project and all your domain classes will work the same as before.
- Initializing Collections
- ------------------------
- You have to be careful when using entity fields that contain a
- collection of related entities. Say we have a User entity that
- contains a collection of groups:
- .. code-block:: php
- <?php
- /** @Entity **/
- class User
- {
- /** @ManyToMany(targetEntity="Group") **/
- private $groups;
- public function getGroups()
- {
- return $this->groups;
- }
- }
- With this code alone the ``$groups`` field only contains an
- instance of ``Doctrine\Common\Collections\Collection`` if the user
- is retrieved from Doctrine, however not after you instantiated a
- fresh instance of the User. When your user entity is still new
- ``$groups`` will obviously be null.
- This is why we recommend to initialize all collection fields to an
- empty ``ArrayCollection`` in your entities constructor:
- .. code-block:: php
- <?php
- use Doctrine\Common\Collections\ArrayCollection;
- /** @Entity **/
- class User
- {
- /** @ManyToMany(targetEntity="Group") **/
- private $groups;
- public function __construct()
- {
- $this->groups = new ArrayCollection();
- }
- public function getGroups()
- {
- return $this->groups;
- }
- }
- Now the following code will work even if the Entity hasn't
- been associated with an EntityManager yet:
- .. code-block:: php
- <?php
- $group = $entityManager->find('Group', $groupId);
- $user = new User();
- $user->getGroups()->add($group);
|