Skip to content

Commit 2480bf2

Browse files
committed
minor #6455 Editing the Doctrine section to improve accuracy and readability (natechicago)
This PR was squashed before being merged into the 2.3 branch (closes #6455). Discussion ---------- Editing the Doctrine section to improve accuracy and readability The changes here add some important details to "Relationship Mapping Metadata" section. I've also reorganized it a little bit. In this section, beginners are led through the process of associating products and categories. These entities are related in a many-to-one relationship. However, the documentation previously introduced the somewhat-confusing and *optional* information (the OneToMany mapping of a collection of product objects onto the category class) prior to introducing the more straight-forward and *mandatory* information (the ManyToOne mapping of a category object onto the product class). The changes in this PR introduce the ManyToOne mapping before the OneToMany mapping, so that the overall flow of information is from the most important to the less important. The changes also make it explicitly clear that the OneToMany mapping is optional. | Q | A | ------------- | --- | Doc fix? | yes | New docs? | no | Applies to | 2.3 | Fixed tickets | n/a Commits ------- 1e06da5 Editing the Doctrine section to improve accuracy and readability
2 parents b4d1839 + 1e06da5 commit 2480bf2

File tree

1 file changed

+140
-118
lines changed

1 file changed

+140
-118
lines changed

book/doctrine.rst

+140-118
Original file line numberDiff line numberDiff line change
@@ -721,31 +721,31 @@ Doctrine Query Language (DQL). DQL is similar to SQL except that you should
721721
imagine that you're querying for one or more objects of an entity class (e.g. ``Product``)
722722
instead of querying for rows on a table (e.g. ``product``).
723723

724-
When querying in Doctrine, you have two options: writing pure Doctrine queries
724+
When querying in Doctrine, you have two main options: writing pure DQL queries
725725
or using Doctrine's Query Builder.
726726

727727
Querying for Objects with DQL
728728
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
729729

730-
Imagine that you want to query for products, but only return products that
731-
cost more than ``19.99``, ordered from cheapest to most expensive. You can use
732-
Doctrine's native SQL-like language called DQL to make a query for this::
730+
Imagine that you want to query for products that cost more than ``19.99``,
731+
ordered from least to most expensive. You can use DQL, Doctrine's native
732+
SQL-like language, to construct a query for this scenario::
733733

734734
$em = $this->getDoctrine()->getManager();
735735
$query = $em->createQuery(
736736
'SELECT p
737737
FROM AppBundle:Product p
738738
WHERE p.price > :price
739739
ORDER BY p.price ASC'
740-
)->setParameter('price', '19.99');
740+
)->setParameter('price', 19.99);
741741

742742
$products = $query->getResult();
743743

744744
If you're comfortable with SQL, then DQL should feel very natural. The biggest
745-
difference is that you need to think in terms of "objects" instead of rows
746-
in a database. For this reason, you select *from* the ``AppBundle:Product``
747-
*object* (an optional shortcut for ``AppBundle\Entity\Product``) and then
748-
alias it as ``p``.
745+
difference is that you need to think in terms of selecting PHP objects,
746+
instead of rows in a database. For this reason, you select *from* the
747+
``AppBundle:Product`` *entity* (an optional shortcut for the
748+
``AppBundle\Entity\Product`` class) and then alias it as ``p``.
749749

750750
.. tip::
751751

@@ -799,11 +799,11 @@ Custom Repository Classes
799799
~~~~~~~~~~~~~~~~~~~~~~~~~
800800

801801
In the previous sections, you began constructing and using more complex queries
802-
from inside a controller. In order to isolate, test and reuse these queries,
803-
it's a good practice to create a custom repository class for your entity and
804-
add methods with your query logic there.
802+
from inside a controller. In order to isolate, reuse and test these queries,
803+
it's a good practice to create a custom repository class for your entity.
804+
Methods containing your query logic can then be stored in this class.
805805

806-
To do this, add the name of the repository class to your mapping definition:
806+
To do this, add the repository class name to your entity's mapping definition:
807807

