123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850 |
- Working with Objects
- ====================
- 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 implicitly started
- when an EntityManager is initially created or after
- ``EntityManager#flush()`` has been invoked. A Unit of Work is
- committed (and a new one started) by invoking
- ``EntityManager#flush()``.
- 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.
- .. note::
- It is very important to understand that only
- ``EntityManager#flush()`` ever causes write operations against the
- database to be executed. Any other methods such as
- ``EntityManager#persist($entity)`` or
- ``EntityManager#remove($entity)`` only notify the UnitOfWork to
- perform these operations during flush.
- Not calling ``EntityManager#flush()`` will lead to all changes
- during that request being lost.
- Entities and the Identity Map
- -----------------------------
- Entities are objects with identity. Their identity has a conceptual
- meaning inside your domain. In a CMS application each article has a
- unique id. You can uniquely identify each article by that id.
- Take the following example, where you find an article with the
- headline "Hello World" with the ID 1234:
- .. code-block:: php
- <?php
- $article = $entityManager->find('CMS\Article', 1234);
- $article->setHeadline('Hello World dude!');
-
- $article2 = $entityManager->find('CMS\Article', 1234);
- echo $article2->getHeadline();
- In this case the Article is accessed from the entity manager twice,
- but modified in between. Doctrine 2 realizes this and will only
- ever give you access to one instance of the Article with ID 1234,
- no matter how often do you retrieve it from the EntityManager and
- even no matter what kind of Query method you are using (find,
- Repository Finder or DQL). This is called "Identity Map" pattern,
- which means Doctrine keeps a map of each entity and ids that have
- been retrieved per PHP request and keeps returning you the same
- instances.
- In the previous example the echo prints "Hello World dude!" to the
- screen. You can even verify that ``$article`` and ``$article2`` are
- indeed pointing to the same instance by running the following
- code:
- .. code-block:: php
- <?php
- if ($article === $article2) {
- echo "Yes we are the same!";
- }
- Sometimes you want to clear the identity map of an EntityManager to
- start over. We use this regularly in our unit-tests to enforce
- loading objects from the database again instead of serving them
- from the identity map. You can call ``EntityManager#clear()`` to
- achieve this result.
- Entity Object Graph Traversal
- -----------------------------
- Although Doctrine allows for a complete separation of your domain
- model (Entity classes) there will never be a situation where
- objects are "missing" when traversing associations. You can walk
- all the associations inside your entity models as deep as you
- want.
- Take the following example of a single ``Article`` entity fetched
- from newly opened EntityManager.
- .. code-block:: php
- <?php
- /** @Entity */
- class Article
- {
- /** @Id @Column(type="integer") @GeneratedValue */
- private $id;
-
- /** @Column(type="string") */
- private $headline;
-
- /** @ManyToOne(targetEntity="User") */
- private $author;
-
- /** @OneToMany(targetEntity="Comment", mappedBy="article") */
- private $comments;
-
- public function __construct {
- $this->comments = new ArrayCollection();
- }
-
- public function getAuthor() { return $this->author; }
- public function getComments() { return $this->comments; }
- }
-
- $article = $em->find('Article', 1);
- This code only retrieves the ``Article`` instance with id 1 executing
- a single SELECT statement against the user table in the database.
- You can still access the associated properties author and comments
- and the associated objects they contain.
- This works by utilizing the lazy loading pattern. Instead of
- passing you back a real Author instance and a collection of
- comments Doctrine will create proxy instances for you. Only if you
- access these proxies for the first time they will go through the
- EntityManager and load their state from the database.
- This lazy-loading process happens behind the scenes, hidden from
- your code. See the following code:
- .. code-block:: php
- <?php
- $article = $em->find('Article', 1);
-
- // accessing a method of the user instance triggers the lazy-load
- echo "Author: " . $article->getAuthor()->getName() . "\n";
-
- // Lazy Loading Proxies pass instanceof tests:
- if ($article->getAuthor() instanceof User) {
- // a User Proxy is a generated "UserProxy" class
- }
-
- // accessing the comments as an iterator triggers the lazy-load
- // retrieving ALL the comments of this article from the database
- // using a single SELECT statement
- foreach ($article->getComments() AS $comment) {
- echo $comment->getText() . "\n\n";
- }
-
- // Article::$comments passes instanceof tests for the Collection interface
- // But it will NOT pass for the ArrayCollection interface
- if ($article->getComments() instanceof \Doctrine\Common\Collections\Collection) {
- echo "This will always be true!";
- }
- A slice of the generated proxy classes code looks like the
- following piece of code. A real proxy class override ALL public
- methods along the lines of the ``getName()`` method shown below:
- .. code-block:: php
- <?php
- class UserProxy extends User implements Proxy
- {
- private function _load()
- {
- // lazy loading code
- }
-
- public function getName()
- {
- $this->_load();
- return parent::getName();
- }
- // .. other public methods of User
- }
- .. warning::
- Traversing the object graph for parts that are lazy-loaded will
- easily trigger lots of SQL queries and will perform badly if used
- to heavily. Make sure to use DQL to fetch-join all the parts of the
- object-graph that you need as efficiently as possible.
- Persisting entities
- -------------------
- An entity can be made persistent by passing it to the
- ``EntityManager#persist($entity)`` method. By applying the persist
- operation on some entity, that entity becomes MANAGED, which means
- that its persistence is from now on managed by an EntityManager. As
- a result the persistent state of such an entity will subsequently
- be properly synchronized with the database when
- ``EntityManager#flush()`` is invoked.
- .. note::
- Invoking the ``persist`` method on an entity does NOT
- cause an immediate SQL INSERT to be issued on the database.
- Doctrine applies a strategy called "transactional write-behind",
- which means that it will delay most SQL commands until
- ``EntityManager#flush()`` is invoked which will then issue all
- necessary SQL statements to synchronize your objects with the
- database in the most efficient way and a single, short transaction,
- taking care of maintaining referential integrity.
- Example:
- .. code-block:: php
- <?php
- $user = new User;
- $user->setName('Mr.Right');
- $em->persist($user);
- $em->flush();
- .. note::
- 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 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.
- 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, 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.
- .. note::
- 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. This
- means that entities scheduled for removal can still be queried
- for and appear in query and collection results. See
- the section on :ref:`Database and UnitOfWork Out-Of-Sync <workingobjects_database_uow_outofsync>`
- for more information.
-
- Example:
- .. code-block:: php
- <?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.
- - 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 existing
- records in many-to-many join tables that link this entity. The
- action taken depends on the value of the ``@joinColumn`` mapping
- attribute "onDelete". Either Doctrine issues a dedicated ``DELETE``
- statement for records of each join table or it depends on the
- foreign key semantics of onDelete="CASCADE".
- Deleting an object with all its associated objects can be achieved
- in multiple ways with very different performance impacts.
- 1. If an association is marked as ``CASCADE=REMOVE`` Doctrine 2
- will fetch this association. If its a Single association it will
- pass this entity to
- ´EntityManager#remove()``. If the association is a collection, Doctrine will loop over all its elements and pass them to``EntityManager#remove()\`.
- In both cases the cascade remove semantics are applied recursively.
- For large object graphs this removal strategy can be very costly.
- 2. Using a DQL ``DELETE`` statement allows you to delete multiple
- entities of a type with a single command and without hydrating
- these entities. This can be very efficient to delete large object
- graphs from the database.
- 3. Using foreign key semantics ``onDelete="CASCADE"`` can force the
- database to remove all associated objects internally. This strategy
- is a bit tricky to get right but can be very powerful and fast. You
- should be aware however that using strategy 1 (``CASCADE=REMOVE``)
- completely by-passes any foreign key ``onDelete=CASCADE`` option,
- because Doctrine will fetch and remove all associated entities
- explicitly nevertheless.
- Detaching entities
- ------------------
- 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:
- .. code-block:: php
- <?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:
- .. code-block:: php
- <?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.
- .. note::
- When you want to serialize/unserialize entities you
- have to make all entity properties protected, never private. The
- reason for this is, if you serialize a class that was a proxy
- instance before, the private variables won't be serialized and a
- PHP Notice is thrown.
- 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.
- - If X is a new entity instance, a new managed copy X' will be
- created and the state of X is copied onto this managed instance.
- - 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 don't 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.
- .. warning::
- If you need to perform multiple merges of entities that share certain subparts
- of their object-graphs and cascade merge, then you have to call ``EntityManager#clear()`` between the
- successive calls to ``EntityManager#merge()``. Otherwise you might end up with
- multiple copies of the "same" object in the database, however with different ids.
- .. 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.
- 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.
- When ``EntityManager#flush()`` is called, Doctrine inspects all
- managed, new and removed entities and will perform the following
- operations.
- .. _workingobjects_database_uow_outofsync:
- Effects of Database and UnitOfWork being Out-Of-Sync
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- As soon as you begin to change the state of entities, call persist or remove the
- contents of the UnitOfWork and the database will drive out of sync. They can
- only be synchronized by calling ``EntityManager#flush()``. This section
- describes the effects of database and UnitOfWork being out of sync.
- - Entities that are scheduled for removal can still be queried from the database.
- They are returned from DQL and Repository queries and are visible in collections.
- - Entities that are passed to ``EntityManager#persist`` do not turn up in query
- results.
- - Entities that have changed will not be overwritten with the state from the database.
- This is because the identity map will detect the construction of an already existing
- entity and assumes its the most up to date version.
- ``EntityManager#flush()`` is never called implicitly by Doctrine. You always have to trigger it manually.
- Synchronizing New and Managed Entities
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- The flush operation applies to a managed entity with the following
- semantics:
- - The entity itself is synchronized to the database using a SQL
- UPDATE statement, only if at least one persistent field has
- changed.
- - No SQL updates are executed if the entity did not change.
- The flush operation applies to a new entity with the following
- semantics:
- - The entity itself is synchronized to the database using a SQL
- INSERT statement.
- For all (initialized) relationships of the new or managed 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()).
- Synchronizing Removed Entities
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 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 cascade remove option is already
- executed during ``EntityManager#remove($entity)``.
- 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:
- .. code-block:: php
- <?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.
- .. note::
- 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.
- .. code-block:: php
- <?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``:
- .. code-block:: php
- <?php
- switch ($em->getUnitOfWork()->getEntityState($entity)) {
- case UnitOfWork::STATE_MANAGED:
- ...
- case UnitOfWork::STATE_REMOVED:
- ...
- case UnitOfWork::STATE_DETACHED:
- ...
- case UnitOfWork::STATE_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.
- 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:
- .. code-block:: php
- <?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:
- .. code-block:: php
- <?php
- // $em instanceof EntityManager
- $user = $em->getRepository('MyProject\Domain\User')->find($id);
- ``EntityManager#getRepository($entityName)`` returns a repository
- object which provides many ways to retrieve 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:
- .. code-block:: php
- <?php
- // $em instanceof EntityManager
-
- // All users that are 20 years old
- $users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20));
-
- // All users that are 20 years old and have a surname of 'Miller'
- $users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20, 'surname' => 'Miller'));
-
- // A single user by its nickname
- $user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb'));
- You can also load by owning side associations through the repository:
- .. code-block:: php
- <?php
- $number = $em->find('MyProject\Domain\Phonenumber', 1234);
- $user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('phone' => $number->getId()));
- Be careful that this only works by passing the ID of the associated entity, not yet by passing the associated entity itself.
- The ``EntityRepository#findBy()`` method additionally accepts orderings, limit and offset as second to fourth parameters:
- .. code-block:: php
- <?php
- $tenUsers = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20), array('name' => 'ASC'), 10, 0);
- If you pass an array of values Doctrine will convert the query into a WHERE field IN (..) query automatically:
- .. code-block:: php
- <?php
- $users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => array(20, 30, 40)));
- // translates roughly to: SELECT * FROM users WHERE age IN (20, 30, 40)
- An EntityRepository also provides a mechanism for more concise
- calls through its use of ``__call``. Thus, the following two
- examples are equivalent:
- .. code-block:: php
- <?php
- // A single user by its nickname
- $user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb'));
-
- // A single user by its nickname (__call magic)
- $user = $em->getRepository('MyProject\Domain\User')->findOneByNickname('romanb');
- By Criteria
- ~~~~~~~~~~~
- .. versionadded:: 2.3
- The Repository implement the ``Doctrine\Common\Collections\Selectable``
- interface. That means you can build ``Doctrine\Common\Collections\Criteria``
- and pass them to the ``matching($criteria)`` method.
- See the :ref:`Working with Associations: Filtering collections
- <filtering-collections>`.
- By Eager Loading
- ~~~~~~~~~~~~~~~~
- 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 familiar 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:
- .. code-block:: php
- <?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 class can be found in
- :doc:`the dedicated chapter <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
- :doc:`in Query Builder chapter <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
- :doc:`the dedicated chapter <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.
- .. code-block:: php
- <?php
- namespace MyDomain\Model;
-
- use Doctrine\ORM\EntityRepository;
-
- /**
- * @entity(repositoryClass="MyDomain\Model\UserRepository")
- */
- class User
- {
-
- }
-
- class UserRepository extends EntityRepository
- {
- public function getAllAdminUsers()
- {
- return $this->_em->createQuery('SELECT u FROM MyDomain\Model\User u WHERE u.status = "admin"')
- ->getResult();
- }
- }
- You can access your repository now by calling:
- .. code-block:: php
- <?php
- // $em instanceof EntityManager
-
- $admins = $em->getRepository('MyDomain\Model\User')->getAllAdminUsers();
|