1
0
mirror of synced 2025-02-21 06:33:14 +03:00

Merge branch 'master' of github.com:doctrine/orm-documentation

This commit is contained in:
beberlei 2010-07-10 08:33:17 +02:00
commit 0c8e0450de
8 changed files with 307 additions and 59 deletions

View File

@ -56,6 +56,7 @@ Optional attributes:
Examples:
[php]
/**
* @Column(type="string", length=32, unique=true, nullable=false)
*/
@ -85,6 +86,7 @@ can be found in the configuration section.
Example:
[php]
/**
* @Entity
* @ChangeTrackingPolicy("DEFERRED_IMPLICIT")
@ -116,6 +118,7 @@ an array as only argument which defines which class should be saved under which
are the database value and values are the classes, either as fully- or as unqualified class names depending
if the classes are in the namespace or not.
[php]
/**
* @Entity
* @InheritanceType("JOINED")
@ -139,6 +142,7 @@ Optional attributes:
Example:
[php]
/**
* @Entity(repositoryClass="MyProject\UserRepository")
*/
@ -161,6 +165,7 @@ Required attributes:
Example:
[php]
/**
* @Id
* @Column(type="integer")
@ -177,6 +182,7 @@ callback annotations set on at least one of its methods. Using @PostLoad, @PrePe
Example:
[php]
/**
* @Entity
* @HasLifecycleCallbacks
@ -203,6 +209,7 @@ Required attributes:
Example:
[php]
/**
* @Entity
* @Table(name="ecommerce_products",indexes={@index(name="search_idx", columns={"name", "email"})})
@ -220,6 +227,7 @@ identifier columns each column has to be marked with @Id.
Example:
[php]
/**
* @Id
* @Column(type="integer")
@ -237,6 +245,7 @@ This annotation has always been used in conjunction with the [@DiscriminatorMap]
Examples:
[php]
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
@ -281,6 +290,7 @@ Optional attributes:
Example:
[php]
/**
* @OneToOne(targetEntity="Customer")
* @JoinColumn(name="customer_id", referencedColumnName="id")
@ -312,6 +322,7 @@ Optional attributes:
Example:
[php]
/**
* @ManyToMany(targetEntity="Phonenumber")
* @JoinTable(name="users_phonenumbers",
@ -334,9 +345,11 @@ Optional attributes:
* cascade - Cascade Option
* fetch - One of LAZY or EAGER
* inversedBy - The inversedBy attribute designates the field in the entity that is the inverse side of the relationship.
Example:
[php]
/**
* @ManyToOne(targetEntity="Cart", cascade="ALL", fetch="EAGER")
*/
@ -356,15 +369,22 @@ Required attributes:
Optional attributes:
* mappedBy - This option specifies the property name on the targetEntity that is the owning side of this relation. Its a required attribute for the inverse side of a relationship.
* inversedBy - The inversedBy attribute designates the field in the entity that is the inverse side of the relationship.
* cascade - Cascade Option
* fetch - One of LAZY or EAGER
> **NOTE**
> For ManyToMany bidirectional relationships either side may be the owning side (the side
> that defines the @JoinTable and/or does not make use of the mappedBy attribute, thus
> using a default join table).
Example:
[php]
/**
* Owning Side
*
* @ManyToMany(targetEntity="Group")
* @ManyToMany(targetEntity="Group", inversedBy="features")
* @JoinTable(name="user_groups",
* joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id")}
@ -390,7 +410,7 @@ The @MappedSuperclass annotation cannot be used in conjunction with @Entity. See
section for [more details on the restrictions of mapped superclasses](/../inheritance-mapping#mapped-superclasses).
<a name="ann_onetoone"></a>
+++ @OnetoOne
+++ @OneToOne
The @OneToOne annotation works almost exactly as the [@ManyToOne](#ann_manytoone) with one additional option
that can be specified. The configuration defaults for [@JoinColumn](#ann_joincolumn) using the target entity table and primary key column names
@ -406,10 +426,39 @@ Optional attributes:
* fetch - One of LAZY or EAGER
* orphanRemoval - Boolean that specifies if orphans, inverse OneToOne entities that are not connected to any
owning instance, should be removed by Doctrine. Defaults to false.
* inversedBy - The inversedBy attribute designates the field in the entity that is the inverse side of the relationship.
Example:
[php]
/**
* @OneToOne(targetEntity="Customer")
* @JoinColumn(name="customer_id", referencedColumnName="id")
*/
private $customer;
<a name="ann_onetomany"></a>
+++ @OneToMany
Required attributes:
* targetEntity - FQCN of the referenced target entity. Can be the unqualified class name if both classes are in the same namespace.
Optional attributes:
* cascade - Cascade Option
* orphanRemoval - Boolean that specifies if orphans, inverse OneToOne entities that are not connected to any
owning instance, should be removed by Doctrine. Defaults to false.
* mappedBy - This option specifies the property name on the targetEntity that is the owning side of this relation. Its a required attribute for the inverse side of a relationship.
Example:
[php]
/**
* @OneToMany(targetEntity="Phonenumber", mappedBy="user", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
*/
public $phonenumbers;
<a name="ann_orderby"></a>
+++ @OrderBy
@ -421,6 +470,7 @@ This annotation requires a single non-attributed value with an DQL snippet:
Example:
[php]
/**
* @ManyToMany(targetEntity="Group")
* @OrderBy({"name" = "ASC"})
@ -485,6 +535,7 @@ Optional attributes:
Example:
[php]
/**
* @Id
* @GeneratedValue(strategy="SEQUENCE")
@ -511,6 +562,7 @@ Optional attributes:
Example:
[php]
/**
* @Entity
* @Table(name="user",
@ -534,6 +586,7 @@ Required attributes:
Example:
[php]
/**
* @Entity
* @Table(name="ecommerce_products",uniqueConstraints={@UniqueConstraint(name="search_idx", columns={"name", "email"})})
@ -550,6 +603,7 @@ It only works on [@Column](#ann_column) annotations that have the type integer o
Example:
[php]
/**
* @column(type="integer")
* @version

View File

@ -9,7 +9,6 @@ When mapping bidirectional associations it is important to understand the concep
* A unidirectional relationship only has an owning side.
* The owning side of a relationship determines the updates to the relationship in the database.
The following rules apply to *bidirectional* associations:
* The inverse side of a bidirectional relationship must refer to its owning side by use of the mappedBy attribute of the OneToOne, OneToMany, or ManyToMany mapping declaration. The mappedBy attribute designates the field in the entity that is the owner of the relationship.
@ -27,7 +26,7 @@ in the object world. There are 2 references on each side of the association
and these 2 references both represent the same association but can change
independently of one another. Of course, in a correct application the semantics
of the bidirectional association are properly maintained by the application
developer (that's his responsiblity). Doctrine needs to know which of
developer (that's his responsibility). Doctrine needs to know which of
these two in-memory references is the one that should be persisted and which
not. This is what the owning/inverse concept is mainly used for.

View File

@ -71,6 +71,8 @@ For example, the Doctrine Mapping Type `string` defines the mapping from a PHP s
* `time`: Type that maps an SQL TIME to a PHP DateTime object.
* `datetime`: Type that maps an SQL DATETIME/TIMESTAMP to a PHP DateTime object.
* `text`: Type that maps an SQL CLOB to a PHP string.
* `object`: Type that maps a SQL CLOB to a PHP object using `serialize()` and `unserialize()`
* `array`: Type that maps a SQL CLOB to a PHP object using `serialize()` and `unserialize()`
> **NOTE**
> Doctrine Mapping Types are NOT SQL types and NOT PHP types! They are mapping types
@ -189,6 +191,17 @@ for the mapping type and map that to the corresponding fully qualified class nam
private $field;
}
To have Schema-Tool convert the underlying database type of your new "mytype" directly into an instance of `MyType`
you have to additionally register this mapping with your database platform:
[php]
$conn = $em->getConnection();
$conn->getDatabasePlatform()->registerDoctrineTypeMapping('db_mytype', 'mytype');
Now using Schema-Tool, whenever it detects a column having the `db_mytype` it will convert it into a `mytype`
Doctrine Type instance for Schema representation. Keep in mind that you can easily produce clashes this way,
each database type can only map to exactly one Doctrine mapping type.
++ Identifiers / Primary Keys
Every entity class needs an identifier/primary key. You designate the field that serves as the identifier with the `@Id` marker annotation. Here is an example:

View File

@ -188,11 +188,12 @@ With Nested Conditions in WHERE Clause:
With COUNT DISTINCT:
[php]
$query = $em->createQuery('SELECT COUNT(DISTINCT u.name) FROM CmsUser
$query = $em->createQuery('SELECT COUNT(DISTINCT u.name) FROM CmsUser');
$users = $query->getResult(); // array of ForumUser objects
With Arithmetic Expression in WHERE clause:
[php]
$query = $em->createQuery('SELECT u FROM CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000');
$users = $query->getResult(); // array of ForumUser objects
@ -452,6 +453,121 @@ We will register the function by calling and can then use it:
\Doctrine\ORM\Query\Parser::registerNumericFunction('FLOOR', 'MyProject\Query\MysqlFloor');
$dql = "SELECT FLOOR(person.salary * 1.75) FROM CompanyPerson person";
++ Querying Inherited Classes
This section demonstrates how you can query inherited classes and what type of results
to expect.
+++ Single Table
[Single Table Inheritance](http://martinfowler.com/eaaCatalog/singleTableInheritance.html) is an inheritance mapping strategy where all classes of a hierarchy are mapped to a single database table. In order to distinguish which row represents which type in the hierarchy a so-called discriminator column is used.
First we need to setup an example set of entities to use. In this scenario it is a generic
Person and Employee example:
[php]
namespace Entities;
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
*/
class Person
{
/**
* @Id @Column(type="integer")
* @GeneratedValue
*/
protected $id;
/**
* @Column(type="string", length=50)
*/
protected $name;
// ...
}
/**
* @Entity
*/
class Employee extends Person
{
/**
* @Column(type="string", length=50)
*/
private $department;
// ...
}
First notice that the generated SQL to create the tables for these entities looks like
the following:
[sql]
CREATE TABLE Person (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(50) NOT NULL, discr VARCHAR(255) NOT NULL, department VARCHAR(50) NOT NULL)
Now when persist a new `Employee` instance it will set the discriminator value for us
automatically:
[php]
$employee = new \Entities\Employee();
$employee->setName('test');
$employee->setDepartment('testing');
$em->persist($employee);
$em->flush();
Now lets run a simple query to retrieve the `Employee` we just created:
[sql]
SELECT e FROM Entities\Employee e WHERE e.name = 'test'
If we check the generated SQL you will notice it has some special conditions added to
ensure that we will only get back `Employee` entities:
[sql]
SELECT p0_.id AS id0, p0_.name AS name1, p0_.department AS department2, p0_.discr AS discr3 FROM Person p0_ WHERE (p0_.name = ?) AND p0_.discr IN ('employee')
+++ Class Table Inheritance
[Class Table Inheritance](http://martinfowler.com/eaaCatalog/classTableInheritance.html) is an inheritance mapping strategy where each class in a hierarchy is mapped to several tables: its own table and the tables of all parent classes. The table of a child class is linked to the table of a parent class through a foreign key constraint.
Doctrine 2 implements this strategy through the use of a discriminator column in the topmost table of the hierarchy because this is the easiest way to achieve polymorphic queries with Class Table Inheritance.
The example for class table inheritance is the same as single table, you just need to
change the inheritance type from `SINGLE_TABLE` to `JOINED`:
[php]
/**
* @Entity
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
*/
class Person
{
// ...
}
Now take a look at the SQL which is generated to create the table, you'll notice some
differences:
[sql]
CREATE TABLE Person (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(50) NOT NULL, discr VARCHAR(255) NOT NULL, PRIMARY KEY(id)) ENGINE = InnoDB;
CREATE TABLE Employee (id INT NOT NULL, department VARCHAR(50) NOT NULL, PRIMARY KEY(id)) ENGINE = InnoDB;
ALTER TABLE Employee ADD FOREIGN KEY (id) REFERENCES Person(id) ON DELETE CASCADE
* The data is split between two tables
* A foreign key exists between the two tables
Now if were to insert the same `Employee` as we did in the `SINGLE_TABLE` example and run
the same example query it will generate different SQL joining the `Person` information
automatically for you:
[sql]
SELECT p0_.id AS id0, p0_.name AS name1, e1_.department AS department2, p0_.discr AS discr3 FROM Employee e1_ INNER JOIN Person p0_ ON e1_.id = p0_.id WHERE p0_.name = ?
++ The Query class
An instance of the `Doctrine\ORM\Query` class represents a DQL query. You create a Query instance be calling `EntityManager#createQuery($dql)`, passing the DQL query string. Alternatively you can create an empty `Query` instance and invoke `Query#setDql($dql)` afterwards. Here are some examples:

View File

@ -97,7 +97,7 @@ This strategy is very efficient for querying across all types in the hierarchy o
++ Class Table Inheritance
[Class Table Inheritance](http://martinfowler.com/eaaCatalog/classTableInheritance.html) is an inheritance mapping strategy where each class in a hierarchy is mapped to several tables: its own table and the tables of all parent classes. The table of a child class is linked to the table of a parent class through a foreign key constraint.
Doctrine 2 implements this strategy through the use of a discriminator column in the topmost table of the hieararchy because this is the easiest way to achieve polymorphic queries with Class Table Inheritance.
Doctrine 2 implements this strategy through the use of a discriminator column in the topmost table of the hierarchy because this is the easiest way to achieve polymorphic queries with Class Table Inheritance.
Example:

View File

@ -227,7 +227,7 @@ Now you can get an exporter instance and export the loaded metadata to yml:
[php]
$exporter = $cme->getExporter('yml', '/path/to/export/yml');
$exporter->setMetadata($metadatas);
$exporter->setMetadata($metadata);
$exporter->export();
You can also reverse engineer a database using the `orm:convert-mapping` command:

View File

@ -1,4 +1,3 @@
++ Understanding
In this chapter we will help you understand the `EntityManager` and the `UnitOfWork`.
A Unit of Work is similar to an object-level transaction. A new Unit of Work is
@ -8,49 +7,7 @@ implicity started when an EntityManager is initially created or after
A Unit of Work can be manually closed by calling EntityManager#close(). Any
changes to objects within this Unit of Work that have not yet been persisted
are lost.
+++ The size of a Unit of Work
The size of a Unit of Work mainly refers to the number of managed entities at
a particular point in time.
+++ The cost of flush()
How costly a flush operation is in terms of performance mainly depends on 2 factors:
* The size of your current Unit of Work
* The configured change tracking policies
You can get the size of your Unit of Work as follows:
[php]
$uowSize = $em->getUnitOfWork()->size();
The size represents the number of managed entities in the Unit of Work. This
size affects the performance of flush() operations due to change tracking
(see "Change Tracking Policies") and, of course, memory consumption, so you
may want to check it from time to time during development.
> **CAUTION**
> Do not invoke `flush` after every change to an entity or every single invocation of
> persist/remove/merge/... This is an anti-pattern and unnecessarily reduces the
> performance of your application. Instead, form units of work that operate on your objects
> and call `flush` when you are done. While serving a single HTTP request there should
> be usually no need for invoking `flush` more than 0-2 times.
+++ Direct access to a Unit of Work
You can get direct access to the Unit of Work by calling `EntityManager#getUnitOfWork()`.
This will return the UnitOfWork instance the EntityManager is currently using.
[php]
$uow = $em->getUnitOfWork();
> **NOTE**
> Directly manipulating a UnitOfWork is not recommended. When working directly with the
> UnitOfWork API, respect methods marked as INTERNAL by not using them and carefully read
> the API documentation.
are lost.
++ Persisting entities
@ -79,7 +36,7 @@ Example:
> **CAUTION**
> Generated entity identifiers / primary keys are guaranteed to be available after the
> next successful flush operation that involves the entity in question.
> You can not reply on a generated identifier to be available directly after invoking `persist`.
> You can not rely on a generated identifier to be available directly after invoking `persist`.
> The inverse is also true. You can not rely on a generated identifier being not available
> after a failed flush operation.
@ -88,15 +45,12 @@ The semantics of the persist operation, applied on an entity X, are as follows:
* If X is a new entity, it becomes managed. The entity X will be entered into the database as a result of the flush operation.
* If X is a preexisting managed entity, it is ignored by the persist operation. However, the persist operation is cascaded to entities referenced by X, if the relationships from X to these other entities are mapped with cascade=PERSIST or cascade=ALL (see "Transitive Persistence").
* If X is a removed entity, it becomes managed.
* If X is a detached entity, the behavior is undefined.
> **CAUTION**
> Do not pass detached entities to the persist operation.
* If X is a detached entity, an exception will be thrown on flush.
++ Removing entities
An entity can be removed from persistent storage by passing it to the `EntityManager#remove($entity)` method. By applying the `remove` operation on some entity, that entity becomes REMOVED, which means that its persistent state will be deleted once `EntityManager#flush()` is invoked. The in-memory state of an entity is unaffected by the `remove` operation.
An entity can be removed from persistent storage by passing it to the `EntityManager#remove($entity)` method. By applying the `remove` operation on some entity, that entity becomes REMOVED, which means that its persistent state will be deleted once `EntityManager#flush()` is invoked.
> **CAUTION**
> Just like `persist`, invoking `remove` on an entity does NOT cause an immediate SQL
@ -115,7 +69,9 @@ The semantics of the remove operation, applied to an entity X are as follows:
* If X is a managed entity, the remove operation causes it to become removed. The remove operation is cascaded to entities referenced by X, if the relationships from X to these other entities is mapped with cascade=REMOVE or cascade=ALL (see "Transitive Persistence").
* If X is a detached entity, an InvalidArgumentException will be thrown.
* If X is a removed entity, it is ignored by the remove operation.
* A removed entity X will be removed from the database as a result of the flush operation.
* A removed entity X will be removed from the database as a result of the flush operation.
After an entity has been removed its in-memory state is the same as before the removal, except for generated identifiers.
Removing an entity will also automatically delete any exisiting records in many-to-many
join tables that link this entity. The action taken depends on the value of the `@joinColumn`
@ -280,6 +236,15 @@ that has O(n) complexity, where n is the size of the map.
Since Doctrine always only looks at the owning side of a bidirectional association, it is essentially not necessary that an inverse collection of a bidirectional one-to-many or many-to-many association is updated. This knowledge can often be used to improve performance by avoiding the loading of the inverse collection.
> **NOTE*
>
> You can also clear the contents of a whole collection using the `Collections::clear()` method. You
> should be aware that using this method can lead to a straight and optimized database delete or update call
> during the flush operation that is not aware of entities that have been re-added to the collection.
>
> Say you clear a collection of tags by calling `$post->getTags()->clear();` and then call
> `$post->getTags()->add($tag)`. This will not recognize tag being already added before and issue
> two database calls.
++ Association Management Methods
@ -355,6 +320,107 @@ Do not blindly apply cascade=all to all associations as it will unnecessarily
degrade the performance of your application.
++ Synchronization with the Database
The state of persistent entities is synchronized with the database on flush of an `EntityManager`
which commits the underlying `UnitOfWork`. The synchronization involves writing any updates to
persistent entities and their relationships to the database. Thereby bidirectional relationships
are persisted based on the references held by the owning side of the relationship as explained
in the Association Mapping chapter.
The flush operation applies to a managed entity with the following semantics:
* The entity itself is synchronized to the database, if it has changed.
For all (initialized) relationships of the entity the following semantics apply to each
associated entity X:
* If X is new and persist operations are configured to cascade on the relationship,
X will be persisted.
* If X is new and no persist operations are configured to cascade on the relationship,
an exception will be thrown as this indicates a programming error.
* If X is removed and persist operations are configured to cascade on the relationship,
an exception will be thrown as this indicates a programming error (X would be re-persisted by the cascade).
* If X is detached and persist operations are configured to cascade on the relationship,
an exception will be thrown (This is semantically the same as passing X to persist()).
The flush operation applies to a removed entity by deleting its persistent state from the database.
No cascade options are relevant for removed entities on flush.
+++ The size of a Unit of Work
The size of a Unit of Work mainly refers to the number of managed entities at
a particular point in time.
+++ The cost of flushing
How costly a flush operation is, mainly depends on two factors:
* The size of the EntityManager's current UnitOfWork.
* The configured change tracking policies
You can get the size of a UnitOfWork as follows:
[php]
$uowSize = $em->getUnitOfWork()->size();
The size represents the number of managed entities in the Unit of Work. This
size affects the performance of flush() operations due to change tracking
(see "Change Tracking Policies") and, of course, memory consumption, so you
may want to check it from time to time during development.
> **CAUTION**
> Do not invoke `flush` after every change to an entity or every single invocation of
> persist/remove/merge/... This is an anti-pattern and unnecessarily reduces the
> performance of your application. Instead, form units of work that operate on your objects
> and call `flush` when you are done. While serving a single HTTP request there should
> be usually no need for invoking `flush` more than 0-2 times.
+++ Direct access to a Unit of Work
You can get direct access to the Unit of Work by calling `EntityManager#getUnitOfWork()`.
This will return the UnitOfWork instance the EntityManager is currently using.
[php]
$uow = $em->getUnitOfWork();
> **NOTE**
> Directly manipulating a UnitOfWork is not recommended. When working directly with the
> UnitOfWork API, respect methods marked as INTERNAL by not using them and carefully read
> the API documentation.
+++ Entity State
As outlined in the architecture overview an entity can be in one of four possible states:
NEW, MANAGED, REMOVED, DETACHED. If you explicitly need to find out what the current state
of an entity is in the context of a certain `EntityManager` you can ask the underlying
`UnitOfWork`:
[php]
switch ($em->getUnitOfWork()->getEntityState($entity)) {
case UnitOfWork::MANAGED:
...
case UnitOfWork::REMOVED:
...
case UnitOfWork::DETACHED:
...
case UnitOfWork::NEW:
...
}
An entity is in MANAGED state if it is associated with an `EntityManager` and it is not REMOVED.
An entity is in REMOVED state after it has been passed to `EntityManager#remove()` until the
next flush operation of the same EntityManager. A REMOVED entity is still associated with an
`EntityManager` until the next flush operation.
An entity is in DETACHED state if it has persistent state and identity but is currently not
associated with an `EntityManager`.
An entity is in NEW state if has no persistent state and identity and is not associated with an
`EntityManager` (for example those just created via the "new" operator).
++ Querying
Doctrine 2 provides the following ways, in increasing level of power and flexibility, to query for persistent objects. You should always start with the simplest one that suits your needs.

View File

@ -27,7 +27,7 @@ It is recommended to put all XML mapping documents in a single folder but you ca
[php]
// $config instanceof Doctrine\ORM\Configuration
$driver = new XmlDriver(array('/path/to/files'));
$driver = new \Doctrine\ORM\Mapping\Driver\XmlDriver(array('/path/to/files'));
$config->setMetadataDriverImpl($driver);