808808
.. configuration-block::
809809

@@ -847,16 +847,22 @@ To do this, add the name of the repository class to your mapping definition:
847847
</entity>
848848
</doctrine-mapping>
849849
850-
Doctrine can generate the repository class for you by running the same command
851-
used earlier to generate the missing getter and setter methods:
850+
Doctrine can generate empty repository classes for all the entities in your
851+
application via the same command used earlier to generate the missing getter
852+
and setter methods:
852853

853854
.. code-block:: bash
854855
855856
$ php app/console doctrine:generate:entities AppBundle
856857
857-
Next, add a new method - ``findAllOrderedByName()`` - to the newly generated
858-
repository class. This method will query for all the ``Product`` entities,
859-
ordered alphabetically.
858+
.. tip::
859+
860+
If you opt to create the repository classes yourself, they must extend
861+
``Doctrine\ORM\EntityRepository``.
862+
863+
Next, add a new method - ``findAllOrderedByName()`` - to the newly-generated
864+
``ProductRepository`` class. This method will query for all the ``Product``
865+
entities, ordered alphabetically by name.
860866

861867
.. code-block:: php
862868
@@ -898,11 +904,13 @@ You can use this new method just like the default finder methods of the reposito
898904
Entity Relationships/Associations
899905
---------------------------------
900906

901-
Suppose that the products in your application all belong to exactly one "category".
902-
In this case, you'll need a ``Category`` object and a way to relate a ``Product``
903-
object to a ``Category`` object. Start by creating the ``Category`` entity.
904-
Since you know that you'll eventually need to persist the class through Doctrine,
905-
you can let Doctrine create the class for you.
907+
Suppose that each product in your application belongs to exactly one category.
908+
In this case, you'll need a ``Category`` class, and a way to relate a
909+
``Product`` object to a ``Category`` object.
910+
911+
Start by creating the ``Category`` entity. Since you know that you'll eventually
912+
need to persist category objects through Doctrine, you can let Doctrine create
913+
the class for you.
906914

907915
.. code-block:: bash
908916
@@ -916,8 +924,81 @@ a ``name`` field and the associated getter and setter functions.
916924
Relationship Mapping Metadata
917925
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
918926

919-
To relate the ``Category`` and ``Product`` entities, start by creating a
920-
``products`` property on the ``Category`` class:
927+
In this example, each category can be associated with *many* products, while
928+
each product can be associated with only *one* category. This relationship
929+
can be summarized as: *many* products to *one* category (or equivalently,
930+
*one* category to *many* products).
931+
932+
From the perspective of the ``Product`` entity, this is a many-to-one relationship.
933+
From the perspective of the ``Category`` entity, this is a one-to-many relationship.
934+
This is important, because the relative nature of the relationship determines
935+
which mapping metadata to use. It also determines which class *must* hold
936+
a reference to the other class.
937+
938+
To relate the ``Product`` and ``Category`` entities, simply create a ``category``
939+
property on the ``Product`` class, annotated as follows:
940+
941+
.. configuration-block::
942+
943+
.. code-block:: php-annotations
944+
945+
// src/AppBundle/Entity/Product.php
946+
947+
// ...
948+
class Product
949+
{
950+
// ...
951+
952+
/**
953+
* @ORM\ManyToOne(targetEntity="Category", inversedBy="products")
954+
* @ORM\JoinColumn(name="category_id", referencedColumnName="id")
955+
*/
956+
private $category;
957+
}
958+
959+
.. code-block:: yaml
960+
961+
# src/AppBundle/Resources/config/doctrine/Product.orm.yml
962+
AppBundle\Entity\Product:
963+
type: entity
964+
# ...
965+
manyToOne:
966+
category:
967+
targetEntity: Category
968+
inversedBy: products
969+
joinColumn:
970+
name: category_id
971+
referencedColumnName: id
972+
973+
.. code-block:: xml
974+
975+
<!-- src/AppBundle/Resources/config/doctrine/Product.orm.xml -->
976+
<?xml version="1.0" encoding="UTF-8" ?>
977+
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
978+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
979+
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
980+
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
981+
982+
<entity name="AppBundle\Entity\Product">
983+
<!-- ... -->
984+
<many-to-one
985+
field="category"
986+
target-entity="Category"
987+
inversed-by="products"
988+
join-column="category">
989+
990+
<join-column name="category_id" referenced-column-name="id" />
991+
</many-to-one>
992+
</entity>
993+
</doctrine-mapping>
994+
995+
This many-to-one mapping is critical. It tells Doctrine to use the ``category_id``
996+
column on the ``product`` table to relate each record in that table with
997+
a record in the ``category`` table.
998+
999+
Next, since a single ``Category`` object will relate to many ``Product``
1000+
objects, a ``products`` property can be added to the ``Category`` class
1001+
to hold those associated objects.
9211002

