1
0
mirror of synced 2025-01-31 20:41:44 +03:00

Cleaned up documentation

Cleaned up documentation, tweaked some of the grammar, changed class#method() references to class::method(), added warning about performance impact of lazy loading
This commit is contained in:
Christian Morgan 2013-07-26 16:16:24 +01:00
parent d7881a1ec2
commit 7bc18d7888

View File

@ -66,17 +66,17 @@ Bug Tracker domain model from the
documentation. Reading their documentation we can extract the documentation. Reading their documentation we can extract the
requirements: requirements:
- A Bugs has a description, creation date, status, reporter and - A Bug has a description, creation date, status, reporter and
engineer engineer
- A bug can occur on different products (platforms) - A Bug can occur on different Products (platforms)
- Products have a name. - A Product has a name.
- Bug Reporter and Engineers are both Users of the System. - Bug reporters and engineers are both Users of the system.
- A user can create new bugs. - A User can create new Bugs.
- The assigned engineer can close a bug. - The assigned engineer can close a Bug.
- A user can see all his reported or assigned bugs. - A User can see all his reported or assigned Bugs.
- Bugs can be paginated through a list-view. - Bugs can be paginated through a list-view.
Setup Project Project Setup
------------- -------------
Create a new empty folder for this tutorial project, for example Create a new empty folder for this tutorial project, for example
@ -171,9 +171,9 @@ factory method.
Generating the Database Schema Generating the Database Schema
------------------------------ ------------------------------
Now that we have defined the Metadata Mappings and bootstrapped the Now that we have defined the Metadata mappings and bootstrapped the
EntityManager we want to generate the relational database schema EntityManager we want to generate the relational database schema
from it. Doctrine has a Command-Line-Interface that allows you to from it. Doctrine has a Command-Line Interface that allows you to
access the SchemaTool, a component that generates the required access the SchemaTool, a component that generates the required
tables to work with the metadata. tables to work with the metadata.
@ -220,9 +220,8 @@ its not available in SQLite since it does not support ALTER TABLE.
Starting with the Product Starting with the Product
------------------------- -------------------------
We start with the Product entity requirements, because it is the most simple one We start with the simplest entity, the Product. Create a ``src/Product.php`` file to contain the ``Product``
to get started. Create a ``src/Product.php`` file and put the ``Product`` entity definition:
entity definition in there:
.. code-block:: php .. code-block:: php
@ -323,14 +322,14 @@ References in the text will be made to the XML mapping.
The top-level ``entity`` definition tag specifies information about The top-level ``entity`` definition tag specifies information about
the class and table-name. The primitive type ``Product::$name`` is the class and table-name. The primitive type ``Product::$name`` is
defined as ``field`` attributes. The Id property is defined with defined as a ``field`` attribute. The ``id`` property is defined with
the ``id`` tag. The id has a ``generator`` tag nested inside which the ``id`` tag, this has a ``generator`` tag nested inside which
defines that the primary key generation mechanism automatically defines that the primary key generation mechanism automatically
uses the database platforms native id generation strategy, for uses the database platforms native id generation strategy (for
example AUTO INCREMENT in the case of MySql or Sequences in the example AUTO INCREMENT in the case of MySql or Sequences in the
case of PostgreSql and Oracle. case of PostgreSql and Oracle).
You have to update the database now, because we have a first Entity now: Now that we have defined our first entity, lets update the database:
:: ::
@ -357,7 +356,7 @@ Now create a new script that will insert products into the database:
echo "Created Product with ID " . $product->getId() . "\n"; echo "Created Product with ID " . $product->getId() . "\n";
Call this script from the command line to see how new products are created: Call this script from the command-line to see how new products are created:
:: ::
@ -379,7 +378,7 @@ Doctrine follows the UnitOfWork pattern which additionally detects all entities
that were fetched and have changed during the request. You don't have to keep track of that were fetched and have changed during the request. You don't have to keep track of
entities yourself, when Doctrine already knowns about them. entities yourself, when Doctrine already knowns about them.
As a next step we want to fetch a list of all the products. Let's create a As a next step we want to fetch a list of all the Products. Let's create a
new script for this: new script for this:
.. code-block:: php .. code-block:: php
@ -395,8 +394,8 @@ new script for this:
echo sprintf("-%s\n", $product->getName()); echo sprintf("-%s\n", $product->getName());
} }
The ``EntityManager#getRepository()`` method can create a finder object (called The ``EntityManager::getRepository()`` method can create a finder object (called
repository) for every entity. It is provided by Doctrine and contains some a repository) for every entity. It is provided by Doctrine and contains some
finder methods such as ``findAll()``. finder methods such as ``findAll()``.
Let's continue with displaying the name of a product based on its ID: Let's continue with displaying the name of a product based on its ID:
@ -555,12 +554,12 @@ We continue with the bug tracker domain, by creating the missing classes
All of the properties discussed so far are simple string and integer values, All of the properties discussed so far are simple string and integer values,
for example the id fields of the entities, their names, description, status and for example the id fields of the entities, their names, description, status and
change dates. With just the scalar values this model cannot describe the dynamics that we want. We change dates. Next we will model the dynamic relationships between the entities
want to model references between entities. by defining the references between entities.
References between objects are foreign keys in the database. You never have to References between objects are foreign keys in the database. You never have to
work with the foreign keys directly, only with objects that represent the (and never should) work with the foreign keys directly, only with the objects
foreign key through their own identity. that represent the foreign key through their own identity.
For every foreign key you either have a Doctrine ManyToOne or OneToOne For every foreign key you either have a Doctrine ManyToOne or OneToOne
association. On the inverse sides of these foreign keys you can have association. On the inverse sides of these foreign keys you can have
@ -778,8 +777,8 @@ the database that points from Bugs to Products.
} }
We are now finished with the domain model given the requirements. We are now finished with the domain model given the requirements.
Now we continue adding metadata mappings for the ``User`` and ``Bug`` Lets add metadata mappings for the ``User`` and ``Bug`` as we did for
as we did for the ``Product`` before: the ``Product`` before:
.. configuration-block:: .. configuration-block::
.. code-block:: php .. code-block:: php
@ -1072,7 +1071,7 @@ Since we only have one user and product, probably with the ID of 1, we can call
php create_bug.php 1 1 1 php create_bug.php 1 1 1
This is the first contact with the read API of the EntityManager, This is the first contact with the read API of the EntityManager,
showing that a call to ``EntityManager#find($name, $id)`` returns a showing that a call to ``EntityManager::find($name, $id)`` returns a
single instance of an entity queried by primary key. Besides this single instance of an entity queried by primary key. Besides this
we see the persist + flush pattern again to save the Bug into the we see the persist + flush pattern again to save the Bug into the
database. database.
@ -1129,7 +1128,7 @@ The console output of this script is then:
.. note:: .. note::
**Dql is not Sql** **DQL is not SQL**
You may wonder why we start writing SQL at the beginning of this You may wonder why we start writing SQL at the beginning of this
use-case. Don't we use an ORM to get rid of all the endless use-case. Don't we use an ORM to get rid of all the endless
@ -1142,6 +1141,7 @@ The console output of this script is then:
of Entity-Class and property. Using the Metadata we defined before of Entity-Class and property. Using the Metadata we defined before
it allows for very short distinctive and powerful queries. it allows for very short distinctive and powerful queries.
An important reason why DQL is favourable to the Query API of most An important reason why DQL is favourable to the Query API of most
ORMs is its similarity to SQL. The DQL language allows query ORMs is its similarity to SQL. The DQL language allows query
constructs that most ORMs don't, GROUP BY even with HAVING, constructs that most ORMs don't, GROUP BY even with HAVING,
@ -1151,30 +1151,31 @@ The console output of this script is then:
throw your ORM into the dumpster, because it doesn't support some throw your ORM into the dumpster, because it doesn't support some
the more powerful SQL concepts. the more powerful SQL concepts.
Besides handwriting DQL you can however also use the
``QueryBuilder`` retrieved by calling
``$entityManager->createQueryBuilder()`` which is a Query Object
around the DQL language.
As a last resort you can however also use Native SQL and a Instead of handwriting DQL you can use the ``QueryBuilder`` retrieved
description of the result set to retrieve entities from the by calling ``$entityManager->createQueryBuilder()``. There are more
database. DQL boils down to a Native SQL statement and a details about this in the relevant part of the documentation.
``ResultSetMapping`` instance itself. Using Native SQL you could
even use stored procedures for data retrieval, or make use of
advanced non-portable database queries like PostgreSql's recursive As a last resort you can still use Native SQL and a description of the
queries. result set to retrieve entities from the database. DQL boils down to a
Native SQL statement and a ``ResultSetMapping`` instance itself. Using
Native SQL you could even use stored procedures for data retrieval, or
make use of advanced non-portable database queries like PostgreSql's
recursive queries.
Array Hydration of the Bug List Array Hydration of the Bug List
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In the previous use-case we retrieved the result as their In the previous use-case we retrieved the results as their
respective object instances. We are not limited to retrieving respective object instances. We are not limited to retrieving
objects only from Doctrine however. For a simple list view like the objects only from Doctrine however. For a simple list view like the
previous one we only need read access to our entities and can previous one we only need read access to our entities and can
switch the hydration from objects to simple PHP arrays instead. switch the hydration from objects to simple PHP arrays instead.
This can obviously yield considerable performance benefits for
read-only requests. Hydration can be an expensive process so only retrieving what you need can
yield considerable performance benefits for read-only requests.
Implementing the same list view as before using array hydration we Implementing the same list view as before using array hydration we
can rewrite our code: can rewrite our code:
@ -1228,7 +1229,7 @@ write scenarios:
echo "Bug: ".$bug->getDescription()."\n"; echo "Bug: ".$bug->getDescription()."\n";
echo "Engineer: ".$bug->getEngineer()->getName()."\n"; echo "Engineer: ".$bug->getEngineer()->getName()."\n";
The output of the engineers name is fetched from the database! What is happening? The output of the engineers name is fetched from the database! What is happening?
Since we only retrieved the bug by primary key both the engineer and reporter Since we only retrieved the bug by primary key both the engineer and reporter
are not immediately loaded from the database but are replaced by LazyLoading are not immediately loaded from the database but are replaced by LazyLoading
@ -1274,6 +1275,14 @@ The call prints:
Bug: Something does not work! Bug: Something does not work!
Engineer: beberlei Engineer: beberlei
.. warning::
Lazy loading additional data can be very convenient but the additional
queries create an overhead. If you know that certain fields will always
(or usually) be required by the query then you will get better performance
by explicitly retrieving them all in the first query.
Dashboard of the User Dashboard of the User
--------------------- ---------------------
@ -1364,7 +1373,7 @@ should be able to close a bug. This looks like:
When retrieving the Bug from the database it is inserted into the When retrieving the Bug from the database it is inserted into the
IdentityMap inside the UnitOfWork of Doctrine. This means your Bug IdentityMap inside the UnitOfWork of Doctrine. This means your Bug
with exactly this id can only exist once during the whole request with exactly this id can only exist once during the whole request
no matter how often you call ``EntityManager#find()``. It even no matter how often you call ``EntityManager::find()``. It even
detects entities that are hydrated using DQL and are already detects entities that are hydrated using DQL and are already
present in the Identity Map. present in the Identity Map.