8000 Editing the Doctrine section to improve accuracy and readability · symfony/symfony-docs@1e06da5 · GitHub
[go: up one dir, main page]

Skip to content

Commit 1e06da5

Browse files
natechicagoxabbuh
authored andcommitted
Editing the Doctrine section to improve accuracy and readability
1 parent 751aa45 commit 1e06da5

File tree

1 file changed

+140
-118
lines changed

1 file changed

+140
-118
lines changed

book/doctrine.rst

Lines changed: 140 additions & 118 deletions
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< 10000 code>-
*/
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)
0