You are browsing the documentation for Symfony 3.0 which is not maintained anymore.
Consider upgrading your projects to Symfony 5.2.
How to Work with Doctrine Associations / Relations
How to Work with Doctrine Associations / Relations¶
Suppose that each product in your application belongs to exactly one category.
In this case, you’ll need a Category
class, and a way to relate a
Product
object to a Category
object.
Start by creating the Category
entity. Since you know that you’ll eventually
need to persist category objects through Doctrine, you can let Doctrine create
the class for you.
1 2 3 | $ php bin/console doctrine:generate:entity --no-interaction \
--entity="AppBundle:Category" \
--fields="name:string(255)"
|
This task generates the Category
entity for you, with an id
field,
a name
field and the associated getter and setter functions.
Relationship Mapping Metadata¶
In this example, each category can be associated with many products, while each product can be associated with only one category. This relationship can be summarized as: many products to one category (or equivalently, one category to many products).
From the perspective of the Product
entity, this is a many-to-one relationship.
From the perspective of the Category
entity, this is a one-to-many relationship.
This is important, because the relative nature of the relationship determines
which mapping metadata to use. It also determines which class must hold
a reference to the other class.
To relate the Product
and Category
entities, simply create a category
property on the Product
class, annotated as follows:
- Annotations
1 2 3 4 5 6 7 8 9 10 11 12 13
// src/AppBundle/Entity/Product.php // ... class Product { // ... /** * @ORM\ManyToOne(targetEntity="Category", inversedBy="products") * @ORM\JoinColumn(name="category_id", referencedColumnName="id") */ private $category; }
- YAML
1 2 3 4 5 6 7 8 9 10 11
# src/AppBundle/Resources/config/doctrine/Product.orm.yml AppBundle\Entity\Product: type: entity # ... manyToOne: category: targetEntity: Category inversedBy: products joinColumn: name: category_id referencedColumnName: id
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
<!-- src/AppBundle/Resources/config/doctrine/Product.orm.xml --> <?xml version="1.0" encoding="UTF-8" ?> <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> <entity name="AppBundle\Entity\Product"> <!-- ... --> <many-to-one field="category" target-entity="Category" inversed-by="products" join-column="category"> <join-column name="category_id" referenced-column-name="id" /> </many-to-one> </entity> </doctrine-mapping>
This many-to-one mapping is critical. It tells Doctrine to use the category_id
column on the product
table to relate each record in that table with
a record in the category
table.
Next, since a single Category
object will relate to many Product
objects, a products
property can be added to the Category
class
to hold those associated objects.
- Annotations
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// src/AppBundle/Entity/Category.php // ... use Doctrine\Common\Collections\ArrayCollection; class Category { // ... /** * @ORM\OneToMany(targetEntity="Product", mappedBy="category") */ private $products; public function __construct() { $this->products = new ArrayCollection(); } }
- YAML
1 2 3 4 5 6 7 8 9 10
# src/AppBundle/Resources/config/doctrine/Category.orm.yml AppBundle\Entity\Category: type: entity # ... oneToMany: products: targetEntity: Product mappedBy: category # Don't forget to initialize the collection in # the __construct() method of the entity
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
<!-- src/AppBundle/Resources/config/doctrine/Category.orm.xml --> <?xml version="1.0" encoding="UTF-8" ?> <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> <entity name="AppBundle\Entity\Category"> <!-- ... --> <one-to-many field="products" target-entity="Product" mapped-by="category" /> <!-- don't forget to init the collection in the __construct() method of the entity --> </entity> </doctrine-mapping>
While the many-to-one mapping shown earlier was mandatory, this one-to-many
mapping is optional. It is included here to help demonstrate Doctrine’s range
of relationship management capabailties. Plus, in the context of this application,
it will likely be convenient for each Category
object to automatically
own a collection of its related Product
objects.
Note
The code in the constructor is important. Rather than being instantiated
as a traditional array
, the $products
property must be of a type
that implements Doctrine’s Collection
interface. In this case, an
ArrayCollection
object is used. This object looks and acts almost
exactly like an array, but has some added flexibility. If this makes
you uncomfortable, don’t worry. Just imagine that it’s an array
and you’ll be in good shape.
See also
To understand inversedBy
and mappedBy
usage, see Doctrine’s
Association Updates documentation.
Tip
The targetEntity value in the metadata used above can reference any entity with a valid namespace, not just entities defined in the same namespace. To relate to an entity defined in a different class or bundle, enter a full namespace as the targetEntity.
Now that you’ve added new properties to both the Product
and Category
classes, tell Doctrine to generate the missing getter and setter methods for you:
1 | $ php bin/console doctrine:generate:entities AppBundle
|
Ignore the Doctrine metadata for a moment. You now have two classes - Product
and Category
, with a natural many-to-one relationship. The Product
class holds a single Category
object, and the Category
class holds
a collection of Product
objects. In other words, you’ve built your classes
in a way that makes sense for your application. The fact that the data needs
to be persisted to a database is always secondary.
Now, review the metadata above the Product
entity’s $category
property.
It tells Doctrine that the related class is Category
, and that the id
of the related category record should be stored in a category_id
field
on the product
table.
In other words, the related Category
object will be stored in the
$category
property, but behind the scenes, Doctrine will persist this
relationship by storing the category’s id in the category_id
column
of the product
table.

The metadata above the Category
entity’s $products
property is less
complicated. It simply tells Doctrine to look at the Product.category
property to figure out how the relationship is mapped.
Before you continue, be sure to tell Doctrine to add the new category
table, the new product.category_id
column, and the new foreign key:
1 | $ php bin/console doctrine:schema:update --force
|
More Information on Associations¶
This section has been an introduction to one common type of entity relationship, the one-to-many relationship. For more advanced details and examples of how to use other types of relations (e.g. one-to-one, many-to-many), see Doctrine’s Association Mapping Documentation.
Note
If you’re using annotations, you’ll need to prepend all annotations with
ORM\
(e.g. ORM\OneToMany
), which is not reflected in Doctrine’s
documentation. You’ll also need to include the use Doctrine\ORM\Mapping as ORM;
statement, which imports the ORM
annotations prefix.
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.