44How to Work with Doctrine Associations / Relations
55==================================================
66
7+ There are **two ** main relationship/association types:
8+
9+ ``ManyToOne `` / ``OneToMany ``
10+ The most common relationship, mapped in the database with a simple foreign
11+ key column (e.g. a ``category_id `` column on the ``product `` table). This is
12+ actually just *one * association type, but seen from the two different *sides *
13+ of the relation.
14+
15+ ``ManyToMany ``
16+ Uses a join table and is needed when both sides of the relationship can have
17+ many of the other side (e.g. "students" and "classes": each student is in many
18+ classes, and each class has many students).
19+
20+ First, you need to determine which relationship to use. If both sides of the relation
21+ will contain many of the oter side (e.g. "students" and "classes"), you need a
22+ ``ManyToMany `` relation. Otherwise, you likely need a ``ManyToOne ``.
23+
24+ ..tip ::
25+
26+ There is also a OneToOne relationship (e.g. one User has one Profile and vice
27+ versa). In practice, using this is similar to ``ManyToOne ``.
28+
29+ The ManyToOne / OneToMany Association
30+ -------------------------------------
31+
732Suppose that each product in your application belongs to exactly one category.
833In this case, you'll need a ``Category `` class, and a way to relate a
934``Product `` object to a ``Category `` object.
1035
11- Start by creating the ``Category `` entity. Since you know that you'll eventually
12- need to persist category objects through Doctrine, you can let Doctrine create
13- the class for you.
36+ Start by creating a ``Category `` entity:
1437
1538..code-block ::terminal
1639
17- $ php bin/console doctrine:generate:entity --no-interaction \
18- --entity="App:Category" \
19- --fields="name:string(255)"
40+ $ php bin/console make:entity Category
41+
42+ Then, add a ``name `` field to that new ``Category `` class::
43+
44+ // src/Entity/Category
45+ // ...
2046
21- This command generates the ``Category `` entity for you, with an ``id `` field,
22- a ``name `` field and the associated getter and setter functions.
47+ class Category
48+ {
49+ /**
50+ * @ORM\Id
51+ * @ORM\GeneratedValue
52+ * @ORM\Column(type="integer")
53+ */
54+ private $id;
55+
56+ /**
57+ * @ORM\Column(type="string")
58+ */
59+ private $name;
60+
61+ // ... getters and setters
62+ }
2363
24- Relationship MappingMetadata
25- -----------------------------
64+ Mappingthe ManyToOne Relationship
65+ ----------------------------------
2666
27- In this example, each category can be associated with *many * products, while
67+ In this example, each category can be associated with *many * products. But,
2868each product can be associated with only *one * category. This relationship
2969can be summarized as: *many * products to *one * category (or equivalently,
3070*one * category to *many * products).
3171
3272From the perspective of the ``Product `` entity, this is a many-to-one relationship.
3373From the perspective of the ``Category `` entity, this is a one-to-many relationship.
34- This is important, because the relative nature of the relationship determines
35- which mapping metadata to use. It also determines which class *must * hold
36- a reference to the other class.
3774
38- Torelate the `` Product `` and `` Category ``entities, simply create a `` category ``
39- property on the ``Product ``class, annotated as follows :
75+ Tomap this, first create a `` category ``property on the `` Product `` class with
76+ the ``ManyToOne ``annotation :
4077
4178..configuration-block ::
4279
@@ -50,10 +87,20 @@ property on the ``Product`` class, annotated as follows:
5087 // ...
5188
5289 /**
53- * @ORM\ManyToOne(targetEntity="Category", inversedBy="products")
54- * @ORM\JoinColumn(name="category_id", referencedColumnName="id" )
90+ * @ORM\ManyToOne(targetEntity="App\Entity\ Category", inversedBy="products")
91+ * @ORM\JoinColumn(nullable=true )
5592 */
5693 private $category;
94+
95+ public function getCategory(): Category
96+ {
97+ return $this->category;
98+ }
99+
100+ public function setCategory(Category $category)
101+ {
102+ $this->category = $category;
103+ }
57104 }
58105
59106 ..code-block ::yaml
@@ -64,11 +111,10 @@ property on the ``Product`` class, annotated as follows:
64111# ...
65112manyToOne :
66113category :
67- targetEntity :Category
114+ targetEntity :App\Entity\ Category
68115inversedBy :products
69116joinColumn :
70- name :category_id
71- referencedColumnName :id
117+ nullable :true
72118
73119 ..code-block ::xml
74120
@@ -83,22 +129,19 @@ property on the ``Product`` class, annotated as follows:
83129<!-- ...-->
84130 <many-to-one
85131field =" category"
86- target-entity =" Category"
87- inversed-by =" products"
88- join-column =" category" >
89-
90- <join-column name =" category_id" referenced-column-name =" id" />
132+ target-entity =" App\Entity\Category"
133+ inversed-by =" products" >
134+ <join-column nullable =" true" />
91135 </many-to-one >
92136 </entity >
93137 </doctrine-mapping >
94138
95- This many-to-one mapping iscritical . It tells Doctrine to use the ``category_id ``
139+ This many-to-one mapping isrequired . It tells Doctrine to use the ``category_id ``
96140column on the ``product `` table to relate each record in that table with
97141a record in the ``category `` table.
98142
99- Next, since a single ``Category `` object will relate to many ``Product ``
100- objects, a ``products `` property can be added to the ``Category `` class
101- to hold those associated objects.
143+ Next, since a *one * ``Category `` object will relate to *many * ``Product ``
144+ objects, add a ``products `` property to ``Category `` that will hold those objects:
102145
103146..configuration-block ::
104147
@@ -108,20 +151,29 @@ to hold those associated objects.
108151
109152 // ...
110153 use Doctrine\Common\Collections\ArrayCollection;
154+ use Doctrine\Common\Collections\Collection;
111155
112156 class Category
113157 {
114158 // ...
115159
116160 /**
117- * @ORM\OneToMany(targetEntity="Product", mappedBy="category")
161+ * @ORM\OneToMany(targetEntity="App\Entity\ Product", mappedBy="category")
118162 */
119163 private $products;
120164
121165 public function __construct()
122166 {
123167 $this->products = new ArrayCollection();
124168 }
169+
170+ /**
171+ * @return Collection|Product[]
172+ */
173+ public function getProducts()
174+ {
175+ return $this->products;
176+ }
125177 }
126178
127179 ..code-block ::yaml
@@ -132,7 +184,7 @@ to hold those associated objects.
132184# ...
133185oneToMany :
134186products :
135- targetEntity :Product
187+ targetEntity :App\Entity\ Product
136188mappedBy :category
137189# Don't forget to initialize the collection in
138190# the __construct() method of the entity
@@ -150,7 +202,7 @@ to hold those associated objects.
150202<!-- ...-->
151203 <one-to-many
152204field =" products"
153- target-entity =" Product"
205+ target-entity =" App\Entity\ Product"
154206mapped-by =" category" />
155207
156208<!--
@@ -160,68 +212,29 @@ to hold those associated objects.
160212 </entity >
161213 </doctrine-mapping >
162214
163- While the many-to-one mapping shown earlierwas mandatory, thisone-to-many
164- mapping is optional. It is included here tohelp demonstrate Doctrine's range
165- of relationship management capabilities. Plus, in the context of this application,
166- it will likely be convenient for each `` Category `` object to automatically
167- own a collection of its related `` Product ``objects .
215+ The `` ManyToOne `` mapping shown earlieris * required *, But, this`` OneToMany ``
216+ is optional: only add it * if * you want tobe able to access the products that are
217+ related to a category. In this example, it * will * be useful to be able to call
218+ `` $category->getProducts() ``. If you don't want it, then you also don't need the
219+ `` inversedBy `` or `` mappedBy ``config .
168220
169- ..note ::
170-
171- The code in the constructor is important. Rather than being instantiated
172- as a traditional ``array ``, the ``$products `` property must be of a type
173- that implements Doctrine's ``Collection `` interface. In this case, an
174- ``ArrayCollection `` object is used. This object looks and acts almost
175- *exactly * like an array, but has some added flexibility. If this makes
176- you uncomfortable, don't worry. Just imagine that it's an ``array ``
177- and you'll be in good shape.
178-
179- ..seealso ::
180-
181- To understand ``inversedBy `` and ``mappedBy `` usage, see Doctrine's
182- `Association Updates `_ documentation.
183-
184- ..tip ::
185-
186- The targetEntity value in the metadata used above can reference any entity
187- with a valid namespace, not just entities defined in the same namespace. To
188- relate to an entity defined in a different class or bundle, enter a full
189- namespace as the targetEntity.
190-
191- Now that you've added new properties to both the ``Product `` and ``Category ``
192- classes, you must generate the missing getter and setter methods manually or
193- using your own IDE.
194-
195- Ignore the Doctrine metadata for a moment. You now have two classes - ``Product ``
196- and ``Category ``, with a natural many-to-one relationship. The ``Product ``
197- class holds a *single * ``Category `` object, and the ``Category `` class holds
198- a *collection * of ``Product `` objects. In other words, you've built your classes
199- in a way that makes sense for your application. The fact that the data needs
200- to be persisted to a database is always secondary.
201-
202- Now, review the metadata above the ``Product `` entity's ``$category `` property.
203- It tells Doctrine that the related class is ``Category ``, and that the ``id ``
204- of the related category record should be stored in a ``category_id `` field
205- on the ``product `` table.
206-
207- In other words, the related ``Category `` object will be stored in the
208- ``$category `` property, but behind the scenes, Doctrine will persist this
209- relationship by storing the category's id in the ``category_id `` column
210- of the ``product `` table.
211-
212- ..image ::/_images/doctrine/mapping_relations.png
213- :align: center
221+ ..sidebar ::What is the ArrayCollection Stuff?
214222
215- The metadata above the ``Category `` entity's ``$products `` property is less
216- complicated. It simply tells Doctrine to look at the ``Product.category ``
217- property to figure out how the relationship is mapped.
223+ The code inside ``__construct() `` is important: The ``$products `` property must
224+ be a collection object that implements Doctrine's ``Collection `` interface.
225+ In this case, an ``ArrayCollection `` object is used. This looks and acts almost
226+ *exactly * like an array, but has some added flexibility. Just imagine that it's
227+ an ``array `` and you'll be in good shape.
218228
219- Before you continue, be sure to tell Doctrine to add the new ``category ``
220- table, the new ``product.category_id `` column, and the new foreign key:
229+ Your database is setup! Now, execute the migrations like normal:
221230
222231..code-block ::terminal
223232
224- $ php bin/console doctrine:schema:update --force
233+ $ php bin/console doctrine:migrations:diff
234+ $ php bin/console doctrine:migrations:migrate
235+
236+ Thanks to the relationship, this creates a ``category_id `` foreign key column on
237+ the ``product `` table. Doctrine is ready to persist our relationship!
225238
226239Saving Related Entities
227240-----------------------
@@ -234,9 +247,12 @@ Now you can see this new code in action! Imagine you're inside a controller::
234247 use App\Entity\Product;
235248 use Symfony\Component\HttpFoundation\Response;
236249
237- classDefaultController extends Controller
250+ classProductController extends Controller
238251 {
239- public function createProductAction()
252+ /**
253+ * @Route("/product", name="product")
254+ */
255+ public function index()
240256 {
241257 $category = new Category();
242258 $category->setName('Computer Peripherals');
@@ -261,10 +277,18 @@ Now you can see this new code in action! Imagine you're inside a controller::
261277 }
262278 }
263279
264- Now, a single row is added to both the ``category `` and ``product `` tables.
265- The ``product.category_id `` column for the new product is set to whatever
266- the ``id `` is of the new category. Doctrine manages the persistence of this
267- relationship for you.
280+ When you go to ``/product ``, a single row is added to both the ``category `` and
281+ ``product `` tables. The ``product.category_id `` column for the new product is set
282+ to whatever the ``id `` is of the new category. Doctrine manages the persistence of this
283+ relationship for you:
284+
285+ ..image ::/_images/doctrine/mapping_relations.png
286+ :align: center
287+
288+ If you're new to an ORM, this is the *hardest * concept: you need to stop thinking
289+ about your database, and instead *only * think about your objects. Instead of setting
290+ the category's integer id onto ``Product ``, you set the entire ``Category `` *object *.
291+ Doctrine takes care of the rest when saving.
268292
269293Fetching Related Objects
270294------------------------
@@ -276,11 +300,13 @@ did before. First, fetch a ``$product`` object and then access its related
276300 use App\Entity\Product;
277301 // ...
278302
279- public function showAction($productId )
303+ public function showAction($id )
280304 {
281305 $product = $this->getDoctrine()
282306 ->getRepository(Product::class)
283- ->find($productId);
307+ ->find($id);
308+
309+ // ...
284310
285311 $categoryName = $product->getCategory()->getName();
286312
@@ -289,7 +315,7 @@ did before. First, fetch a ``$product`` object and then access its related
289315
290316In this example, you first query for a ``Product `` object based on the product's
291317``id ``. This issues a query for *just * the product data and hydrates the
292- ``$product `` object with that data . Later, when you call ``$product->getCategory()->getName() ``,
318+ ``$product ``. Later, when you call ``$product->getCategory()->getName() ``,
293319Doctrine silently makes a second query to find the ``Category `` that's related
294320to this ``Product ``. It prepares the ``$category `` object and returns it to
295321you.
@@ -301,7 +327,8 @@ What's important is the fact that you have easy access to the product's related
301327category, but the category data isn't actually retrieved until you ask for
302328the category (i.e. it's "lazily loaded").
303329
304- You can also query in the other direction::
330+ Because we mapped the optiona ``OneToMany `` side, you can also query in the other
331+ direction::
305332
306333 public function showProductsAction($categoryId)
307334 {
@@ -314,6 +341,8 @@ You can also query in the other direction::
314341 // ...
315342 }
316343
344+ TODO TODO, STARTING HERE!!!!!!!!!!!!!!!!!!
345+
317346In this case, the same things occur: you first query out for a single ``Category ``
318347object, and then Doctrine makes a second query to retrieve the related ``Product ``
319348objects, but only once/if you ask for them (i.e. when you call ``getProducts() ``).
@@ -412,4 +441,3 @@ Doctrine's `Association Mapping Documentation`_.
412441 statement, which *imports * the ``ORM `` annotations prefix.
413442
414443.. _`Association Mapping Documentation` :http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html
415- .. _`Association Updates` :http://docs.doctrine-project.org/en/latest/reference/unitofwork-associations.html