1
0
mirror of synced 2025-02-22 15:13:13 +03:00

Work on the Tutorial

This commit is contained in:
Benjamin Eberlei 2013-04-09 00:00:16 +02:00
parent d1f8e18d02
commit 142c20aad1

View File

@ -258,8 +258,8 @@ entity definition in there:
} }
Note how the properties have getter and setter methods defined except Note how the properties have getter and setter methods defined except
`$id`. To access data from entities Doctrine 2 uses the Reflection API, so it ``$id``. To access data from entities Doctrine 2 uses the Reflection API, so it
is possible for Doctrine to access the value of `$id`. You don't have to is possible for Doctrine to access the value of ``$id``. You don't have to
take Doctrine into account when designing access to the state of your objects. take Doctrine into account when designing access to the state of your objects.
The next step for persistence with Doctrine is to describe the The next step for persistence with Doctrine is to describe the
@ -268,9 +268,8 @@ language. The metadata language describes how entities, their
properties and references should be persisted and what constraints properties and references should be persisted and what constraints
should be applied to them. should be applied to them.
Metadata for entities are configured using a Metadata for entities are configured using a XML, YAML or Docblock Annotations.
XML, YAML or Docblock Annotations. This This Getting Started Guide will show the mappings for all Mapping Drivers.
Getting Started Guide will show the mappings for all Mapping Drivers.
References in the text will be made to the XML mapping. References in the text will be made to the XML mapping.
.. configuration-block:: .. configuration-block::
@ -337,9 +336,12 @@ You have to update the database now, because we have a first Entity now:
:: ::
$ php vendor/bin/doctrine orm:schema-tool:update $ php vendor/bin/doctrine orm:schema-tool:update --force --dump-sql
Now create a simple script to create a new product: Specifying both flags ``--force`` and ``-dump-sql`` prints and executes the DDL
statements.
Now create a new script that will insert products into the database:
.. code-block:: php .. code-block:: php
@ -357,41 +359,37 @@ Now create a simple script to create a new product:
echo "Created Product with ID " . $product->getId() . "\n"; echo "Created Product with ID " . $product->getId() . "\n";
Call this script to see how new products are created: Call this script from the command line to see how new products are created:
:: ::
$ php create_product.php ORM $ php create_product.php ORM
$ php create_product.php DBAL $ php create_product.php DBAL
What is happening here? In the code using the Product is pretty standard OOP. What is happening here? Using the ``Product`` is pretty standard OOP.
The interesting bits are the communication with the ``EntityManager``. To The interesting bits are the use of the ``EntityManager`` service. To
notify the EntityManager that a new entity should be inserted into the database notify the EntityManager that a new entity should be inserted into the database
you have to call ``persist()``. However the EntityManager does not act on this you have to call ``persist()``. To intiate a transaction to actually perform
command, its merely notified. You have to explicitly call ``flush()`` to have the insertion, You have to explicitly call ``flush()`` on the ``EntityManager``.
the EntityManager write those two entities to the database.
You might wonder why does this distinction between persist notification and This distinction between persist and flush is allows to aggregate all writes
flush exist: Doctrine 2 uses the UnitOfWork pattern to aggregate all writes
(INSERT, UPDATE, DELETE) into one single transaction, which is executed when (INSERT, UPDATE, DELETE) into one single transaction, which is executed when
flush is called. Using this approach the write-performance is significantly flush is called. Using this approach the write-performance is significantly
better than in a scenario where updates are done for each entity in isolation. better than in a scenario where updates are done for each entity in isolation.
In more complex scenarios than the previous two, you are free to request
updates on many different entities and all flush them at once.
Doctrine's UnitOfWork detects entities that have changed after retrieval from Doctrine follows the UnitOfWork pattern which additionally detects all entities
the database automatically when the flush operation is called, so that you only that were fetched and have changed during the request. You don't have to keep track of
have to keep track of those entities that are new or to be removed and pass entities yourself, when Doctrine already knowns about them.
them to ``EntityManager#persist()`` and ``EntityManager#remove()``
respectively.
We want to see a list of all the products now, so lets create a new script for As a next step we want to fetch a list of all the products. Let's create a
this: new script for this:
.. code-block:: php .. code-block:: php
<?php <?php
// list_products.php // list_products.php
require_once "bootstrap.php";
$productRepository = $entityManager->getRepository('Product'); $productRepository = $entityManager->getRepository('Product');
$products = $productRepository->findAll(); $products = $productRepository->findAll();
@ -399,65 +397,144 @@ this:
echo sprintf("-%s\n", $product->getName()); echo sprintf("-%s\n", $product->getName());
} }
The ``EntityRepository`` fetched through the ``EntityManager#getRepository()`` The ``EntityManager#getRepository()`` method can create a finder object (called
method exists for every entity and is provided by Doctrine. It contains repository) for every entity. It is provided by Doctrine and contains some
some finder methods such as ``findAll()`` we used here. finder methods such as ``findAll()``.
Lets display the name of a product based on its ID: Let's continue with displaying the name of a product based on its ID:
.. code-block:: php .. code-block:: php
<?php <?php
// show_product.php // show_product.php <id>
require_once "bootstrap.php";
$id = $argv[1]; $id = $argv[1];
$product = $entityManager->find('Product', $id); $product = $entityManager->find('Product', $id);
if ($product === null) {
echo "No product found.\n";
exit(1);
}
echo sprintf("-%s\n", $product->getName()); echo sprintf("-%s\n", $product->getName());
Updating a product name demonstrates the functionality UnitOfWork of pattern
discussed before. We only need to find a product entity and all changes to its
properties are written to the database:
.. code-block:: php
<?php
// update_product.php <id> <new-name>
require_once "bootstrap.php";
$id = $argv[1];
$newName = $argv[2];
$product = $entityManager->find('Product', $id);
if ($product === null) {
echo "Product $id does not exist.\n";
exit(1);
}
$product->setName($newName);
$entityManager->flush();
After calling this script on one of the existing products, you can verify the
product name changed by calling the ``show_product.php`` script.
Adding Bug and User Entities Adding Bug and User Entities
---------------------------- ----------------------------
We continue with the bug tracker domain, by creating the missing We continue with the bug tracker domain, by creating the missing classes
classes ``Bug`` and ``User`` and putting them into ``Bug`` and ``User`` and putting them into ``src/Bug.php`` and
`src/Bug.php` and `src/User.php` ``src/User.php`` respectively.
respectively.
.. code-block:: php .. code-block:: php
<?php <?php
// src/Bug.php // src/Bug.php
/**
* @Entity(repositoryClass="BugRepository") @Table(name="bugs")
*/
class Bug class Bug
{ {
/** /**
* @Id @Column(type="integer") @GeneratedValue
* @var int * @var int
*/ */
protected $id; protected $id;
/** /**
* @Column(type="string")
* @var string * @var string
*/ */
protected $description; protected $description;
/** /**
* @Column(type="datetime")
* @var DateTime * @var DateTime
*/ */
protected $created; protected $created;
/** /**
* @Column(type="string")
* @var string * @var string
*/ */
protected $status; protected $status;
public function getId()
{
return $this->id;
} }
public function getDescription()
{
return $this->description;
}
public function setDescription($description)
{
$this->description = $description;
}
public function setCreated(DateTime $created)
{
$this->created = $created;
}
public function getCreated()
{
return $this->created;
}
public function setStatus($status)
{
$this->status = $status;
}
public function getStatus()
{
return $this->status;
}
}
.. code-block:: php .. code-block:: php
<?php <?php
// src/User.php // src/User.php
/**
* @Entity @Table(name="users")
*/
class User class User
{ {
/** /**
* @Id @GeneratedValue @Column(type="integer")
* @var int * @var int
*/ */
protected $id; protected $id;
/** /**
* @Column(type="string")
* @var string * @var string
*/ */
protected $name; protected $name;
@ -478,24 +555,20 @@ respectively.
} }
} }
All of the properties so far are scalar values, for example the 3 ID All of the properties discussed so far are simple string and integer values,
fields of the entities, their names, description, status and change dates. 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.
With just the scalar values this model is useless. We need to add references References between objects are foreign keys in the database. You never have to
between entities in this domain model. The semantics of each type of reference work with the foreign keys directly, only with objects that represent the
are now introduced and discussed on a case by case basis foreign key through their own identity.
to explain how Doctrine handles them.
In general each OneToOne or ManyToOne Relation in the Database is replaced by For every foreign key you either have a Doctrine ManyToOne or OneToOne
an instance of the related object in the domain model. Each OneToMany or association. On the inverse sides of these foreign keys you can have
ManyToMany Relation is replaced by a collection of instances in the domain OneToMany associations. Obviously you can have ManyToMany associations
model. You never have to work with the foreign keys, only with objects that that connect two tables with each other through a join table with
represent the foreign key through their own identity. two foreign keys.
To prevent Doctrine 2 from loading up the complete database in memory if you
access one object, the Lazy Load pattern is implemented. Proxies of entities or
collections are created of all the relations that haven't been explicitly
retrieved from the database yet.
Now that you know the basics about references in Doctrine, we can extend the Now that you know the basics about references in Doctrine, we can extend the
domain model to match the requirements: domain model to match the requirements: