* 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").
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.
> **CAUTION**
> Just like `persist`, invoking `remove` on an entity does NOT cause an immediate SQL
> DELETE to be issued on the database. The entity will be deleted on the next invocation
> of `EntityManager#flush()` that involves that entity.
Example:
[php]
$em->remove($user);
$em->flush();
The semantics of the remove operation, applied to an entity X are as follows:
* If X is a new entity, it is ignored by the remove operation. However, the remove operation is cascaded to entities referenced by X, if the relationship from X to these other entities is mapped with cascade=REMOVE or cascade=ALL (see "Transitive Persistence").
* 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.
An entity is detached from an EntityManager and thus no longer managed by
invoking the `EntityManager#detach($entity)` method on it or by cascading
the detach operation to it. Changes made to the detached entity, if any
(including removal of the entity), will not be synchronized to the database
after the entity has been detached.
Doctrine will not hold on to any references to a detached entity.
Example:
[php]
$em->detach($entity);
The semantics of the detach operation, applied to an entity X are as follows:
* If X is a managed entity, the detach operation causes it to become detached. The detach operation is cascaded to entities referenced by X, if the relationships from X to these other entities is mapped with cascade=DETACH or cascade=ALL (see "Transitive Persistence"). Entities which previously referenced X will continue to reference X.
* If X is a new or detached entity, it is ignored by the detach operation.
* If X is a removed entity, the detach operation is cascaded to entities referenced by X, if the relationships from X to these other entities is mapped with cascade=DETACH or cascade=ALL (see "Transitive Persistence"). Entities which previously referenced X will continue to reference X.
There are several situations in which an entity is detached automatically without invoking the `detach` method:
* When `EntityManager#clear()` is invoked, all entities that are currently managed by the EntityManager instance become detached.
* When serializing an entity. The entity retrieved upon subsequent unserialization will be detached (This is the case for all entities that are serialized and stored in some cache, i.e. when using the Query Result Cache).
The `detach` operation is usually not as frequently needed and used as `persist` and `remove`.
++ Merging entities
Merging entities refers to the merging of (usually detached) entities into the
context of an EntityManager so that they become managed again. To merge the
state of an entity into an EntityManager use the `EntityManager#merge($entity)`
method. The state of the passed entity will be merged into a managed copy of
this entity and this copy will subsequently be returned.
Example:
[php]
$detachedEntity = unserialize($serializedEntity); // some detached entity
$entity = $em->merge($detachedEntity);
// $entity now refers to the fully managed copy returned by the merge operation.
// The EntityManager $em now manages the persistence of $entity as usual.
The semantics of the merge operation, applied to an entity X, are as follows:
* If X is a detached entity, the state of X is copied onto a pre-existing managed entity instance X' of the same identity or a new managed copy X' of X is created.
* If X is a new entity instance, an InvalidArgumentException will be thrown.
* If X is a removed entity instance, an InvalidArgumentException will be thrown.
* If X is a managed entity, it is ignored by the merge operation, however, the merge operation is cascaded to entities referenced by relationships from X if these relationships have been mapped with the cascade element value MERGE or ALL (see "Transitive Persistence").
* For all entities Y referenced by relationships from X having the cascade element value
MERGE or ALL, Y is merged recursively as Y'. For all such Y referenced by X, X' is set to reference Y'. (Note that if X is managed then X is the same object as X'.)
* If X is an entity merged to X', with a reference to another entity Y, where cascade=MERGE or cascade=ALL is not specified, then navigation of the same association from X' yields a reference to a managed object Y' with the same persistent identity as Y.
The `merge` operation will throw an `OptimisticLockException` if the entity
being merged uses optimistic locking through a version field and the versions
of the entity being merged and the managed copy dont match. This usually means
that the entity has been modified while being detached.
The `merge` operation is usually not as frequently needed and used as `persist`
and `remove`. The most common scenario for the `merge` operation is to reattach
entities to an EntityManager that come from some cache (and are therefore detached)
and you want to modify and persist such an entity.
> **NOTE**
> If you load some detached entities from a cache and you do not need to persist or
> delete them or otherwise make use of them without the need for persistence services
> there is no need to use `merge`. I.e. you can simply pass detached objects from a cache
> directly to the view.
++ Associations
Associations between entities are represented just like in regular object-oriented PHP, with references to other objects or collections of objects. When it comes to persistence, it is important to understand three main things:
* The concept of owning and inverse sides in bidirectional associations as described [here](http://www.doctrine-project.org/documentation/manual/2_0/en/association-mapping#owning-side-and-inverse-side).
* A collection of entities always only represents the association to the containing entities. If an entity is removed from a collection, the association is removed, not the entity itself.
* Collection-valued persistent fields and properties must be defined in terms of the Doctrine\Common\Collections\Collection interface. [See here](http://www.doctrine-project.org/documentation/manual/2_0/en/architecture#entities:persistent-fields) for more details.
++ Establishing Associations
Establishing an association between two entities is straight-forward. Here are some examples:
[php]
// Article <- one-to-many -> Comment
$article->getComments()->add($comment);
$comment->setArticle($article);
// User <- many-to-many -> Groups
$user->getGroups()->add($group);
$group->getUsers()->add($user);
// User <- one-to-one -> Address
$user->setAddress($address);
$address->setUser($user);
Notice how always both sides of the bidirectional association are updated. Unidirectional associations are consequently simpler to handle.
++ Removing Associations
Removing an association between two entities is similarly straight-forward. There are two strategies
to do so, by key and by element. Here are some examples:
[php]
// Remove by Elements
// Article <- one-to-many -> Comment
$article->getComments()->removeElement($comment);
$comment->setArticle(null);
// User <- many-to-many -> Group
$user->getGroups()->removeElement($group);
$group->getUsers()->removeElement($user);
// Remove by key
$article->getComments()->remove($ithComment);
$comment->setArticle(null);
// User <- one-to-one -> Address
$user->setAddress(null);
$address->setUser(null);
Notice how both sides of the bidirectional association are always updated. Unidirectional associations are consequently simpler to handle. Also note that if you type-hint your methods, i.e. `setAddress(Address $address)`, then PHP does not allow null values and setAddress(null) will fail for removing the association. If you insist on type-hinting a typical way to deal with this is to provide a special method, like `removeAddress()`. This can also provide better encapsulation as it hides the internal meaning of not having an address.
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.
++ Association Management Methods
It is generally a good idea to encapsulate proper association management inside the entity classes. This makes it easier to use the class correctly and can encapsulate details about how the association is maintained.
The following code shows a simple, idiomatic example for a bidirectional one-to-many association between an Article and its Comments.
[php]
// Mappings not shown.
class Article {
// The comments of the article.
private $comments;
// ... constructor omitted ...
public function addComment(Comment $comment) {
$this->comments->add($comment);
$comment->setArticle($this);
}
public function getComments() {
return $this->comments;
}
}
class Comment {
// The article the comment refers to.
private $article;
// ... constructor omitted ...
public function setArticle($article) {
$this->article = $article;
}
public function getArticle() {
return $this->article;
}
}
With the above implementation, it is always ensured that at least the owning side from Doctrine's point of view (Comment) is properly updated. You will notice that `setArticle` does not call `addComment`, thus the bidirectional association is strictly-speaking still incomplete, if a user of the class only invokes `setArticle`. If you naively call `addComment` in `setArticle`, however, you end up with an infinite loop, so more work is needed. As you can see, proper bidirectional association management in plain OOP is a non-trivial task and encapsulating all the details inside the classes can be challenging.
There is no single, best way for association management. It greatly depends on the requirements of your concrete domain model as well as your preferences.
++ Transitive persistence
Persisting, removing, detaching and merging individual entities can become pretty
cumbersome, especially when a larger object graph with collections is involved.
Therefore Doctrine 2 provides a mechanism for transitive persistence through
cascading of these operations. Each association to another entity or a collection
of entities can be configured to automatically cascade certain operations. By
default, no operations are cascaded.
The following cascade options exist:
* persist : Cascades persist operations to the associated entities.
* remove : Cascades remove operations to the associated entities.
* merge : Cascades merge operations to the associated entities.
* detach : Cascades detach operations to the associated entities.
* all : Cascades persist, remove, merge and detach operations to associated entities.
The following example shows an association to a number of addresses. If persist()
or remove() is invoked on any User entity, it will be cascaded to all associated
Even though automatic cascading is convenient it should be used with care.
Do not blindly apply cascade=all to all associations as it will unnecessarily
degrade the performance of your application.
++ 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.
+++ By Primary Key
The most basic way to query for a persistent object is by its identifier / primary key using the `EntityManager#find($entityName, $id)` method. Here is an example:
[php]
// $em instanceof EntityManager
$user = $em->find('MyProject\Domain\User', $id);
The return value is either the found entity instance or null if no instance could be found with the given identifier.
Essentially, `EntityManager#find()` is just a shortcut for the following:
`EntityManager#getRepository($entityName)` returns a repository object which provides many ways to retreive entities of the specified type. By default, the repository instance is of type `Doctrine\ORM\EntityRepository`. You can also use custom repository classes as shown later.
+++ By Simple Conditions
To query for one or more entities based on several conditions that form a logical conjunction, use the `findBy` and `findOneBy` methods on a repository as follows:
Whenever you query for an entity that has persistent associations and these associations are mapped as EAGER, they will automatically be loaded together with the entity being queried and is thus immediately available to your application.
+++ By Lazy Loading
Whenever you have a managed entity instance at hand, you can traverse and use any associations of that entity that are configured LAZY as if they were in-memory already. Doctrine will automatically load the associated objects on demand through the concept of lazy-loading.
+++ By DQL
The most powerful and flexible method to query for persistent objects is the Doctrine Query Language, an object query language. DQL enables you to query for persistent objects in the language of objects. DQL understands classes, fields, inheritance and associations.
DQL is syntactically very similar to the familar SQL but *it is not SQL*.
A DQL query is represented by an instance of the `Doctrine\ORM\Query` class. You create a query using `EntityManager#createQuery($dql)`. Here is a simple example:
[php]
// $em instanceof EntityManager
// All users with an age between 20 and 30 (inclusive).
$q = $em->createQuery("select u from MyDomain\Model\User u where u.age >= 20 and u.age <= 30");
$users = $q->getResult();
Note that this query contains no knowledge about the relational schema, only about the object model. DQL supports positional as well as named parameters, many functions, (fetch) joins, aggregates, subqueries and much more. Detailed information about DQL and its syntax as well as the Doctrine\ORM\Query class can be found in [the dedicated chapter](http://www.doctrine-project.org/documentation/manual/2_0/en/dql-doctrine-query-language). For programmatically building up queries based on conditions that are only known at runtime, Doctrine provides the special `Doctrine\ORM\QueryBuilder` class. More information on constructing queries with a QueryBuilder can be found [in the dedicated chapter](http://www.doctrine-project.org/documentation/manual/2_0/en/query-builder).
+++ By Native Queries
As an alternative to DQL or as a fallback for special SQL statements native queries can be used.
Native queries are built by using a hand-crafted SQL query and a ResultSetMapping that describes
how the SQL result set should be transformed by Doctrine. More information about native queries
can be found in [the dedicated chapter](http://www.doctrine-project.org/documentation/manual/2_0/en/native-sql).
+++ Custom Repositories
By default the EntityManager returns a default implementation of `Doctrine\ORM\EntityRepository` when
you call `EntityManager#getRepository($entityClass)`. You can overwrite this behaviour by specifying
the class name of your own Entity Repository in the Annotation, XML or YAML metadata.
In large applications that require lots of specialized DQL queries using a custom repository is
one recommended way of grouping these queries in a central location.