Huge refactoring of the Getting Started Tutorial to allow for an earlier success with the simple Product entity.
This commit is contained in:
parent
4ef8e8c7aa
commit
ea19a0063f
@ -44,6 +44,19 @@ as a secondary problem. This doesn't mean persistence is downplayed by Doctrine
|
|||||||
object-oriented programming if persistence and entities are kept
|
object-oriented programming if persistence and entities are kept
|
||||||
separated.
|
separated.
|
||||||
|
|
||||||
|
What are Entities?
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Entities are PHP Objects that can be identified over many requests
|
||||||
|
by a unique identifier or primary key. These classes don't need to extend any
|
||||||
|
abstract base class or interface. An entity class must not be final
|
||||||
|
or contain final methods. Additionally it must not implement
|
||||||
|
**clone** nor **wakeup** or :doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
|
||||||
|
|
||||||
|
An entity contains persistable properties. A persistable property
|
||||||
|
is an instance variable of the entity that is saved into and retrieved from the database
|
||||||
|
by Doctrine's data mapping capabilities.
|
||||||
|
|
||||||
An Example Model: Bug Tracker
|
An Example Model: Bug Tracker
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
@ -78,7 +91,7 @@ the following contents:
|
|||||||
"symfony/yaml": "2.*"
|
"symfony/yaml": "2.*"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-0": {"": "entities"}
|
"psr-0": {"": "src/"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,39 +116,115 @@ You can prepare the directory structure:
|
|||||||
| `-- yaml
|
| `-- yaml
|
||||||
`-- src
|
`-- src
|
||||||
|
|
||||||
A first prototype
|
Obtaining the EntityManager
|
||||||
-----------------
|
---------------------------
|
||||||
|
|
||||||
We start with a simplified design for the bug tracker domain, by creating three
|
Doctrine's public interface is the EntityManager, it provides the
|
||||||
classes ``Bug``, ``Product`` and ``User`` and putting them into
|
access point to the complete lifecycle management of your entities
|
||||||
`src/Bug.php`, `src/Product.php` and `src/User.php`
|
and transforms entities from and back to persistence. You have to
|
||||||
respectively. We want instances of this three classes to be saved
|
configure and create it to use your entities with Doctrine 2. I
|
||||||
in the database. A class saved into a database with Doctrine is called
|
will show the configuration steps and then discuss them step by
|
||||||
entity. Entity classes are part of the domain model of your application.
|
step:
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
// src/Bug.php
|
// bootstrap.php
|
||||||
class Bug
|
use Doctrine\ORM\Tools\Setup;
|
||||||
{
|
use Doctrine\ORM\EntityManager;
|
||||||
/**
|
|
||||||
* @var int
|
require_once "vendor/autoload.php";
|
||||||
*/
|
|
||||||
protected $id;
|
// Create a simple "default" Doctrine ORM configuration for Annotations
|
||||||
/**
|
$isDevMode = true;
|
||||||
* @var string
|
$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/entities"), $isDevMode);
|
||||||
*/
|
// or if you prefer yaml or XML
|
||||||
protected $description;
|
//$config = Setup::createXMLMetadataConfiguration(array(__DIR__."/config/xml"), $isDevMode);
|
||||||
/**
|
//$config = Setup::createYAMLMetadataConfiguration(array(__DIR__."/config/yaml"), $isDevMode);
|
||||||
* @var DateTime
|
|
||||||
*/
|
// database configuration parameters
|
||||||
protected $created;
|
$conn = array(
|
||||||
/**
|
'driver' => 'pdo_sqlite',
|
||||||
* @var string
|
'path' => __DIR__ . '/db.sqlite',
|
||||||
*/
|
);
|
||||||
protected $status;
|
|
||||||
}
|
// obtaining the entity manager
|
||||||
|
$entityManager = EntityManager::create($conn, $config);
|
||||||
|
|
||||||
|
The first require statement sets up the autoloading capabilities of Doctrine
|
||||||
|
using the Composer autoload.
|
||||||
|
|
||||||
|
The second block consists of the instantiation of the ORM
|
||||||
|
``Configuration`` object using the Setup helper. It assumes a bunch
|
||||||
|
of defaults that you don't have to bother about for now. You can
|
||||||
|
read up on the configuration details in the
|
||||||
|
:doc:`reference chapter on configuration <../reference/configuration>`.
|
||||||
|
|
||||||
|
The third block shows the configuration options required to connect
|
||||||
|
to a database, in my case a file-based sqlite database. All the
|
||||||
|
configuration options for all the shipped drivers are given in the
|
||||||
|
`DBAL Configuration section of the manual <http://www.doctrine-project.org/documentation/manual/2_0/en/dbal>`_.
|
||||||
|
|
||||||
|
The last block shows how the ``EntityManager`` is obtained from a
|
||||||
|
factory method.
|
||||||
|
|
||||||
|
Generating the Database Schema
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
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
|
||||||
|
access the SchemaTool, a component that generates the required
|
||||||
|
tables to work with the metadata.
|
||||||
|
|
||||||
|
For the command-line tool to work a cli-config.php file has to be
|
||||||
|
present in the project root directory, where you will execute the
|
||||||
|
doctrine command. Its a fairly simple file:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// cli-config.php
|
||||||
|
require_once "bootstrap.php";
|
||||||
|
|
||||||
|
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
|
||||||
|
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($entityManager)
|
||||||
|
));
|
||||||
|
|
||||||
|
You can then change into your project directory and call the
|
||||||
|
Doctrine command-line tool:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
$ cd project/
|
||||||
|
$ php vendor/bin/doctrine orm:schema-tool:create
|
||||||
|
|
||||||
|
During the development you probably need to re-create the database
|
||||||
|
several times when changing the Entity metadata. You can then
|
||||||
|
either re-create the database:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
$ php vendor/bin/doctrine orm:schema-tool:drop --force
|
||||||
|
$ php vendor/bin/doctrine orm:schema-tool:create
|
||||||
|
|
||||||
|
Or use the update functionality:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
$ php vendor/bin/doctrine orm:schema-tool:update --force
|
||||||
|
|
||||||
|
The updating of databases uses a Diff Algorithm for a given
|
||||||
|
Database Schema, a cornerstone of the ``Doctrine\DBAL`` package,
|
||||||
|
which can even be used without the Doctrine ORM package. However
|
||||||
|
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:
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
@ -168,6 +257,196 @@ entity. Entity classes are part of the domain model of your application.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
|
||||||
|
The next step for persistence with Doctrine is to describe the
|
||||||
|
structure of the ``Product`` entity to Doctrine using a metadata
|
||||||
|
language. The metadata language describes how entities, their
|
||||||
|
properties and references should be persisted and what constraints
|
||||||
|
should be applied to them.
|
||||||
|
|
||||||
|
Metadata for entities are configured using a
|
||||||
|
XML, YAML or Docblock Annotations. This
|
||||||
|
Getting Started Guide will show the mappings for all Mapping Drivers.
|
||||||
|
References in the text will be made to the XML mapping.
|
||||||
|
|
||||||
|
.. configuration-block::
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// src/Product.php
|
||||||
|
/**
|
||||||
|
* @Entity @Table(name="products")
|
||||||
|
**/
|
||||||
|
class Product
|
||||||
|
{
|
||||||
|
/** @Id @Column(type="integer") @GeneratedValue **/
|
||||||
|
protected $id;
|
||||||
|
/** @Column(type="string") **/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
// .. (other code)
|
||||||
|
}
|
||||||
|
|
||||||
|
.. code-block:: xml
|
||||||
|
|
||||||
|
<!-- config/xml/Product.dcm.xml -->
|
||||||
|
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||||
|
http://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">
|
||||||
|
|
||||||
|
<entity name="Product" table="products">
|
||||||
|
<id name="id" type="integer">
|
||||||
|
<generator strategy="AUTO" />
|
||||||
|
</id>
|
||||||
|
|
||||||
|
<field name="name" type="string" />
|
||||||
|
</entity>
|
||||||
|
</doctrine-mapping>
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
# config/yaml/Product.dcm.yml
|
||||||
|
Product:
|
||||||
|
type: entity
|
||||||
|
table: products
|
||||||
|
id:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
generator:
|
||||||
|
strategy: AUTO
|
||||||
|
fields:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
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
|
||||||
|
defines that the primary key generation mechanism automatically
|
||||||
|
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.
|
||||||
|
|
||||||
|
You have to update the database now, because we have a first Entity now:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
$ php vendor/bin/doctrine orm:schema-tool:update
|
||||||
|
|
||||||
|
Now create a simple script to create a new product:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// create_product.php
|
||||||
|
require_once "bootstrap.php";
|
||||||
|
|
||||||
|
$newProductName = $argv[1];
|
||||||
|
|
||||||
|
$product = new Product();
|
||||||
|
$product->setName($newProductName);
|
||||||
|
|
||||||
|
$entityManager->persist($product);
|
||||||
|
$entityManager->flush();
|
||||||
|
|
||||||
|
echo "Created Product with ID " . $product->getId() . "\n";
|
||||||
|
|
||||||
|
Call this script to see how new products are created:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
$ php create_product.php ORM
|
||||||
|
$ php create_product.php DBAL
|
||||||
|
|
||||||
|
What is happening here? In the code using the Product is pretty standard OOP.
|
||||||
|
The interesting bits are the communication with the ``EntityManager``. To
|
||||||
|
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
|
||||||
|
command, its merely notified. You have to explicitly call ``flush()`` to have
|
||||||
|
the EntityManager write those two entities to the database.
|
||||||
|
|
||||||
|
You might wonder why does this distinction between persist notification and
|
||||||
|
flush exist: Doctrine 2 uses the UnitOfWork pattern to aggregate all writes
|
||||||
|
(INSERT, UPDATE, DELETE) into one single transaction, which is executed when
|
||||||
|
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.
|
||||||
|
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
|
||||||
|
the database automatically when the flush operation is called, so that you only
|
||||||
|
have to keep track of those entities that are new or to be removed and pass
|
||||||
|
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
|
||||||
|
this:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// list_products.php
|
||||||
|
$productRepository = $entityManager->getRepository('Product');
|
||||||
|
$products = $productRepository->findAll();
|
||||||
|
|
||||||
|
foreach ($products as $product) {
|
||||||
|
echo sprintf("-%s\n", $product->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
The ``EntityRepository`` fetched through the ``EntityManager#getRepository()``
|
||||||
|
method exists for every entity and is provided by Doctrine. It contains
|
||||||
|
some finder methods such as ``findAll()`` we used here.
|
||||||
|
|
||||||
|
Lets display the name of a product based on its ID:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// show_product.php
|
||||||
|
$id = $argv[1];
|
||||||
|
$product = $entityManager->find('Product', $id);
|
||||||
|
|
||||||
|
echo sprintf("-%s\n", $product->getName());
|
||||||
|
|
||||||
|
Adding Bug and User Entities
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
We continue with the bug tracker domain, by creating the missing
|
||||||
|
classes ``Bug`` and ``User`` and putting them into
|
||||||
|
`src/Bug.php` and `src/User.php`
|
||||||
|
respectively.
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// src/Bug.php
|
||||||
|
class Bug
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $id;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description;
|
||||||
|
/**
|
||||||
|
* @var DateTime
|
||||||
|
*/
|
||||||
|
protected $created;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
@ -199,25 +478,6 @@ entity. Entity classes are part of the domain model of your application.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
**What are Entities?**
|
|
||||||
|
|
||||||
Entities are PHP Objects that can be identified over many requests
|
|
||||||
by a unique identifier or primary key. These classes don't need to extend any
|
|
||||||
abstract base class or interface. An entity class must not be final
|
|
||||||
or contain final methods. Additionally it must not implement
|
|
||||||
**clone** nor **wakeup** or :doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
|
|
||||||
|
|
||||||
An entity contains persistable properties. A persistable property
|
|
||||||
is an instance variable of the entity that is saved into and retrieved from the database
|
|
||||||
by Doctrine's data mapping capabilities.
|
|
||||||
|
|
||||||
Note how all properties have getter and setter methods defined except
|
|
||||||
`$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
|
|
||||||
take Doctrine into account when designing access to the state of your objects.
|
|
||||||
|
|
||||||
All of the properties so far are scalar values, for example the 3 ID
|
All of the properties so far are scalar values, for example the 3 ID
|
||||||
fields of the entities, their names, description, status and change dates.
|
fields of the entities, their names, description, status and change dates.
|
||||||
|
|
||||||
@ -296,7 +556,6 @@ persistence.
|
|||||||
Debug::dump() method just ignores any occurrences of it in Proxy
|
Debug::dump() method just ignores any occurrences of it in Proxy
|
||||||
instances.
|
instances.
|
||||||
|
|
||||||
|
|
||||||
Because we only work with collections for the references we must be
|
Because we only work with collections for the references we must be
|
||||||
careful to implement a bidirectional reference in the domain model.
|
careful to implement a bidirectional reference in the domain model.
|
||||||
The concept of owning or inverse side of a relation is central to
|
The concept of owning or inverse side of a relation is central to
|
||||||
@ -448,112 +707,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.
|
||||||
From the simple model with public properties only we had to do
|
Now we continue adding metadata mappings for the ``User`` and ``Bug``
|
||||||
quite some work to get to a model where we encapsulated the
|
as we did for the ``Product`` before:
|
||||||
references between the objects to make sure we don't break its
|
|
||||||
consistent state when using Doctrine.
|
|
||||||
|
|
||||||
However up to now the assumptions Doctrine imposed on our business
|
|
||||||
objects have not restricting us much in our domain modelling
|
|
||||||
capabilities. Actually we would have encapsulated access to all the
|
|
||||||
properties anyways by using object-oriented best-practices.
|
|
||||||
|
|
||||||
Metadata Mappings for our Entities
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
Up to now we have only implemented our Entities as Data-Structures
|
|
||||||
without actually telling Doctrine how to persist them in the
|
|
||||||
database. If perfect in-memory databases would exist, we could now
|
|
||||||
finish the application using these entities by implementing code to
|
|
||||||
fulfil all the requirements. However the world isn't perfect and we
|
|
||||||
have to persist our entities in some storage to make sure we don't
|
|
||||||
loose their state. Doctrine currently serves Relational Database
|
|
||||||
Management Systems. In the future we are thinking to support NoSQL
|
|
||||||
vendors like CouchDb or MongoDb, however this is still far in the
|
|
||||||
future.
|
|
||||||
|
|
||||||
The next step for persistence with Doctrine is to describe the
|
|
||||||
structure of our domain model entities to Doctrine using a metadata
|
|
||||||
language. The metadata language describes how entities, their
|
|
||||||
properties and references should be persisted and what constraints
|
|
||||||
should be applied to them.
|
|
||||||
|
|
||||||
Metadata for entities are loaded using a
|
|
||||||
``Doctrine\ORM\Mapping\Driver\Driver`` implementation and Doctrine
|
|
||||||
2 already comes with XML, YAML and Annotations Drivers. This
|
|
||||||
Getting Started Guide will show the mappings for all Mapping Drivers.
|
|
||||||
References in the text will be made to the XML mapping.
|
|
||||||
|
|
||||||
Since we haven't namespaced our three entities, we have to
|
|
||||||
implement three mapping files called Bug.dcm.xml, Product.dcm.xml
|
|
||||||
and User.dcm.xml (or .yml) and put them into a distinct folder for mapping
|
|
||||||
configurations. For the annotations driver we need to use
|
|
||||||
doc-block comments on the entity classes themselves.
|
|
||||||
|
|
||||||
The first discussed definition will be for the Product, since it is
|
|
||||||
the most simple one:
|
|
||||||
|
|
||||||
.. configuration-block::
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
// src/Product.php
|
|
||||||
/**
|
|
||||||
* @Entity @Table(name="products")
|
|
||||||
**/
|
|
||||||
class Product
|
|
||||||
{
|
|
||||||
/** @Id @Column(type="integer") @GeneratedValue **/
|
|
||||||
protected $id;
|
|
||||||
/** @Column(type="string") **/
|
|
||||||
protected $name;
|
|
||||||
|
|
||||||
// .. (other code)
|
|
||||||
}
|
|
||||||
|
|
||||||
.. code-block:: xml
|
|
||||||
|
|
||||||
<!-- config/xml/Product.dcm.xml -->
|
|
||||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
|
||||||
http://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">
|
|
||||||
|
|
||||||
<entity name="Product" table="products">
|
|
||||||
<id name="id" type="integer">
|
|
||||||
<generator strategy="AUTO" />
|
|
||||||
</id>
|
|
||||||
|
|
||||||
<field name="name" type="string" />
|
|
||||||
</entity>
|
|
||||||
</doctrine-mapping>
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
# config/yaml/Product.dcm.yml
|
|
||||||
Product:
|
|
||||||
type: entity
|
|
||||||
table: products
|
|
||||||
id:
|
|
||||||
id:
|
|
||||||
type: integer
|
|
||||||
generator:
|
|
||||||
strategy: AUTO
|
|
||||||
fields:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
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
|
|
||||||
defines that the primary key generation mechanism automatically
|
|
||||||
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.
|
|
||||||
|
|
||||||
We then go on specifying the definition of a Bug:
|
|
||||||
|
|
||||||
.. configuration-block::
|
.. configuration-block::
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
@ -654,7 +809,7 @@ We then go on specifying the definition of a Bug:
|
|||||||
targetEntity: Product
|
targetEntity: Product
|
||||||
|
|
||||||
|
|
||||||
Here again we have the entity, id and primitive type definitions.
|
Here we have the entity, id and primitive type definitions.
|
||||||
The column names are used from the Zend\_Db\_Table examples and
|
The column names are used from the Zend\_Db\_Table examples and
|
||||||
have different names than the properties on the Bug class.
|
have different names than the properties on the Bug class.
|
||||||
Additionally for the "created" field it is specified that it is of
|
Additionally for the "created" field it is specified that it is of
|
||||||
@ -774,118 +929,10 @@ class that holds the owning sides.
|
|||||||
This example has a fair overview of the most basic features of the
|
This example has a fair overview of the most basic features of the
|
||||||
metadata definition language.
|
metadata definition language.
|
||||||
|
|
||||||
Obtaining the EntityManager
|
Implementing more Requirements
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Doctrine's public interface is the EntityManager, it provides the
|
|
||||||
access point to the complete lifecycle management of your entities
|
|
||||||
and transforms entities from and back to persistence. You have to
|
|
||||||
configure and create it to use your entities with Doctrine 2. I
|
|
||||||
will show the configuration steps and then discuss them step by
|
|
||||||
step:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
// bootstrap.php
|
|
||||||
use Doctrine\ORM\Tools\Setup;
|
|
||||||
use Doctrine\ORM\EntityManager;
|
|
||||||
|
|
||||||
require_once "vendor/autoload.php";
|
|
||||||
|
|
||||||
// Create a simple "default" Doctrine ORM configuration for Annotations
|
|
||||||
$isDevMode = true;
|
|
||||||
$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/entities"), $isDevMode);
|
|
||||||
// or if you prefer yaml or XML
|
|
||||||
//$config = Setup::createXMLMetadataConfiguration(array(__DIR__."/config/xml"), $isDevMode);
|
|
||||||
//$config = Setup::createYAMLMetadataConfiguration(array(__DIR__."/config/yaml"), $isDevMode);
|
|
||||||
|
|
||||||
// database configuration parameters
|
|
||||||
$conn = array(
|
|
||||||
'driver' => 'pdo_sqlite',
|
|
||||||
'path' => __DIR__ . '/db.sqlite',
|
|
||||||
);
|
|
||||||
|
|
||||||
// obtaining the entity manager
|
|
||||||
$entityManager = EntityManager::create($conn, $config);
|
|
||||||
|
|
||||||
The first require statement sets up the autoloading capabilities of Doctrine
|
|
||||||
using the Composer autoload.
|
|
||||||
|
|
||||||
The second block consists of the instantiation of the ORM
|
|
||||||
``Configuration`` object using the Setup helper. It assumes a bunch
|
|
||||||
of defaults that you don't have to bother about for now. You can
|
|
||||||
read up on the configuration details in the
|
|
||||||
:doc:`reference chapter on configuration <../reference/configuration>`.
|
|
||||||
|
|
||||||
The third block shows the configuration options required to connect
|
|
||||||
to a database, in my case a file-based sqlite database. All the
|
|
||||||
configuration options for all the shipped drivers are given in the
|
|
||||||
`DBAL Configuration section of the manual <http://www.doctrine-project.org/documentation/manual/2_0/en/dbal>`_.
|
|
||||||
|
|
||||||
The last block shows how the ``EntityManager`` is obtained from a
|
|
||||||
factory method.
|
|
||||||
|
|
||||||
Generating the Database Schema
|
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
Now that we have defined the Metadata Mappings and bootstrapped the
|
For starters we need a create user entities:
|
||||||
EntityManager we want to generate the relational database schema
|
|
||||||
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.
|
|
||||||
|
|
||||||
For the command-line tool to work a cli-config.php file has to be
|
|
||||||
present in the project root directory, where you will execute the
|
|
||||||
doctrine command. Its a fairly simple file:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
// cli-config.php
|
|
||||||
require_once "bootstrap.php";
|
|
||||||
|
|
||||||
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
|
|
||||||
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($entityManager)
|
|
||||||
));
|
|
||||||
|
|
||||||
You can then change into your project directory and call the
|
|
||||||
Doctrine command-line tool:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
$ cd project/
|
|
||||||
$ php vendor/bin/doctrine orm:schema-tool:create
|
|
||||||
|
|
||||||
During the development you probably need to re-create the database
|
|
||||||
several times when changing the Entity metadata. You can then
|
|
||||||
either re-create the database:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
$ php vendor/bin/doctrine orm:schema-tool:drop --force
|
|
||||||
$ php vendor/bin/doctrine orm:schema-tool:create
|
|
||||||
|
|
||||||
Or use the update functionality:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
$ php vendor/bin/doctrine orm:schema-tool:update --force
|
|
||||||
|
|
||||||
The updating of databases uses a Diff Algorithm for a given
|
|
||||||
Database Schema, a cornerstone of the ``Doctrine\DBAL`` package,
|
|
||||||
which can even be used without the Doctrine ORM package. However
|
|
||||||
its not available in SQLite since it does not support ALTER TABLE.
|
|
||||||
|
|
||||||
Writing Entities into the Database
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
This tutorial assumes you call all the example scripts from the CLI.
|
|
||||||
|
|
||||||
Having created the schema we can now start and save entities in the
|
|
||||||
database. For starters we need a create user use-case:
|
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
@ -903,59 +950,14 @@ database. For starters we need a create user use-case:
|
|||||||
|
|
||||||
echo "Created User with ID " . $user->getId() . "\n";
|
echo "Created User with ID " . $user->getId() . "\n";
|
||||||
|
|
||||||
Products can also be created:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
// create_product.php
|
|
||||||
require_once "bootstrap.php";
|
|
||||||
|
|
||||||
$newProductName = $argv[1];
|
|
||||||
|
|
||||||
$product = new Product();
|
|
||||||
$product->setName($newProductName);
|
|
||||||
|
|
||||||
$entityManager->persist($product);
|
|
||||||
$entityManager->flush();
|
|
||||||
|
|
||||||
echo "Created Product with ID " . $product->getId() . "\n";
|
|
||||||
|
|
||||||
Now call:
|
Now call:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
$ php create_user.php beberlei
|
$ php create_user.php beberlei
|
||||||
$ php create_product.php MyProduct
|
|
||||||
|
|
||||||
So what is happening in those two snippets? In both examples the
|
We now have the data to create a bug and the code for this scenario may look
|
||||||
code that works on User and Product is pretty standard OOP. The interesting bits are the
|
like this:
|
||||||
communication with the ``EntityManager``. To 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 command, its merely notified. You have to explicitly
|
|
||||||
call ``flush()`` to have the EntityManager write those two entities
|
|
||||||
to the database.
|
|
||||||
|
|
||||||
You might wonder why does this distinction between persist
|
|
||||||
notification and flush exist: Doctrine 2 uses the UnitOfWork
|
|
||||||
pattern to aggregate all writes (INSERT, UPDATE, DELETE) into one
|
|
||||||
single transaction, which is executed when 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. 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 the database automatically when the flush operation
|
|
||||||
is called, so that you only have to keep track of those entities
|
|
||||||
that are new or to be removed and pass them to
|
|
||||||
``EntityManager#persist()`` and ``EntityManager#remove()``
|
|
||||||
respectively.
|
|
||||||
|
|
||||||
We are now getting to the "Create a New Bug" requirement and the
|
|
||||||
code for this scenario may look like this:
|
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
@ -1462,4 +1464,3 @@ will be added to this tutorial incrementally, topics will include:
|
|||||||
Additional details on all the topics discussed here can be found in
|
Additional details on all the topics discussed here can be found in
|
||||||
the respective manual chapters.
|
the respective manual chapters.
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user