9221003
.. configuration-block::
9231004

@@ -979,126 +1060,67 @@ To relate the ``Category`` and ``Product`` entities, start by creating a
9791060
</entity>
9801061
</doctrine-mapping>
9811062
982-
First, since a ``Category`` object will relate to many ``Product`` objects,
983-
a ``products`` array property is added to hold those ``Product`` objects.
984-
Again, this isn't done because Doctrine needs it, but instead because it
985-
makes sense in the application for each ``Category`` to hold an array of
986-
``Product`` objects.
1063+
While the many-to-one mapping shown earlier was mandatory, this one-to-many
1064+
mapping is optional. It is included here to help demonstrate Doctrine's range
1065+
of relationship management capabailties. Plus, in the context of this application,
1066+
it will likely be convenient for each ``Category`` object to automatically
1067+
own a collection of its related ``Product`` objects.
9871068

9881069
.. note::
9891070

990-
The code in the ``__construct()`` method is important because Doctrine
991-
requires the ``$products`` property to be an ``ArrayCollection`` object.
992-
This object looks and acts almost *exactly* like an array, but has some
993-
added flexibility. If this makes you uncomfortable, don't worry. Just
994-
imagine that it's an ``array`` and you'll be in good shape.
1071+
The code in the constructor is important. Rather than being instantiated
1072+
as a traditional ``array``, the ``$products`` property must be of a type
1073+
that implements Doctrine's ``Collection`` interface. In this case, an
1074+
``ArrayCollection`` object is used. This object looks and acts almost
1075+
*exactly* like an array, but has some added flexibility. If this makes
1076+
you uncomfortable, don't worry. Just imagine that it's an ``array``
1077+
and you'll be in good shape.
9951078

9961079
.. tip::
9971080

998-
The targetEntity value in the decorator used above can reference any entity
1081+
The targetEntity value in the metadata used above can reference any entity
9991082
with a valid namespace, not just entities defined in the same namespace. To
10001083
relate to an entity defined in a different class or bundle, enter a full
10011084
namespace as the targetEntity.
10021085

1003-
Next, since each ``Product`` class can relate to exactly one ``Category``
1004-
object, you'll want to add a ``$category`` property to the ``Product`` class:
1005-
1006-
.. configuration-block::
1007-
1008-
.. code-block:: php-annotations
1009-
1010-
// src/AppBundle/Entity/Product.php
1011-
1012-
// ...
1013-
class Product
1014-
{
1015-
// ...
1016-
1017-
/**
1018-
* @ORM\ManyToOne(targetEntity="Category", inversedBy="products")
1019-
* @ORM\JoinColumn(name="category_id", referencedColumnName="id")
1020-
*/
1021-
private $category;
1022-
}
1023-
1024-
.. code-block:: yaml
1025-
1026-
# src/AppBundle/Resources/config/doctrine/Product.orm.yml
1027-
AppBundle\Entity\Product:
1028-
type: entity
1029-
# ...
1030-
manyToOne:
1031-
category:
1032-
targetEntity: Category
1033-
inversedBy: products
1034-
joinColumn:
1035-
name: category_id
1036-
referencedColumnName: id
1037-
1038-
.. code-block:: xml
1039-
1040-
<!-- src/AppBundle/Resources/config/doctrine/Product.orm.xml -->
1041-
<?xml version="1.0" encoding="UTF-8" ?>
1042-
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
1043-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1044-
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
1045-
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
1046-
1047-
<entity name="AppBundle\Entity\Product">
1048-
<!-- ... -->
1049-
<many-to-one
1050-
field="category"
1051-
target-entity="Category"
1052-
inversed-by="products"
1053-
join-column="category">
1054-
1055-
<join-column name="category_id" referenced-column-name="id" />
1056-
</many-to-one>
1057-
</entity>
1058-
</doctrine-mapping>
1059-
1060-
Finally, now that you've added a new property to both the ``Category`` and
1061-
``Product`` classes, tell Doctrine to generate the missing getter and setter
1062-
methods for you:
1086+
Now that you've added new properties to both the ``Product`` and ``Category``
1087+
classes, tell Doctrine to generate the missing getter and setter methods for you:
10631088

