Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 839 lines (626 sloc) 30.75 kb
a5a0dfa @beberlei Converted ORM Docs into ReST
beberlei authored
1 Working with Objects
2 ====================
3
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
4 In this chapter we will help you understand the ``EntityManager``
5 and the ``UnitOfWork``. A Unit of Work is similar to an
6 object-level transaction. A new Unit of Work is implicitly started
7 when an EntityManager is initially created or after
8 ``EntityManager#flush()`` has been invoked. A Unit of Work is
9 committed (and a new one started) by invoking
10 ``EntityManager#flush()``.
11
12 A Unit of Work can be manually closed by calling
13 EntityManager#close(). Any changes to objects within this Unit of
14 Work that have not yet been persisted are lost.
15
a5a0dfa @beberlei Converted ORM Docs into ReST
beberlei authored
16 .. note::
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
17
18 It is very important to understand that only
19 ``EntityManager#flush()`` ever causes write operations against the
20 database to be executed. Any other methods such as
21 ``EntityManager#persist($entity)`` or
22 ``EntityManager#remove($entity)`` only notify the UnitOfWork to
23 perform these operations during flush.
24
25 Not calling ``EntityManager#flush()`` will lead to all changes
26 during that request being lost.
27
28
29 Entities and the Identity Map
30 -----------------------------
31
32 Entities are objects with identity. Their identity has a conceptual
33 meaning inside your domain. In a CMS application each article has a
34 unique id. You can uniquely identify each article by that id.
35
36 Take the following example, where you find an article with the
37 headline "Hello World" with the ID 1234:
38
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
39 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
40
41 <?php
42 $article = $entityManager->find('CMS\Article', 1234);
43 $article->setHeadline('Hello World dude!');
44
45 $article2 = $entityManager->find('CMS\Article', 1234);
46 echo $article2->getHeadline();
47
48 In this case the Article is accessed from the entity manager twice,
49 but modified in between. Doctrine 2 realizes this and will only
50 ever give you access to one instance of the Article with ID 1234,
51 no matter how often do you retrieve it from the EntityManager and
52 even no matter what kind of Query method you are using (find,
53 Repository Finder or DQL). This is called "Identity Map" pattern,
54 which means Doctrine keeps a map of each entity and ids that have
55 been retrieved per PHP request and keeps returning you the same
56 instances.
57
58 In the previous example the echo prints "Hello World dude!" to the
59 screen. You can even verify that ``$article`` and ``$article2`` are
60 indeed pointing to the same instance by running the following
61 code:
62
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
63 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
64
65 <?php
66 if ($article === $article2) {
67 echo "Yes we are the same!";
68 }
69
70 Sometimes you want to clear the identity map of an EntityManager to
71 start over. We use this regularly in our unit-tests to enforce
72 loading objects from the database again instead of serving them
73 from the identity map. You can call ``EntityManager#clear()`` to
74 achieve this result.
75
76 Entity Object Graph Traversal
77 -----------------------------
78
79 Although Doctrine allows for a complete separation of your domain
80 model (Entity classes) there will never be a situation where
81 objects are "missing" when traversing associations. You can walk
82 all the associations inside your entity models as deep as you
83 want.
84
85 Take the following example of a single ``Article`` entity fetched
86 from newly opened EntityManager.
87
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
88 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
89
90 <?php
91 /** @Entity */
92 class Article
93 {
94 /** @Id @Column(type="integer") @GeneratedValue */
95 private $id;
96
97 /** @Column(type="string") */
98 private $headline;
99
100 /** @ManyToOne(targetEntity="User") */
101 private $author;
102
103 /** @OneToMany(targetEntity="Comment", mappedBy="article") */
104 private $comments;
105
106 public function __construct {
107 $this->comments = new ArrayCollection();
108 }
109
110 public function getAuthor() { return $this->author; }
111 public function getComments() { return $this->comments; }
112 }
113
114 $article = $em->find('Article', 1);
115
c89290e @rrehbeindoi Corrected mismatch between example and comment entities.
rrehbeindoi authored
116 This code only retrieves the ``Article`` instance with id 1 executing
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
117 a single SELECT statement against the user table in the database.
118 You can still access the associated properties author and comments
119 and the associated objects they contain.
120
121 This works by utilizing the lazy loading pattern. Instead of
122 passing you back a real Author instance and a collection of
123 comments Doctrine will create proxy instances for you. Only if you
124 access these proxies for the first time they will go through the
125 EntityManager and load their state from the database.
126
127 This lazy-loading process happens behind the scenes, hidden from
128 your code. See the following code:
129
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
130 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
131
132 <?php
133 $article = $em->find('Article', 1);
134
135 // accessing a method of the user instance triggers the lazy-load
136 echo "Author: " . $article->getAuthor()->getName() . "\n";
137
138 // Lazy Loading Proxies pass instanceof tests:
139 if ($article->getAuthor() instanceof User) {
140 // a User Proxy is a generated "UserProxy" class
141 }
142
143 // accessing the comments as an iterator triggers the lazy-load
144 // retrieving ALL the comments of this article from the database
145 // using a single SELECT statement
146 foreach ($article->getComments() AS $comment) {
147 echo $comment->getText() . "\n\n";
148 }
149
150 // Article::$comments passes instanceof tests for the Collection interface
151 // But it will NOT pass for the ArrayCollection interface
152 if ($article->getComments() instanceof \Doctrine\Common\Collections\Collection) {
153 echo "This will always be true!";
154 }
155
156 A slice of the generated proxy classes code looks like the
157 following piece of code. A real proxy class override ALL public
158 methods along the lines of the ``getName()`` method shown below:
159
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
160 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
161
162 <?php
163 class UserProxy extends User implements Proxy
164 {
165 private function _load()
166 {
167 // lazy loading code
168 }
169
170 public function getName()
171 {
172 $this->_load();
173 return parent::getName();
174 }
175 // .. other public methods of User
176 }
177
a5a0dfa @beberlei Converted ORM Docs into ReST
beberlei authored
178 .. warning::
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
179
180 Traversing the object graph for parts that are lazy-loaded will
181 easily trigger lots of SQL queries and will perform badly if used
182 to heavily. Make sure to use DQL to fetch-join all the parts of the
183 object-graph that you need as efficiently as possible.
184
185
186 Persisting entities
187 -------------------
188
189 An entity can be made persistent by passing it to the
190 ``EntityManager#persist($entity)`` method. By applying the persist
191 operation on some entity, that entity becomes MANAGED, which means
192 that its persistence is from now on managed by an EntityManager. As
193 a result the persistent state of such an entity will subsequently
194 be properly synchronized with the database when
195 ``EntityManager#flush()`` is invoked.
196
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
197 .. note::
a5a0dfa @beberlei Converted ORM Docs into ReST
beberlei authored
198
199 Invoking the ``persist`` method on an entity does NOT
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
200 cause an immediate SQL INSERT to be issued on the database.
201 Doctrine applies a strategy called "transactional write-behind",
202 which means that it will delay most SQL commands until
203 ``EntityManager#flush()`` is invoked which will then issue all
204 necessary SQL statements to synchronize your objects with the
205 database in the most efficient way and a single, short transaction,
206 taking care of maintaining referential integrity.
207
208
209 Example:
210
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
211 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
212
213 <?php
214 $user = new User;
215 $user->setName('Mr.Right');
216 $em->persist($user);
217 $em->flush();
218
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
219 .. note::
a5a0dfa @beberlei Converted ORM Docs into ReST
beberlei authored
220
221 Generated entity identifiers / primary keys are
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
222 guaranteed to be available after the next successful flush
223 operation that involves the entity in question. You can not rely on
224 a generated identifier to be available directly after invoking
225 ``persist``. The inverse is also true. You can not rely on a
226 generated identifier being not available after a failed flush
227 operation.
228
229
230 The semantics of the persist operation, applied on an entity X, are
231 as follows:
232
233
234 - If X is a new entity, it becomes managed. The entity X will be
235 entered into the database as a result of the flush operation.
236 - If X is a preexisting managed entity, it is ignored by the
237 persist operation. However, the persist operation is cascaded to
238 entities referenced by X, if the relationships from X to these
239 other entities are mapped with cascade=PERSIST or cascade=ALL (see
240 "Transitive Persistence").
241 - If X is a removed entity, it becomes managed.
242 - If X is a detached entity, an exception will be thrown on
243 flush.
244
245 Removing entities
246 -----------------
247
248 An entity can be removed from persistent storage by passing it to
249 the ``EntityManager#remove($entity)`` method. By applying the
250 ``remove`` operation on some entity, that entity becomes REMOVED,
251 which means that its persistent state will be deleted once
252 ``EntityManager#flush()`` is invoked.
253
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
254 .. note::
a5a0dfa @beberlei Converted ORM Docs into ReST
beberlei authored
255
256 Just like ``persist``, invoking ``remove`` on an entity
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
257 does NOT cause an immediate SQL DELETE to be issued on the
258 database. The entity will be deleted on the next invocation of
eb0fd4d @beberlei Add section about database and unit of work being out of sync and how…
beberlei authored
259 ``EntityManager#flush()`` that involves that entity. This
260 means that entities scheduled for removal can still be queried
261 for and appear in query and collection results. See
262 the section on :ref:`Database and UnitOfWork Out-Of-Sync <workingobjects_database_uow_outofsync>`
263 for more information.
264
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
265
266 Example:
267
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
268 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
269
270 <?php
271 $em->remove($user);
272 $em->flush();
273
274 The semantics of the remove operation, applied to an entity X are
275 as follows:
276
277
278 - If X is a new entity, it is ignored by the remove operation.
279 However, the remove operation is cascaded to entities referenced by
280 X, if the relationship from X to these other entities is mapped
281 with cascade=REMOVE or cascade=ALL (see "Transitive Persistence").
282 - If X is a managed entity, the remove operation causes it to
283 become removed. The remove operation is cascaded to entities
284 referenced by X, if the relationships from X to these other
285 entities is mapped with cascade=REMOVE or cascade=ALL (see
286 "Transitive Persistence").
287 - If X is a detached entity, an InvalidArgumentException will be
288 thrown.
289 - If X is a removed entity, it is ignored by the remove operation.
290 - A removed entity X will be removed from the database as a result
291 of the flush operation.
292
293 After an entity has been removed its in-memory state is the same as
294 before the removal, except for generated identifiers.
295
296 Removing an entity will also automatically delete any existing
297 records in many-to-many join tables that link this entity. The
298 action taken depends on the value of the ``@joinColumn`` mapping
299 attribute "onDelete". Either Doctrine issues a dedicated ``DELETE``
300 statement for records of each join table or it depends on the
301 foreign key semantics of onDelete="CASCADE".
302
303 Deleting an object with all its associated objects can be achieved
304 in multiple ways with very different performance impacts.
305
306
307 1. If an association is marked as ``CASCADE=REMOVE`` Doctrine 2
308 will fetch this association. If its a Single association it will
309 pass this entity to
310 ´EntityManager#remove()``. If the association is a collection, Doctrine will loop over all its elements and pass them to``EntityManager#remove()\`.
311 In both cases the cascade remove semantics are applied recursively.
312 For large object graphs this removal strategy can be very costly.
313 2. Using a DQL ``DELETE`` statement allows you to delete multiple
314 entities of a type with a single command and without hydrating
315 these entities. This can be very efficient to delete large object
316 graphs from the database.
317 3. Using foreign key semantics ``onDelete="CASCADE"`` can force the
318 database to remove all associated objects internally. This strategy
319 is a bit tricky to get right but can be very powerful and fast. You
320 should be aware however that using strategy 1 (``CASCADE=REMOVE``)
321 completely by-passes any foreign key ``onDelete=CASCADE`` option,
322 because Doctrine will fetch and remove all associated entities
323 explicitly nevertheless.
324
325 Detaching entities
326 ------------------
327
328 An entity is detached from an EntityManager and thus no longer
329 managed by invoking the ``EntityManager#detach($entity)`` method on
330 it or by cascading the detach operation to it. Changes made to the
331 detached entity, if any (including removal of the entity), will not
332 be synchronized to the database after the entity has been
333 detached.
334
335 Doctrine will not hold on to any references to a detached entity.
336
337 Example:
338
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
339 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
340
341 <?php
342 $em->detach($entity);
343
344 The semantics of the detach operation, applied to an entity X are
345 as follows:
346
347
348 - If X is a managed entity, the detach operation causes it to
349 become detached. The detach operation is cascaded to entities
350 referenced by X, if the relationships from X to these other
351 entities is mapped with cascade=DETACH or cascade=ALL (see
352 "Transitive Persistence"). Entities which previously referenced X
353 will continue to reference X.
354 - If X is a new or detached entity, it is ignored by the detach
355 operation.
356 - If X is a removed entity, the detach operation is cascaded to
357 entities referenced by X, if the relationships from X to these
358 other entities is mapped with cascade=DETACH or cascade=ALL (see
359 "Transitive Persistence"). Entities which previously referenced X
360 will continue to reference X.
361
362 There are several situations in which an entity is detached
363 automatically without invoking the ``detach`` method:
364
365
366 - When ``EntityManager#clear()`` is invoked, all entities that are
367 currently managed by the EntityManager instance become detached.
368 - When serializing an entity. The entity retrieved upon subsequent
369 unserialization will be detached (This is the case for all entities
370 that are serialized and stored in some cache, i.e. when using the
371 Query Result Cache).
372
373 The ``detach`` operation is usually not as frequently needed and
374 used as ``persist`` and ``remove``.
375
376 Merging entities
377 ----------------
378
379 Merging entities refers to the merging of (usually detached)
380 entities into the context of an EntityManager so that they become
381 managed again. To merge the state of an entity into an
382 EntityManager use the ``EntityManager#merge($entity)`` method. The
383 state of the passed entity will be merged into a managed copy of
384 this entity and this copy will subsequently be returned.
385
386 Example:
387
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
388 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
389
390 <?php
391 $detachedEntity = unserialize($serializedEntity); // some detached entity
392 $entity = $em->merge($detachedEntity);
393 // $entity now refers to the fully managed copy returned by the merge operation.
394 // The EntityManager $em now manages the persistence of $entity as usual.
395
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
396 .. note::
397
398 When you want to serialize/unserialize entities you
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
399 have to make all entity properties protected, never private. The
400 reason for this is, if you serialize a class that was a proxy
401 instance before, the private variables won't be serialized and a
402 PHP Notice is thrown.
403
404
405 The semantics of the merge operation, applied to an entity X, are
406 as follows:
407
408
409 - If X is a detached entity, the state of X is copied onto a
410 pre-existing managed entity instance X' of the same identity.
411 - If X is a new entity instance, a new managed copy X' will be
412 created and the state of X is copied onto this managed instance.
413 - If X is a removed entity instance, an InvalidArgumentException
414 will be thrown.
415 - If X is a managed entity, it is ignored by the merge operation,
416 however, the merge operation is cascaded to entities referenced by
417 relationships from X if these relationships have been mapped with
418 the cascade element value MERGE or ALL (see "Transitive
419 Persistence").
420 - For all entities Y referenced by relationships from X having the
421 cascade element value MERGE or ALL, Y is merged recursively as Y'.
422 For all such Y referenced by X, X' is set to reference Y'. (Note
423 that if X is managed then X is the same object as X'.)
424 - If X is an entity merged to X', with a reference to another
425 entity Y, where cascade=MERGE or cascade=ALL is not specified, then
426 navigation of the same association from X' yields a reference to a
427 managed object Y' with the same persistent identity as Y.
428
429 The ``merge`` operation will throw an ``OptimisticLockException``
430 if the entity being merged uses optimistic locking through a
431 version field and the versions of the entity being merged and the
432 managed copy don't match. This usually means that the entity has
433 been modified while being detached.
434
435 The ``merge`` operation is usually not as frequently needed and
436 used as ``persist`` and ``remove``. The most common scenario for
437 the ``merge`` operation is to reattach entities to an EntityManager
438 that come from some cache (and are therefore detached) and you want
439 to modify and persist such an entity.
440
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
441 .. warning::
442
443 If you need to perform multiple merges of entities that share certain subparts
444 of their object-graphs and cascade merge, then you have to call ``EntityManager#clear()`` between the
445 successive calls to ``EntityManager#merge()``. Otherwise you might end up with
446 multiple copies of the "same" object in the database, however with different ids.
447
a5a0dfa @beberlei Converted ORM Docs into ReST
beberlei authored
448 .. note::
449
450 If you load some detached entities from a cache and you do
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
451 not need to persist or delete them or otherwise make use of them
452 without the need for persistence services there is no need to use
453 ``merge``. I.e. you can simply pass detached objects from a cache
454 directly to the view.
455
456
457 Synchronization with the Database
458 ---------------------------------
459
460 The state of persistent entities is synchronized with the database
461 on flush of an ``EntityManager`` which commits the underlying
462 ``UnitOfWork``. The synchronization involves writing any updates to
463 persistent entities and their relationships to the database.
464 Thereby bidirectional relationships are persisted based on the
465 references held by the owning side of the relationship as explained
466 in the Association Mapping chapter.
467
468 When ``EntityManager#flush()`` is called, Doctrine inspects all
469 managed, new and removed entities and will perform the following
470 operations.
471
eb0fd4d @beberlei Add section about database and unit of work being out of sync and how…
beberlei authored
472 .. _workingobjects_database_uow_outofsync:
473
474 Effects of Database and UnitOfWork being Out-Of-Sync
475 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
476
477 As soon as you begin to change the state ofentities, call persist or remove the
478 contents of the UnitOfWork and the database will drive out of sync. They can
0252368 @beberlei Fix some Sphinx/ReST warnings
beberlei authored
479 only be sychronized by calling ``EntityManager#flush()``. This section
eb0fd4d @beberlei Add section about database and unit of work being out of sync and how…
beberlei authored
480 describes the effects of database and UnitOfWork being out of sync.
481
482 - Entities that are scheduled for removal can still be queried from the database.
483 They are returned from DQL and Repository queries and are visible in collections.
484 - Entities that are passed to ``EntityManager#persist`` do not turn up in query
485 results.
486 - Entities that have changed will not be overwritten with the state from the database.
487 This is because the identity map will detect the construction of an already existing
488 entity and assumes its the most up to date version.
489
490 ``EntityManager#flush()`` is never called implicitly by Doctrine. You always have to trigger it manually.
491
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
492 Synchronizing New and Managed Entities
493 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
494
495 The flush operation applies to a managed entity with the following
496 semantics:
497
498
499 - The entity itself is synchronized to the database using a SQL
500 UPDATE statement, only if at least one persistent field has
501 changed.
502 - No SQL updates are executed if the entity did not change.
503
504 The flush operation applies to a new entity with the following
505 semantics:
506
507
508 - The entity itself is synchronized to the database using a SQL
509 INSERT statement.
510
511 For all (initialized) relationships of the new or managed entity
512 the following semantics apply to each associated entity X:
513
514
515 - If X is new and persist operations are configured to cascade on
516 the relationship, X will be persisted.
517 - If X is new and no persist operations are configured to cascade
518 on the relationship, an exception will be thrown as this indicates
519 a programming error.
520 - If X is removed and persist operations are configured to cascade
521 on the relationship, an exception will be thrown as this indicates
522 a programming error (X would be re-persisted by the cascade).
523 - If X is detached and persist operations are configured to
524 cascade on the relationship, an exception will be thrown (This is
525 semantically the same as passing X to persist()).
526
527 Synchronizing Removed Entities
528 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
529
530 The flush operation applies to a removed entity by deleting its
531 persistent state from the database. No cascade options are relevant
532 for removed entities on flush, the cascade remove option is already
533 executed during ``EntityManager#remove($entity)``.
534
535 The size of a Unit of Work
536 ~~~~~~~~~~~~~~~~~~~~~~~~~~
537
538 The size of a Unit of Work mainly refers to the number of managed
539 entities at a particular point in time.
540
541 The cost of flushing
542 ~~~~~~~~~~~~~~~~~~~~
543
544 How costly a flush operation is, mainly depends on two factors:
545
546
547 - The size of the EntityManager's current UnitOfWork.
548 - The configured change tracking policies
549
550 You can get the size of a UnitOfWork as follows:
551
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
552 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
553
554 <?php
555 $uowSize = $em->getUnitOfWork()->size();
556
557 The size represents the number of managed entities in the Unit of
558 Work. This size affects the performance of flush() operations due
559 to change tracking (see "Change Tracking Policies") and, of course,
560 memory consumption, so you may want to check it from time to time
561 during development.
562
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
563 .. note::
a5a0dfa @beberlei Converted ORM Docs into ReST
beberlei authored
564
565 Do not invoke ``flush`` after every change to an entity
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
566 or every single invocation of persist/remove/merge/... This is an
567 anti-pattern and unnecessarily reduces the performance of your
568 application. Instead, form units of work that operate on your
569 objects and call ``flush`` when you are done. While serving a
570 single HTTP request there should be usually no need for invoking
571 ``flush`` more than 0-2 times.
572
573
574 Direct access to a Unit of Work
575 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
576
577 You can get direct access to the Unit of Work by calling
578 ``EntityManager#getUnitOfWork()``. This will return the UnitOfWork
579 instance the EntityManager is currently using.
580
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
581 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
582
583 <?php
584 $uow = $em->getUnitOfWork();
585
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
586 .. note::
587
588 Directly manipulating a UnitOfWork is not recommended.
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
589 When working directly with the UnitOfWork API, respect methods
590 marked as INTERNAL by not using them and carefully read the API
591 documentation.
592
593
594 Entity State
595 ~~~~~~~~~~~~
596
597 As outlined in the architecture overview an entity can be in one of
598 four possible states: NEW, MANAGED, REMOVED, DETACHED. If you
599 explicitly need to find out what the current state of an entity is
600 in the context of a certain ``EntityManager`` you can ask the
601 underlying ``UnitOfWork``:
602
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
603 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
604
605 <?php
606 switch ($em->getUnitOfWork()->getEntityState($entity)) {
729ad9e fixed constants typos in "Entity State"
Eriksen Costa authored
607 case UnitOfWork::STATE_MANAGED:
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
608 ...
729ad9e fixed constants typos in "Entity State"
Eriksen Costa authored
609 case UnitOfWork::STATE_REMOVED:
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
610 ...
729ad9e fixed constants typos in "Entity State"
Eriksen Costa authored
611 case UnitOfWork::STATE_DETACHED:
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
612 ...
729ad9e fixed constants typos in "Entity State"
Eriksen Costa authored
613 case UnitOfWork::STATE_NEW:
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
614 ...
615 }
616
617 An entity is in MANAGED state if it is associated with an
618 ``EntityManager`` and it is not REMOVED.
619
620 An entity is in REMOVED state after it has been passed to
621 ``EntityManager#remove()`` until the next flush operation of the
622 same EntityManager. A REMOVED entity is still associated with an
623 ``EntityManager`` until the next flush operation.
624
625 An entity is in DETACHED state if it has persistent state and
626 identity but is currently not associated with an
627 ``EntityManager``.
628
629 An entity is in NEW state if has no persistent state and identity
630 and is not associated with an ``EntityManager`` (for example those
631 just created via the "new" operator).
632
633 Querying
634 --------
635
636 Doctrine 2 provides the following ways, in increasing level of
637 power and flexibility, to query for persistent objects. You should
638 always start with the simplest one that suits your needs.
639
640 By Primary Key
641 ~~~~~~~~~~~~~~
642
643 The most basic way to query for a persistent object is by its
644 identifier / primary key using the
645 ``EntityManager#find($entityName, $id)`` method. Here is an
646 example:
647
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
648 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
649
650 <?php
651 // $em instanceof EntityManager
652 $user = $em->find('MyProject\Domain\User', $id);
653
654 The return value is either the found entity instance or null if no
655 instance could be found with the given identifier.
656
657 Essentially, ``EntityManager#find()`` is just a shortcut for the
658 following:
659
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
660 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
661
662 <?php
663 // $em instanceof EntityManager
664 $user = $em->getRepository('MyProject\Domain\User')->find($id);
665
666 ``EntityManager#getRepository($entityName)`` returns a repository
667 object which provides many ways to retrieve entities of the
668 specified type. By default, the repository instance is of type
669 ``Doctrine\ORM\EntityRepository``. You can also use custom
670 repository classes as shown later.
671
672 By Simple Conditions
673 ~~~~~~~~~~~~~~~~~~~~
674
675 To query for one or more entities based on several conditions that
676 form a logical conjunction, use the ``findBy`` and ``findOneBy``
677 methods on a repository as follows:
678
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
679 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
680
681 <?php
682 // $em instanceof EntityManager
683
684 // All users that are 20 years old
685 $users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20));
686
687 // All users that are 20 years old and have a surname of 'Miller'
688 $users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20, 'surname' => 'Miller'));
689
690 // A single user by its nickname
691 $user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb'));
692
6f909b6 @beberlei Clarifications and additions in DQL and Working with objects chapter
beberlei authored
693 You can also load by owning side associations through the repository:
694
91b2c82 @beberlei Brought most of the documentation up to date on 2.1
beberlei authored
695 .. code-block:: php
696
697 <?php
6f909b6 @beberlei Clarifications and additions in DQL and Working with objects chapter
beberlei authored
698 $number = $em->find('MyProject\Domain\Phonenumber', 1234);
699 $user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('phone' => $number->getId()));
700
91b2c82 @beberlei Brought most of the documentation up to date on 2.1
beberlei authored
701 Be careful that this only works by passing the ID of the associated entity, not yet by passing the associated entity itself.
702
703 The ``EntityRepository#findBy()`` method additionally accepts orderings, limit and offset as second to fourth parameters:
704
705 .. code-block:: php
706
707 <?php
78ef07f @asm89 Update en/reference/working-with-objects.rst
asm89 authored
708 $tenUsers = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20), array('name' => 'ASC'), 10, 0);
91b2c82 @beberlei Brought most of the documentation up to date on 2.1
beberlei authored
709
710 If you pass an array of values Doctrine will convert the query into a WHERE field IN (..) query automatically:
711
712 .. code-block:: php
713
714 <?php
78ef07f @asm89 Update en/reference/working-with-objects.rst
asm89 authored
715 $users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => array(20, 30, 40)));
91b2c82 @beberlei Brought most of the documentation up to date on 2.1
beberlei authored
716 // translates roughly to: SELECT * FROM users WHERE age IN (20, 30, 40)
6f909b6 @beberlei Clarifications and additions in DQL and Working with objects chapter
beberlei authored
717
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
718 An EntityRepository also provides a mechanism for more concise
719 calls through its use of ``__call``. Thus, the following two
720 examples are equivalent:
721
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
722 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
723
724 <?php
725 // A single user by its nickname
726 $user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb'));
727
728 // A single user by its nickname (__call magic)
729 $user = $em->getRepository('MyProject\Domain\User')->findOneByNickname('romanb');
730
731 By Eager Loading
732 ~~~~~~~~~~~~~~~~
733
734 Whenever you query for an entity that has persistent associations
735 and these associations are mapped as EAGER, they will automatically
736 be loaded together with the entity being queried and is thus
737 immediately available to your application.
738
739 By Lazy Loading
740 ~~~~~~~~~~~~~~~
741
742 Whenever you have a managed entity instance at hand, you can
743 traverse and use any associations of that entity that are
744 configured LAZY as if they were in-memory already. Doctrine will
745 automatically load the associated objects on demand through the
746 concept of lazy-loading.
747
748 By DQL
749 ~~~~~~
750
751 The most powerful and flexible method to query for persistent
752 objects is the Doctrine Query Language, an object query language.
753 DQL enables you to query for persistent objects in the language of
754 objects. DQL understands classes, fields, inheritance and
755 associations. DQL is syntactically very similar to the familiar SQL
756 but *it is not SQL*.
757
758 A DQL query is represented by an instance of the
759 ``Doctrine\ORM\Query`` class. You create a query using
760 ``EntityManager#createQuery($dql)``. Here is a simple example:
761
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
762 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
763
764 <?php
765 // $em instanceof EntityManager
766
767 // All users with an age between 20 and 30 (inclusive).
768 $q = $em->createQuery("select u from MyDomain\Model\User u where u.age >= 20 and u.age <= 30");
769 $users = $q->getResult();
770
771 Note that this query contains no knowledge about the relational
772 schema, only about the object model. DQL supports positional as
773 well as named parameters, many functions, (fetch) joins,
774 aggregates, subqueries and much more. Detailed information about
775 DQL and its syntax as well as the Doctrine class can be found in
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
776 :doc:`the dedicated chapter <dql-doctrine-query-language>`.
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
777 For programmatically building up queries based on conditions that
778 are only known at runtime, Doctrine provides the special
779 ``Doctrine\ORM\QueryBuilder`` class. More information on
780 constructing queries with a QueryBuilder can be found
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
781 :doc:`in Query Builder chapter <query-builder>`.
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
782
783 By Native Queries
784 ~~~~~~~~~~~~~~~~~
785
786 As an alternative to DQL or as a fallback for special SQL
787 statements native queries can be used. Native queries are built by
788 using a hand-crafted SQL query and a ResultSetMapping that
789 describes how the SQL result set should be transformed by Doctrine.
790 More information about native queries can be found in
c0f86e7 @michalpipa Changed external link to internal link.
michalpipa authored
791 :doc:`the dedicated chapter <native-sql>`.
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
792
793 Custom Repositories
794 ~~~~~~~~~~~~~~~~~~~
795
796 By default the EntityManager returns a default implementation of
797 ``Doctrine\ORM\EntityRepository`` when you call
798 ``EntityManager#getRepository($entityClass)``. You can overwrite
799 this behaviour by specifying the class name of your own Entity
800 Repository in the Annotation, XML or YAML metadata. In large
801 applications that require lots of specialized DQL queries using a
802 custom repository is one recommended way of grouping these queries
803 in a central location.
804
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
805 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
806
807 <?php
808 namespace MyDomain\Model;
809
810 use Doctrine\ORM\EntityRepository;
811
812 /**
813 * @entity(repositoryClass="MyDomain\Model\UserRepository")
814 */
815 class User
816 {
817
818 }
819
820 class UserRepository extends EntityRepository
821 {
822 public function getAllAdminUsers()
823 {
824 return $this->_em->createQuery('SELECT u FROM MyDomain\Model\User u WHERE u.status = "admin"')
825 ->getResult();
826 }
827 }
828
829 You can access your repository now by calling:
830
4698346 @beberlei Finialized ReST doc changes, merged changes from latest Markdown docs.
beberlei authored
831 .. code-block:: php
1bfeaf3 @beberlei Initial conversion from Markdown to ReST - Finalized Cookbook
beberlei authored
832
833 <?php
834 // $em instanceof EntityManager
835
836 $admins = $em->getRepository('MyDomain\Model\User')->getAllAdminUsers();
837
838
Something went wrong with that request. Please try again.