1
0
mirror of synced 2025-01-19 15:01:40 +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
requirements:
- A Bugs has a description, creation date, status, reporter and
- A Bug has a description, creation date, status, reporter and
engineer
- A bug can occur on different products (platforms)
- Products have a name.
- Bug Reporter and Engineers are both Users of the System.
- A user can create new bugs.
- The assigned engineer can close a bug.
- A user can see all his reported or assigned bugs.
- A Bug can occur on different Products (platforms)
- A Product has a name.
- Bug reporters and engineers are both Users of the system.
- A User can create new Bugs.
- The assigned engineer can close a Bug.
- A User can see all his reported or assigned Bugs.
- Bugs can be paginated through a list-view.
Setup Project
Project Setup
-------------
Create a new empty folder for this tutorial project, for example
@ -171,9 +171,9 @@ factory method.
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
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
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
-------------------------
We start with the Product entity requirements, because it is the most simple one
to get started. Create a ``src/Product.php`` file and put the ``Product``
entity definition in there:
We start with the simplest entity, the Product. Create a ``src/Product.php`` file to contain the ``Product``
entity definition:
.. 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 class and table-name. The primitive type ``Product::$name`` is
defined as ``field`` attributes. The Id property is defined with
the ``id`` tag. The id has a ``generator`` tag nested inside which
defined as a ``field`` attribute. The ``id`` property is defined with
the ``id`` tag, this has a ``generator`` tag nested inside which
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
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";
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
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:
.. code-block:: php
@ -395,8 +394,8 @@ new script for this:
echo sprintf("-%s\n", $product->getName());
}
The ``EntityManager#getRepository()`` method can create a finder object (called
repository) for every entity. It is provided by Doctrine and contains some
The ``EntityManager::getRepository()`` method can create a finder object (called
a repository) for every entity. It is provided by Doctrine and contains some
finder methods such as ``findAll()``.
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,
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
want to model references between entities.
change dates. Next we will model the dynamic relationships between the entities
by defining the references between entities.
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
foreign key through their own identity.
(and never should) work with the foreign keys directly, only with the objects
that represent the foreign key through their own identity.
For every foreign key you either have a Doctrine ManyToOne or OneToOne
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.
Now we continue adding metadata mappings for the ``User`` and ``Bug``
as we did for the ``Product`` before:
Lets add metadata mappings for the ``User`` and ``Bug`` as we did for
the ``Product`` before:
.. configuration-block::
.. 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
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
we see the persist + flush pattern again to save the Bug into the
database.
@ -1129,7 +1128,7 @@ The console output of this script is then:
.. note::
**Dql is not Sql**
**DQL is not SQL**
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
@ -1142,6 +1141,7 @@ The console output of this script is then:
of Entity-Class and property. Using the Metadata we defined before
it allows for very short distinctive and powerful queries.
An important reason why DQL is favourable to the Query API of most
ORMs is its similarity to SQL. The DQL language allows query
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
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
description of the 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.
Instead of handwriting DQL you can use the ``QueryBuilder`` retrieved
by calling ``$entityManager->createQueryBuilder()``. There are more
details about this in the relevant part of the documentation.
As a last resort you can still use Native SQL and a description of the
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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
objects only from Doctrine however. For a simple list view like the
previous one we only need read access to our entities and can
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
can rewrite our code:
@ -1228,7 +1229,7 @@ write scenarios:
echo "Bug: ".$bug->getDescription()."\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
are not immediately loaded from the database but are replaced by LazyLoading
@ -1274,6 +1275,14 @@ The call prints:
Bug: Something does not work!
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
---------------------
@ -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
IdentityMap inside the UnitOfWork of Doctrine. This means your Bug
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
present in the Identity Map.