10641089
.. code-block:: bash
10651090
10661091
$ php app/console doctrine:generate:entities AppBundle
10671092
1068-
Ignore the Doctrine metadata for a moment. You now have two classes - ``Category``
1069-
and ``Product`` with a natural one-to-many relationship. The ``Category``
1070-
class holds an array of ``Product`` objects and the ``Product`` object can
1071-
hold one ``Category`` object. In other words - you've built your classes
1072-
in a way that makes sense for your needs. The fact that the data needs to
1073-
be persisted to a database is always secondary.
1074-
1075-
Now, look at the metadata above the ``$category`` property on the ``Product``
1076-
class. The information here tells Doctrine that the related class is ``Category``
1077-
and that it should store the ``id`` of the category record on a ``category_id``
1078-
field that lives on the ``product`` table. In other words, the related ``Category``
1079-
object will be stored on the ``$category`` property, but behind the scenes,
1080-
Doctrine will persist this relationship by storing the category's id value
1081-
on a ``category_id`` column of the ``product`` table.
1093+
Ignore the Doctrine metadata for a moment. You now have two classes - ``Product``
1094+
and ``Category``, with a natural many-to-one relationship. The ``Product``
1095+
class holds a *single* ``Category`` object, and the ``Category`` class holds
1096+
a *collection* of ``Product`` objects. In other words, you've built your classes
1097+
in a way that makes sense for your application. The fact that the data needs
1098+
to be persisted to a database is always secondary.
1099+
1100+
Now, review the metadata above the ``Product`` entity's ``$category`` property.
1101+
It tells Doctrine that the related class is ``Category``, and that the ``id``
1102+
of the related category record should be stored in a ``category_id`` field
1103+
on the ``product`` table.
1104+
1105+
In other words, the related ``Category`` object will be stored in the
1106+
``$category`` property, but behind the scenes, Doctrine will persist this
1107+
relationship by storing the category's id in the ``category_id`` column
1108+
of the ``product`` table.
10821109

10831110
.. image:: /images/book/doctrine_image_2.png
10841111
:align: center
10851112

1086-
The metadata above the ``$products`` property of the ``Category`` object
1087-
is less important, and simply tells Doctrine to look at the ``Product.category``
1113+
The metadata above the ``Category`` entity's ``$products`` property is less
1114+
complicated. It simply tells Doctrine to look at the ``Product.category``
10881115
property to figure out how the relationship is mapped.
10891116

10901117
Before you continue, be sure to tell Doctrine to add the new ``category``
1091-
table, and ``product.category_id`` column, and new foreign key:
1118+
table, the new ``product.category_id`` column, and the new foreign key:
10921119

10931120
.. code-block:: bash
10941121
10951122
$ php app/console doctrine:schema:update --force
10961123
1097-
.. note::
1098-
1099-
This command should only be used during development. For a more robust
1100-
method of systematically updating your production database, read about
1101-
`migrations`_.
11021124
11031125
Saving Related Entities
11041126
~~~~~~~~~~~~~~~~~~~~~~~

0 commit comments

Comments
 (0)