Complex classes like UnitOfWork often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use UnitOfWork, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
59 | class UnitOfWork implements PropertyChangedListener |
||
60 | { |
||
61 | /** |
||
62 | * An entity is in MANAGED state when its persistence is managed by an EntityManager. |
||
63 | */ |
||
64 | const STATE_MANAGED = 1; |
||
65 | |||
66 | /** |
||
67 | * An entity is new if it has just been instantiated (i.e. using the "new" operator) |
||
68 | * and is not (yet) managed by an EntityManager. |
||
69 | */ |
||
70 | const STATE_NEW = 2; |
||
71 | |||
72 | /** |
||
73 | * A detached entity is an instance with persistent state and identity that is not |
||
74 | * (or no longer) associated with an EntityManager (and a UnitOfWork). |
||
75 | */ |
||
76 | const STATE_DETACHED = 3; |
||
77 | |||
78 | /** |
||
79 | * A removed entity instance is an instance with a persistent identity, |
||
80 | * associated with an EntityManager, whose persistent state will be deleted |
||
81 | * on commit. |
||
82 | */ |
||
83 | const STATE_REMOVED = 4; |
||
84 | |||
85 | /** |
||
86 | * Hint used to collect all primary keys of associated entities during hydration |
||
87 | * and execute it in a dedicated query afterwards |
||
88 | * @see https://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html?highlight=eager#temporarily-change-fetch-mode-in-dql |
||
89 | */ |
||
90 | const HINT_DEFEREAGERLOAD = 'deferEagerLoad'; |
||
91 | |||
92 | /** |
||
93 | * The identity map that holds references to all managed entities that have |
||
94 | * an identity. The entities are grouped by their class name. |
||
95 | * Since all classes in a hierarchy must share the same identifier set, |
||
96 | * we always take the root class name of the hierarchy. |
||
97 | * |
||
98 | * @var array |
||
99 | */ |
||
100 | private $identityMap = []; |
||
101 | |||
102 | /** |
||
103 | * Map of all identifiers of managed entities. |
||
104 | * This is a 2-dimensional data structure (map of maps). Keys are object ids (spl_object_hash). |
||
105 | * Values are maps of entity identifiers, where its key is the column name and the value is the raw value. |
||
106 | * |
||
107 | * @var array |
||
108 | */ |
||
109 | private $entityIdentifiers = []; |
||
110 | |||
111 | /** |
||
112 | * Map of the original entity data of managed entities. |
||
113 | * This is a 2-dimensional data structure (map of maps). Keys are object ids (spl_object_hash). |
||
114 | * Values are maps of entity data, where its key is the field name and the value is the converted |
||
115 | * (convertToPHPValue) value. |
||
116 | * This structure is used for calculating changesets at commit time. |
||
117 | * |
||
118 | * Internal: Note that PHPs "copy-on-write" behavior helps a lot with memory usage. |
||
119 | * A value will only really be copied if the value in the entity is modified by the user. |
||
120 | * |
||
121 | * @var array |
||
122 | */ |
||
123 | private $originalEntityData = []; |
||
124 | |||
125 | /** |
||
126 | * Map of entity changes. Keys are object ids (spl_object_hash). |
||
127 | * Filled at the beginning of a commit of the UnitOfWork and cleaned at the end. |
||
128 | * |
||
129 | * @var array |
||
130 | */ |
||
131 | private $entityChangeSets = []; |
||
132 | |||
133 | /** |
||
134 | * The (cached) states of any known entities. |
||
135 | * Keys are object ids (spl_object_hash). |
||
136 | * |
||
137 | * @var array |
||
138 | */ |
||
139 | private $entityStates = []; |
||
140 | |||
141 | /** |
||
142 | * Map of entities that are scheduled for dirty checking at commit time. |
||
143 | * This is only used for entities with a change tracking policy of DEFERRED_EXPLICIT. |
||
144 | * Keys are object ids (spl_object_hash). |
||
145 | * |
||
146 | * @var array |
||
147 | */ |
||
148 | private $scheduledForSynchronization = []; |
||
149 | |||
150 | /** |
||
151 | * A list of all pending entity insertions. |
||
152 | * |
||
153 | * @var array |
||
154 | */ |
||
155 | private $entityInsertions = []; |
||
156 | |||
157 | /** |
||
158 | * A list of all pending entity updates. |
||
159 | * |
||
160 | * @var array |
||
161 | */ |
||
162 | private $entityUpdates = []; |
||
163 | |||
164 | /** |
||
165 | * Any pending extra updates that have been scheduled by persisters. |
||
166 | * |
||
167 | * @var array |
||
168 | */ |
||
169 | private $extraUpdates = []; |
||
170 | |||
171 | /** |
||
172 | * A list of all pending entity deletions. |
||
173 | * |
||
174 | * @var array |
||
175 | */ |
||
176 | private $entityDeletions = []; |
||
177 | |||
178 | /** |
||
179 | * All pending collection deletions. |
||
180 | * |
||
181 | * @var array |
||
182 | */ |
||
183 | private $collectionDeletions = []; |
||
184 | |||
185 | /** |
||
186 | * All pending collection updates. |
||
187 | * |
||
188 | * @var array |
||
189 | */ |
||
190 | private $collectionUpdates = []; |
||
191 | |||
192 | /** |
||
193 | * List of collections visited during changeset calculation on a commit-phase of a UnitOfWork. |
||
194 | * At the end of the UnitOfWork all these collections will make new snapshots |
||
195 | * of their data. |
||
196 | * |
||
197 | * @var array |
||
198 | */ |
||
199 | private $visitedCollections = []; |
||
200 | |||
201 | /** |
||
202 | * The EntityManager that "owns" this UnitOfWork instance. |
||
203 | * |
||
204 | * @var EntityManagerInterface |
||
205 | */ |
||
206 | private $em; |
||
207 | |||
208 | /** |
||
209 | * The entity persister instances used to persist entity instances. |
||
210 | * |
||
211 | * @var array |
||
212 | */ |
||
213 | private $entityPersisters = []; |
||
214 | |||
215 | /** |
||
216 | * The collection persister instances used to persist collections. |
||
217 | * |
||
218 | * @var array |
||
219 | */ |
||
220 | private $collectionPersisters = []; |
||
221 | |||
222 | /** |
||
223 | * The EventManager used for dispatching events. |
||
224 | * |
||
225 | * @var \Doctrine\Common\EventManager |
||
226 | */ |
||
227 | private $eventManager; |
||
228 | |||
229 | /** |
||
230 | * The ListenersInvoker used for dispatching events. |
||
231 | * |
||
232 | * @var \Doctrine\ORM\Event\ListenersInvoker |
||
233 | */ |
||
234 | private $listenersInvoker; |
||
235 | |||
236 | /** |
||
237 | * @var Instantiator |
||
238 | */ |
||
239 | private $instantiator; |
||
240 | |||
241 | /** |
||
242 | * Orphaned entities that are scheduled for removal. |
||
243 | * |
||
244 | * @var array |
||
245 | */ |
||
246 | private $orphanRemovals = []; |
||
247 | |||
248 | /** |
||
249 | * Read-Only objects are never evaluated |
||
250 | * |
||
251 | * @var array |
||
252 | */ |
||
253 | private $readOnlyObjects = []; |
||
254 | |||
255 | /** |
||
256 | * Map of Entity Class-Names and corresponding IDs that should eager loaded when requested. |
||
257 | * |
||
258 | * @var array |
||
259 | */ |
||
260 | private $eagerLoadingEntities = []; |
||
261 | |||
262 | /** |
||
263 | * @var boolean |
||
264 | */ |
||
265 | protected $hasCache = false; |
||
266 | |||
267 | /** |
||
268 | * Helper for handling completion of hydration |
||
269 | * |
||
270 | * @var HydrationCompleteHandler |
||
271 | */ |
||
272 | private $hydrationCompleteHandler; |
||
273 | |||
274 | /** |
||
275 | * @var NormalizeIdentifier |
||
276 | */ |
||
277 | private $normalizeIdentifier; |
||
278 | |||
279 | /** |
||
280 | * Initializes a new UnitOfWork instance, bound to the given EntityManager. |
||
281 | * |
||
282 | * @param EntityManagerInterface $em |
||
283 | */ |
||
284 | public function __construct(EntityManagerInterface $em) |
||
285 | { |
||
286 | $this->em = $em; |
||
287 | $this->eventManager = $em->getEventManager(); |
||
288 | $this->listenersInvoker = new ListenersInvoker($em); |
||
289 | $this->hasCache = $em->getConfiguration()->isSecondLevelCacheEnabled(); |
||
290 | $this->instantiator = new Instantiator(); |
||
291 | $this->hydrationCompleteHandler = new HydrationCompleteHandler($this->listenersInvoker, $em); |
||
292 | 2290 | $this->normalizeIdentifier = new NormalizeIdentifier(); |
|
293 | } |
||
294 | 2290 | ||
295 | 2290 | /** |
|
296 | 2290 | * Commits the UnitOfWork, executing all operations that have been postponed |
|
297 | 2290 | * up to this point. The state of all managed entities will be synchronized with |
|
298 | 2290 | * the database. |
|
299 | 2290 | * |
|
300 | 2290 | * The operations are executed in the following order: |
|
301 | 2290 | * |
|
302 | * 1) All entity insertions |
||
303 | * 2) All entity updates |
||
304 | * 3) All collection deletions |
||
305 | * 4) All collection updates |
||
306 | * 5) All entity deletions |
||
307 | * |
||
308 | * @return void |
||
309 | * |
||
310 | * @throws \Exception |
||
311 | */ |
||
312 | public function commit() |
||
313 | { |
||
314 | // Raise preFlush |
||
315 | if ($this->eventManager->hasListeners(Events::preFlush)) { |
||
316 | $this->eventManager->dispatchEvent(Events::preFlush, new PreFlushEventArgs($this->em)); |
||
317 | } |
||
318 | |||
319 | $this->computeChangeSets(); |
||
320 | |||
321 | if ( ! ($this->entityInsertions || |
||
|
|||
322 | 1015 | $this->entityDeletions || |
|
323 | $this->entityUpdates || |
||
324 | $this->collectionUpdates || |
||
325 | 1015 | $this->collectionDeletions || |
|
326 | 2 | $this->orphanRemovals)) { |
|
327 | $this->dispatchOnFlushEvent(); |
||
328 | $this->dispatchPostFlushEvent(); |
||
329 | |||
330 | 1015 | return; // Nothing to do. |
|
331 | 1007 | } |
|
332 | 16 | ||
333 | 15 | if ($this->orphanRemovals) { |
|
334 | 1 | foreach ($this->orphanRemovals as $orphan) { |
|
335 | 1 | $this->remove($orphan); |
|
336 | 1 | } |
|
337 | } |
||
338 | |||
339 | $this->dispatchOnFlushEvent(); |
||
340 | 1012 | ||
341 | 165 | // Now we need a commit order to maintain referential integrity |
|
342 | 129 | $commitOrder = $this->getCommitOrder(); |
|
343 | 40 | ||
344 | 37 | $conn = $this->em->getConnection(); |
|
345 | 1012 | $conn->beginTransaction(); |
|
346 | 25 | ||
347 | 25 | try { |
|
348 | // Collection deletions (deletions of complete collections) |
||
349 | 25 | foreach ($this->collectionDeletions as $collectionToDelete) { |
|
350 | $this->getCollectionPersister($collectionToDelete->getMapping())->delete($collectionToDelete); |
||
351 | } |
||
352 | 1008 | ||
353 | 16 | if ($this->entityInsertions) { |
|
354 | 16 | foreach ($commitOrder as $class) { |
|
355 | $this->executeInserts($class); |
||
356 | } |
||
357 | } |
||
358 | 1008 | ||
359 | if ($this->entityUpdates) { |
||
360 | foreach ($commitOrder as $class) { |
||
361 | 1008 | $this->executeUpdates($class); |
|
362 | } |
||
363 | 1008 | } |
|
364 | 1008 | ||
365 | // Extra updates that were requested by persisters. |
||
366 | if ($this->extraUpdates) { |
||
367 | $this->executeExtraUpdates(); |
||
368 | 1008 | } |
|
369 | 19 | ||
370 | // Collection updates (deleteRows, updateRows, insertRows) |
||
371 | foreach ($this->collectionUpdates as $collectionToUpdate) { |
||
372 | 1008 | $this->getCollectionPersister($collectionToUpdate->getMapping())->update($collectionToUpdate); |
|
373 | 1004 | } |
|
374 | 1004 | ||
375 | // Entity deletions come last and need to be in reverse commit order |
||
376 | if ($this->entityDeletions) { |
||
377 | foreach (array_reverse($commitOrder) as $committedEntityName) { |
||
378 | 1007 | if (! $this->entityDeletions) { |
|
379 | 115 | break; // just a performance optimisation |
|
380 | 115 | } |
|
381 | |||
382 | $this->executeDeletions($committedEntityName); |
||
383 | } |
||
384 | } |
||
385 | 1003 | ||
386 | 40 | $conn->commit(); |
|
387 | } catch (Exception $e) { |
||
388 | $this->em->close(); |
||
389 | $conn->rollBack(); |
||
390 | 1003 | ||
391 | 531 | $this->afterTransactionRolledBack(); |
|
392 | |||
393 | throw $e; |
||
394 | } |
||
395 | 1003 | ||
396 | 62 | $this->afterTransactionComplete(); |
|
397 | 62 | ||
398 | // Take new snapshots from visited collections |
||
399 | foreach ($this->visitedCollections as $coll) { |
||
400 | $coll->takeSnapshot(); |
||
401 | 1003 | } |
|
402 | 11 | ||
403 | 11 | $this->dispatchPostFlushEvent(); |
|
404 | 11 | ||
405 | // Clear up |
||
406 | 11 | $this->entityInsertions = |
|
407 | $this->entityUpdates = |
||
408 | 11 | $this->entityDeletions = |
|
409 | $this->extraUpdates = |
||
410 | $this->entityChangeSets = |
||
411 | 1003 | $this->collectionUpdates = |
|
412 | $this->collectionDeletions = |
||
413 | $this->visitedCollections = |
||
414 | 1003 | $this->scheduledForSynchronization = |
|
415 | 530 | $this->orphanRemovals = []; |
|
416 | } |
||
417 | |||
418 | 1003 | /** |
|
419 | * Computes the changesets of all entities scheduled for insertion. |
||
420 | * |
||
421 | 1002 | * @return void |
|
422 | 1002 | */ |
|
423 | 1002 | private function computeScheduleInsertsChangeSets() |
|
424 | 1002 | { |
|
425 | 1002 | foreach ($this->entityInsertions as $entity) { |
|
426 | 1002 | $class = $this->em->getClassMetadata(get_class($entity)); |
|
427 | 1002 | ||
428 | 1002 | $this->computeChangeSet($class, $entity); |
|
429 | 1002 | } |
|
430 | 1002 | } |
|
431 | 1002 | ||
432 | /** |
||
433 | * Executes any extra updates that have been scheduled. |
||
434 | */ |
||
435 | private function executeExtraUpdates() |
||
436 | { |
||
437 | foreach ($this->extraUpdates as $oid => $update) { |
||
438 | 1014 | list ($entity, $changeset) = $update; |
|
439 | |||
440 | 1014 | $this->entityChangeSets[$oid] = $changeset; |
|
441 | 1006 | ||
442 | // echo 'Extra update: '; |
||
443 | 1006 | // \Doctrine\Common\Util\Debug::dump($changeset, 3); |
|
444 | |||
445 | 1012 | $this->getEntityPersister(get_class($entity))->update($entity); |
|
446 | } |
||
447 | |||
448 | $this->extraUpdates = []; |
||
449 | } |
||
450 | |||
451 | /** |
||
452 | * Gets the changeset for an entity. |
||
453 | * |
||
454 | * @param object $entity |
||
455 | * |
||
456 | * @return array |
||
457 | */ |
||
458 | public function & getEntityChangeSet($entity) |
||
459 | { |
||
460 | $oid = spl_object_hash($entity); |
||
461 | 16 | $data = []; |
|
462 | |||
463 | 16 | if (!isset($this->entityChangeSets[$oid])) { |
|
464 | return $data; |
||
465 | 16 | } |
|
466 | 1 | ||
467 | return $this->entityChangeSets[$oid]; |
||
468 | } |
||
469 | 15 | ||
470 | /** |
||
471 | 15 | * Computes the changes that happened to a single entity. |
|
472 | 14 | * |
|
473 | * Modifies/populates the following properties: |
||
474 | * |
||
475 | * {@link originalEntityData} |
||
476 | 15 | * If the entity is NEW or MANAGED but not yet fully persisted (only has an id) |
|
477 | * then it was not fetched from the database and therefore we have no original |
||
478 | 15 | * entity data yet. All of the current entity data is stored as the original entity data. |
|
479 | * |
||
480 | * {@link entityChangeSets} |
||
481 | * The changes detected on all properties of the entity are stored there. |
||
482 | * A change is a tuple array where the first entry is the old value and the second |
||
483 | 15 | * entry is the new value of the property. Changesets are used by persisters |
|
484 | 2 | * to INSERT/UPDATE the persistent entity state. |
|
485 | * |
||
486 | * {@link entityUpdates} |
||
487 | * If the entity is already fully MANAGED (has been fetched from the database before) |
||
488 | 13 | * and any changes to its properties are detected, then a reference to the entity is stored |
|
489 | * there to mark it for an update. |
||
490 | 13 | * |
|
491 | 6 | * {@link collectionDeletions} |
|
492 | * If a PersistentCollection has been de-referenced in a fully MANAGED entity, |
||
493 | 12 | * then this collection is marked for deletion. |
|
494 | * |
||
495 | * @ignore |
||
496 | * |
||
497 | * @internal Don't call from the outside. |
||
498 | 40 | * |
|
499 | * @param ClassMetadata $class The class descriptor of the entity. |
||
500 | 40 | * @param object $entity The entity for which to compute the changes. |
|
501 | 40 | * |
|
502 | * @return void |
||
503 | 40 | */ |
|
504 | public function computeChangeSet(ClassMetadata $class, $entity) |
||
505 | { |
||
506 | $oid = spl_object_hash($entity); |
||
507 | |||
508 | 40 | if (isset($this->readOnlyObjects[$oid])) { |
|
509 | return; |
||
510 | } |
||
511 | 40 | ||
512 | 40 | if ($class->inheritanceType !== InheritanceType::NONE) { |
|
513 | $class = $this->em->getClassMetadata(get_class($entity)); |
||
514 | } |
||
515 | |||
516 | $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preFlush) & ~ListenersInvoker::INVOKE_MANAGER; |
||
517 | |||
518 | if ($invoke !== ListenersInvoker::INVOKE_NONE) { |
||
519 | $this->listenersInvoker->invoke($class, Events::preFlush, $entity, new PreFlushEventArgs($this->em), $invoke); |
||
520 | } |
||
521 | 1006 | ||
522 | $actualData = []; |
||
523 | 1006 | ||
524 | 1006 | foreach ($class->getDeclaredPropertiesIterator() as $name => $property) { |
|
525 | $value = $property->getValue($entity); |
||
526 | 1006 | ||
527 | 1 | if ($property instanceof ToManyAssociationMetadata && $value !== null) { |
|
528 | if ($value instanceof PersistentCollection && $value->getOwner() === $entity) { |
||
529 | continue; |
||
530 | 1006 | } |
|
531 | |||
532 | $value = $property->wrap($entity, $value, $this->em); |
||
533 | |||
534 | $property->setValue($entity, $value); |
||
535 | |||
536 | $actualData[$name] = $value; |
||
537 | |||
538 | continue; |
||
539 | } |
||
540 | |||
541 | if ( |
||
542 | ( ! $class->isIdentifier($name) |
||
543 | || ! $class->getProperty($name) instanceof FieldMetadata |
||
544 | || ! $class->getProperty($name)->hasValueGenerator() |
||
545 | || $class->getProperty($name)->getValueGenerator()->getType() !== GeneratorType::IDENTITY |
||
546 | ) && (! $class->isVersioned() || $name !== $class->versionProperty->getName())) { |
||
547 | $actualData[$name] = $value; |
||
548 | } |
||
549 | } |
||
550 | |||
551 | if ( ! isset($this->originalEntityData[$oid])) { |
||
552 | // Entity is either NEW or MANAGED but not yet fully persisted (only has an id). |
||
553 | // These result in an INSERT. |
||
554 | $this->originalEntityData[$oid] = $actualData; |
||
555 | $changeSet = []; |
||
556 | |||
557 | foreach ($actualData as $propName => $actualValue) { |
||
558 | $property = $class->getProperty($propName); |
||
559 | |||
560 | if (($property instanceof FieldMetadata) || |
||
561 | ($property instanceof ToOneAssociationMetadata && $property->isOwningSide())) { |
||
562 | $changeSet[$propName] = [null, $actualValue]; |
||
563 | } |
||
564 | } |
||
565 | |||
566 | $this->entityChangeSets[$oid] = $changeSet; |
||
567 | 1016 | } else { |
|
568 | // Entity is "fully" MANAGED: it was already fully persisted before |
||
569 | 1016 | // and we have a copy of the original data |
|
570 | $originalData = $this->originalEntityData[$oid]; |
||
571 | 1016 | $isChangeTrackingNotify = $class->changeTrackingPolicy === ChangeTrackingPolicy::NOTIFY; |
|
572 | 2 | $changeSet = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid])) |
|
573 | ? $this->entityChangeSets[$oid] |
||
574 | : []; |
||
575 | 1016 | ||
576 | 307 | foreach ($actualData as $propName => $actualValue) { |
|
577 | // skip field, its a partially omitted one! |
||
578 | if ( ! (isset($originalData[$propName]) || array_key_exists($propName, $originalData))) { |
||
579 | 1016 | continue; |
|
580 | } |
||
581 | 1016 | ||
582 | 137 | $orgValue = $originalData[$propName]; |
|
583 | |||
584 | // skip if value haven't changed |
||
585 | 1016 | if ($orgValue === $actualValue) { |
|
586 | continue; |
||
587 | 1016 | } |
|
588 | 1016 | ||
589 | $property = $class->getProperty($propName); |
||
590 | 1016 | ||
591 | 777 | // Persistent collection was exchanged with the "originally" |
|
592 | 200 | // created one. This can only mean it was cloned and replaced |
|
593 | 200 | // on another entity. |
|
594 | if ($actualValue instanceof PersistentCollection) { |
||
595 | $owner = $actualValue->getOwner(); |
||
596 | 5 | ||
597 | if ($owner === null) { // cloned |
||
598 | $actualValue->setOwner($entity, $property); |
||
599 | } else if ($owner !== $entity) { // no clone, we have to fix |
||
600 | 772 | if (! $actualValue->isInitialized()) { |
|
601 | 242 | $actualValue->initialize(); // we have to do this otherwise the cols share state |
|
602 | } |
||
603 | |||
604 | 772 | $newValue = clone $actualValue; |
|
605 | |||
606 | $newValue->setOwner($entity, $property); |
||
607 | 772 | ||
608 | 772 | $property->setValue($entity, $newValue); |
|
609 | } |
||
610 | 772 | } |
|
611 | 772 | ||
612 | switch (true) { |
||
613 | 772 | case ($property instanceof FieldMetadata): |
|
614 | if ($isChangeTrackingNotify) { |
||
615 | 772 | // Continue inside switch behaves as break. |
|
616 | // We are required to use continue 2, since we need to continue to next $actualData item |
||
617 | 772 | continue 2; |
|
618 | } |
||
619 | |||
620 | 1016 | $changeSet[$propName] = [$orgValue, $actualValue]; |
|
621 | 1016 | break; |
|
622 | 1016 | ||
623 | case ($property instanceof ToOneAssociationMetadata): |
||
624 | if ($property->isOwningSide()) { |
||
625 | $changeSet[$propName] = [$orgValue, $actualValue]; |
||
626 | 1016 | } |
|
627 | |||
628 | if ($orgValue !== null && $property->isOrphanRemoval()) { |
||
629 | 1012 | $this->scheduleOrphanRemoval($orgValue); |
|
630 | 1012 | } |
|
631 | |||
632 | 1012 | break; |
|
633 | 996 | ||
634 | 945 | case ($property instanceof ToManyAssociationMetadata): |
|
635 | // Check if original value exists |
||
636 | 945 | if ($orgValue instanceof PersistentCollection) { |
|
637 | // A PersistentCollection was de-referenced, so delete it. |
||
638 | $coid = spl_object_hash($orgValue); |
||
639 | 894 | ||
640 | if (!isset($this->collectionDeletions[$coid])) { |
||
641 | 894 | $this->collectionDeletions[$coid] = $orgValue; |
|
642 | 894 | $changeSet[$propName] = $orgValue; // Signal changeset, to-many associations will be ignored |
|
643 | } |
||
644 | } |
||
645 | |||
646 | 1012 | break; |
|
647 | |||
648 | default: |
||
649 | // Do nothing |
||
650 | 263 | } |
|
651 | 263 | } |
|
652 | 263 | ||
653 | if ($changeSet) { |
||
654 | 263 | $this->entityChangeSets[$oid] = $changeSet; |
|
655 | $this->originalEntityData[$oid] = $actualData; |
||
656 | 263 | $this->entityUpdates[$oid] = $entity; |
|
657 | } |
||
658 | 248 | } |
|
659 | 7 | ||
660 | // Look for changes in associations of the entity |
||
661 | foreach ($class->getDeclaredPropertiesIterator() as $property) { |
||
662 | 248 | if (! ($property instanceof AssociationMetadata) || ($value = $property->getValue($entity)) === null) { |
|
663 | continue; |
||
664 | } |
||
665 | 248 | ||
666 | 232 | $this->computeAssociationChanges($property, $value); |
|
667 | |||
668 | if ($property instanceof ManyToManyAssociationMetadata && |
||
669 | $value instanceof PersistentCollection && |
||
670 | 111 | ! isset($this->entityChangeSets[$oid]) && |
|
671 | 57 | $property->isOwningSide() && |
|
672 | $value->isDirty()) { |
||
673 | |||
674 | $this->entityChangeSets[$oid] = []; |
||
675 | 57 | $this->originalEntityData[$oid] = $actualData; |
|
676 | $this->entityUpdates[$oid] = $entity; |
||
677 | 57 | } |
|
678 | } |
||
679 | } |
||
680 | 58 | ||
681 | /** |
||
682 | * Computes all the changes that have been done to entities and collections |
||
683 | * since the last commit and stores these changes in the _entityChangeSet map |
||
684 | * temporarily for access by the persisters, until the UoW commit is finished. |
||
685 | 58 | * |
|
686 | 8 | * @return void |
|
687 | 8 | */ |
|
688 | public function computeChangeSets() |
||
689 | 8 | { |
|
690 | // Compute changes for INSERTed entities first. This must always happen. |
||
691 | $this->computeScheduleInsertsChangeSets(); |
||
692 | |||
693 | // Compute changes for other MANAGED entities. Change tracking policies take effect here. |
||
694 | foreach ($this->identityMap as $className => $entities) { |
||
695 | $class = $this->em->getClassMetadata($className); |
||
696 | |||
697 | // Skip class if instances are read-only |
||
698 | if ($class->isReadOnly()) { |
||
699 | 58 | continue; |
|
700 | } |
||
701 | 8 | ||
702 | // If change tracking is explicit or happens through notification, then only compute |
||
703 | 8 | // changes on entities of that type that are explicitly marked for synchronization. |
|
704 | switch (true) { |
||
705 | case ($class->changeTrackingPolicy === ChangeTrackingPolicy::DEFERRED_IMPLICIT): |
||
706 | $entitiesToProcess = $entities; |
||
707 | 8 | break; |
|
708 | 8 | ||
709 | case (isset($this->scheduledForSynchronization[$className])): |
||
710 | 8 | $entitiesToProcess = $this->scheduledForSynchronization[$className]; |
|
711 | break; |
||
712 | |||
713 | 50 | default: |
|
714 | 49 | $entitiesToProcess = []; |
|
715 | 21 | ||
716 | } |
||
717 | |||
718 | 49 | foreach ($entitiesToProcess as $entity) { |
|
719 | 50 | // Ignore uninitialized proxy objects |
|
720 | if ($entity instanceof GhostObjectInterface && ! $entity->isProxyInitialized()) { |
||
721 | continue; |
||
722 | } |
||
723 | |||
724 | 263 | // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here. |
|
725 | 84 | $oid = spl_object_hash($entity); |
|
726 | 84 | ||
727 | 84 | if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) { |
|
728 | $this->computeChangeSet($class, $entity); |
||
729 | } |
||
730 | } |
||
731 | } |
||
732 | 1016 | } |
|
733 | 894 | ||
734 | 639 | /** |
|
735 | * Computes the changes of an association. |
||
736 | * |
||
737 | 865 | * @param AssociationMetadata $association The association mapping. |
|
738 | * @param mixed $value The value of the association. |
||
739 | 857 | * |
|
740 | 857 | * @throws ORMInvalidArgumentException |
|
741 | 857 | * @throws ORMException |
|
742 | 857 | * |
|
743 | 857 | * @return void |
|
744 | */ |
||
745 | 35 | private function computeAssociationChanges(AssociationMetadata $association, $value) |
|
746 | 35 | { |
|
747 | 857 | if ($value instanceof GhostObjectInterface && ! $value->isProxyInitialized()) { |
|
748 | return; |
||
749 | } |
||
750 | 1008 | ||
751 | if ($value instanceof PersistentCollection && $value->isDirty()) { |
||
752 | $coid = spl_object_hash($value); |
||
753 | |||
754 | $this->collectionUpdates[$coid] = $value; |
||
755 | $this->visitedCollections[$coid] = $value; |
||
756 | } |
||
757 | |||
758 | // Look through the entities, and in any of their associations, |
||
759 | 1007 | // for transient (new) entities, recursively. ("Persistence by reachability") |
|
760 | // Unwrap. Uninitialized collections will simply be empty. |
||
761 | $unwrappedValue = ($association instanceof ToOneAssociationMetadata) ? [$value] : $value->unwrap(); |
||
762 | 1007 | $targetEntity = $association->getTargetEntity(); |
|
763 | $targetClass = $this->em->getClassMetadata($targetEntity); |
||
764 | |||
765 | 1005 | foreach ($unwrappedValue as $key => $entry) { |
|
766 | 447 | if (! ($entry instanceof $targetEntity)) { |
|
767 | throw ORMInvalidArgumentException::invalidAssociation($targetClass, $association, $entry); |
||
768 | } |
||
769 | 447 | ||
770 | 1 | $state = $this->getEntityState($entry, self::STATE_NEW); |
|
771 | |||
772 | if (! ($entry instanceof $targetEntity)) { |
||
773 | throw ORMException::unexpectedAssociationValue( |
||
774 | $association->getSourceEntity(), |
||
775 | $association->getName(), |
||
776 | 446 | get_class($entry), |
|
777 | 444 | $targetEntity |
|
778 | 444 | ); |
|
779 | } |
||
780 | 3 | ||
781 | 3 | switch ($state) { |
|
782 | 3 | case self::STATE_NEW: |
|
783 | if ( ! in_array('persist', $association->getCascade())) { |
||
784 | throw ORMInvalidArgumentException::newEntityFoundThroughRelationship($association, $entry); |
||
785 | 1 | } |
|
786 | |||
787 | $this->persistNew($targetClass, $entry); |
||
788 | $this->computeChangeSet($targetClass, $entry); |
||
789 | 446 | break; |
|
790 | |||
791 | 426 | case self::STATE_REMOVED: |
|
792 | 35 | // Consume the $value as array (it's either an array or an ArrayAccess) |
|
793 | // and remove the element from Collection. |
||
794 | if ($association instanceof ToManyAssociationMetadata) { |
||
795 | unset($value[$key]); |
||
796 | 425 | } |
|
797 | break; |
||
798 | 425 | ||
799 | 446 | case self::STATE_DETACHED: |
|
800 | // Can actually not happen right now as we assume STATE_NEW, |
||
801 | // so the exception will be raised from the DBAL layer (constraint violation). |
||
802 | throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($association, $entry); |
||
803 | 1005 | break; |
|
804 | |||
805 | default: |
||
806 | // MANAGED associated entities are already taken into account |
||
807 | // during changeset calculation anyway, since they are in the identity map. |
||
808 | } |
||
809 | } |
||
810 | } |
||
811 | |||
812 | /** |
||
813 | * @param \Doctrine\ORM\Mapping\ClassMetadata $class |
||
814 | * @param object $entity |
||
815 | * |
||
816 | 865 | * @return void |
|
817 | */ |
||
818 | 865 | private function persistNew($class, $entity) |
|
819 | 28 | { |
|
820 | $oid = spl_object_hash($entity); |
||
821 | $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::prePersist); |
||
822 | 864 | ||
823 | 533 | if ($invoke !== ListenersInvoker::INVOKE_NONE) { |
|
824 | $this->listenersInvoker->invoke($class, Events::prePersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke); |
||
825 | 533 | } |
|
826 | 533 | ||
827 | $generationPlan = $class->getValueGenerationPlan(); |
||
828 | $persister = $this->getEntityPersister($class->getClassName()); |
||
829 | $generationPlan->executeImmediate($this->em, $entity); |
||
830 | |||
831 | if (! $generationPlan->containsDeferred()) { |
||
832 | 864 | $id = $this->em->getIdentifierFlattener()->flattenIdentifier($class, $persister->getIdentifier($entity)); |
|
833 | 864 | $this->entityIdentifiers[$oid] = $id; |
|
834 | } |
||
835 | 864 | ||
836 | 722 | $this->entityStates[$oid] = self::STATE_MANAGED; |
|
837 | 6 | ||
838 | $this->scheduleForInsert($entity); |
||
839 | } |
||
840 | 716 | ||
841 | /** |
||
842 | 716 | * INTERNAL: |
|
843 | * Computes the changeset of an individual entity, independently of the |
||
844 | * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit(). |
||
845 | * |
||
846 | * The passed entity must be a managed entity. If the entity already has a change set |
||
847 | 716 | * because this method is invoked during a commit cycle then the change sets are added. |
|
848 | 39 | * whereby changes detected in this method prevail. |
|
849 | 4 | * |
|
850 | * @ignore |
||
851 | * |
||
852 | 35 | * @param ClassMetadata $class The class descriptor of the entity. |
|
853 | 35 | * @param object $entity The entity for which to (re)calculate the change set. |
|
854 | 35 | * |
|
855 | * @return void |
||
856 | 710 | * |
|
857 | * @throws ORMInvalidArgumentException If the passed entity is not MANAGED. |
||
858 | * @throws \RuntimeException |
||
859 | 4 | */ |
|
860 | 3 | public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity) : void |
|
861 | { |
||
862 | 4 | $oid = spl_object_hash($entity); |
|
863 | |||
864 | 710 | if (! isset($this->entityStates[$oid]) || $this->entityStates[$oid] !== self::STATE_MANAGED) { |
|
865 | throw ORMInvalidArgumentException::entityNotManaged($entity); |
||
866 | } |
||
867 | |||
868 | // skip if change tracking is "NOTIFY" |
||
869 | if ($class->changeTrackingPolicy === ChangeTrackingPolicy::NOTIFY) { |
||
870 | 713 | return; |
|
871 | } |
||
872 | |||
873 | if ($class->inheritanceType !== InheritanceType::NONE) { |
||
874 | $class = $this->em->getClassMetadata(get_class($entity)); |
||
875 | 856 | } |
|
876 | |||
877 | $actualData = []; |
||
878 | |||
879 | foreach ($class->getDeclaredPropertiesIterator() as $name => $property) { |
||
880 | switch (true) { |
||
881 | case ($property instanceof VersionFieldMetadata): |
||
882 | // Ignore version field |
||
883 | 1031 | break; |
|
884 | |||
885 | 1031 | case ($property instanceof FieldMetadata): |
|
886 | 1031 | if (! $property->isPrimaryKey() |
|
887 | || ! $property->getValueGenerator() |
||
888 | 1031 | || $property->getValueGenerator()->getType() !== GeneratorType::IDENTITY) { |
|
889 | 139 | $actualData[$name] = $property->getValue($entity); |
|
890 | } |
||
891 | |||
892 | 1031 | break; |
|
893 | |||
894 | 1031 | case ($property instanceof ToOneAssociationMetadata): |
|
895 | 269 | $actualData[$name] = $property->getValue($entity); |
|
896 | break; |
||
897 | 269 | } |
|
898 | 1 | } |
|
899 | |||
900 | 1 | if ( ! isset($this->originalEntityData[$oid])) { |
|
901 | throw new \RuntimeException('Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.'); |
||
902 | } |
||
903 | 269 | ||
904 | $originalData = $this->originalEntityData[$oid]; |
||
905 | $changeSet = []; |
||
906 | 1031 | ||
907 | foreach ($actualData as $propName => $actualValue) { |
||
908 | 1031 | $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; |
|
909 | 1031 | ||
910 | if ($orgValue !== $actualValue) { |
||
911 | $changeSet[$propName] = [$orgValue, $actualValue]; |
||
912 | } |
||
913 | } |
||
914 | |||
915 | if ($changeSet) { |
||
916 | if (isset($this->entityChangeSets[$oid])) { |
||
917 | $this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet); |
||
918 | } else if ( ! isset($this->entityInsertions[$oid])) { |
||
919 | $this->entityChangeSets[$oid] = $changeSet; |
||
920 | $this->entityUpdates[$oid] = $entity; |
||
921 | } |
||
922 | $this->originalEntityData[$oid] = $actualData; |
||
923 | } |
||
924 | } |
||
925 | |||
926 | /** |
||
927 | * Executes all entity insertions for entities of the specified type. |
||
928 | * |
||
929 | 16 | * @param ClassMetadata $class |
|
930 | * |
||
931 | 16 | * @return void |
|
932 | */ |
||
933 | 16 | private function executeInserts(ClassMetadata $class) : void |
|
934 | { |
||
935 | $className = $class->getClassName(); |
||
936 | $persister = $this->getEntityPersister($className); |
||
937 | $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist); |
||
938 | 16 | $generationPlan = $class->getValueGenerationPlan(); |
|
939 | |||
940 | foreach ($this->entityInsertions as $oid => $entity) { |
||
941 | if ($this->em->getClassMetadata(get_class($entity))->getClassName() !== $className) { |
||
942 | 16 | continue; |
|
943 | 3 | } |
|
944 | |||
945 | $persister->insert($entity); |
||
946 | 16 | ||
947 | if ($generationPlan->containsDeferred()) { |
||
948 | 16 | // Entity has post-insert IDs |
|
949 | 16 | $oid = spl_object_hash($entity); |
|
950 | 16 | $id = $this->em->getIdentifierFlattener()->flattenIdentifier($class, $persister->getIdentifier($entity)); |
|
951 | 16 | ||
952 | 16 | $this->entityIdentifiers[$oid] = $id; |
|
953 | $this->entityStates[$oid] = self::STATE_MANAGED; |
||
954 | $this->originalEntityData[$oid] = $id + $this->originalEntityData[$oid]; |
||
955 | |||
956 | 16 | $this->addToIdentityMap($entity); |
|
957 | } |
||
958 | |||
959 | unset($this->entityInsertions[$oid]); |
||
960 | 16 | ||
961 | 16 | if ($invoke !== ListenersInvoker::INVOKE_NONE) { |
|
962 | $eventArgs = new LifecycleEventArgs($entity, $this->em); |
||
963 | 16 | ||
964 | 16 | $this->listenersInvoker->invoke($class, Events::postPersist, $entity, $eventArgs, $invoke); |
|
965 | } |
||
966 | 16 | } |
|
967 | 16 | } |
|
968 | |||
969 | /** |
||
970 | * Executes all entity updates for entities of the specified type. |
||
971 | 16 | * |
|
972 | 7 | * @param \Doctrine\ORM\Mapping\ClassMetadata $class |
|
973 | 6 | * |
|
974 | 1 | * @return void |
|
975 | 1 | */ |
|
976 | 1 | private function executeUpdates($class) |
|
977 | { |
||
978 | 7 | $className = $class->getClassName(); |
|
979 | $persister = $this->getEntityPersister($className); |
||
980 | 16 | $preUpdateInvoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preUpdate); |
|
981 | $postUpdateInvoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postUpdate); |
||
982 | |||
983 | foreach ($this->entityUpdates as $oid => $entity) { |
||
984 | if ($this->em->getClassMetadata(get_class($entity))->getClassName() !== $className) { |
||
985 | continue; |
||
986 | } |
||
987 | |||
988 | if ($preUpdateInvoke != ListenersInvoker::INVOKE_NONE) { |
||
989 | 1004 | $this->listenersInvoker->invoke($class, Events::preUpdate, $entity, new PreUpdateEventArgs($entity, $this->em, $this->getEntityChangeSet($entity)), $preUpdateInvoke); |
|
990 | |||
991 | 1004 | $this->recomputeSingleEntityChangeSet($class, $entity); |
|
992 | 1004 | } |
|
993 | 1004 | ||
994 | 1004 | if ( ! empty($this->entityChangeSets[$oid])) { |
|
995 | // echo 'Update: '; |
||
996 | 1004 | // \Doctrine\Common\Util\Debug::dump($this->entityChangeSets[$oid], 3); |
|
997 | |||
998 | 1004 | $persister->update($entity); |
|
999 | 855 | } |
|
1000 | |||
1001 | unset($this->entityUpdates[$oid]); |
||
1002 | 1004 | ||
1003 | if ($postUpdateInvoke != ListenersInvoker::INVOKE_NONE) { |
||
1004 | 1004 | $this->listenersInvoker->invoke($class, Events::postUpdate, $entity, new LifecycleEventArgs($entity, $this->em), $postUpdateInvoke); |
|
1005 | } |
||
1006 | 1004 | } |
|
1007 | 1004 | } |
|
1008 | |||
1009 | /** |
||
1010 | * Executes all entity deletions for entities of the specified type. |
||
1011 | 1004 | * |
|
1012 | * @param \Doctrine\ORM\Mapping\ClassMetadata $class |
||
1013 | 1004 | * |
|
1014 | * @return void |
||
1015 | 918 | */ |
|
1016 | 918 | private function executeDeletions($class) |
|
1017 | 918 | { |
|
1018 | 918 | $className = $class->getClassName(); |
|
1019 | 918 | $persister = $this->getEntityPersister($className); |
|
1020 | $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postRemove); |
||
1021 | 918 | ||
1022 | foreach ($this->entityDeletions as $oid => $entity) { |
||
1023 | 918 | if ($this->em->getClassMetadata(get_class($entity))->getClassName() !== $className) { |
|
1024 | 918 | continue; |
|
1025 | 918 | } |
|
1026 | |||
1027 | 918 | $persister->delete($entity); |
|
1028 | |||
1029 | unset( |
||
1030 | $this->entityDeletions[$oid], |
||
1031 | 1004 | $this->entityIdentifiers[$oid], |
|
1032 | 135 | $this->originalEntityData[$oid], |
|
1033 | $this->entityStates[$oid] |
||
1034 | 1004 | ); |
|
1035 | |||
1036 | // Entity with this $oid after deletion treated as NEW, even if the $oid |
||
1037 | // is obtained by a new entity because the old one went out of scope. |
||
1038 | //$this->entityStates[$oid] = self::STATE_NEW; |
||
1039 | if (! $class->isIdentifierComposite()) { |
||
1040 | $property = $class->getProperty($class->getSingleIdentifierFieldName()); |
||
1041 | |||
1042 | if ($property instanceof FieldMetadata && $property->hasValueGenerator()) { |
||
1043 | 115 | $property->setValue($entity, null); |
|
1044 | } |
||
1045 | 115 | } |
|
1046 | 115 | ||
1047 | 115 | if ($invoke !== ListenersInvoker::INVOKE_NONE) { |
|
1048 | 115 | $eventArgs = new LifecycleEventArgs($entity, $this->em); |
|
1049 | |||
1050 | 115 | $this->listenersInvoker->invoke($class, Events::postRemove, $entity, $eventArgs, $invoke); |
|
1051 | 115 | } |
|
1052 | 74 | } |
|
1053 | } |
||
1054 | |||
1055 | 115 | /** |
|
1056 | 13 | * Gets the commit order. |
|
1057 | * |
||
1058 | 13 | * @return array |
|
1059 | */ |
||
1060 | private function getCommitOrder() |
||
1061 | 115 | { |
|
1062 | $calc = new Internal\CommitOrderCalculator(); |
||
1063 | |||
1064 | // See if there are any new classes in the changeset, that are not in the |
||
1065 | 81 | // commit order graph yet (don't have a node). |
|
1066 | // We have to inspect changeSet to be able to correctly build dependencies. |
||
1067 | // It is not possible to use IdentityMap here because post inserted ids |
||
1068 | 111 | // are not yet available. |
|
1069 | $newNodes = []; |
||
1070 | 111 | ||
1071 | 111 | foreach (\array_merge($this->entityInsertions, $this->entityUpdates, $this->entityDeletions) as $entity) { |
|
1072 | $class = $this->em->getClassMetadata(get_class($entity)); |
||
1073 | |||
1074 | 111 | if ($calc->hasNode($class->getClassName())) { |
|
1075 | continue; |
||
1076 | } |
||
1077 | |||
1078 | $calc->addNode($class->getClassName(), $class); |
||
1079 | |||
1080 | $newNodes[] = $class; |
||
1081 | } |
||
1082 | |||
1083 | 62 | // Calculate dependencies for new nodes |
|
1084 | while ($class = array_pop($newNodes)) { |
||
1085 | 62 | foreach ($class->getDeclaredPropertiesIterator() as $property) { |
|
1086 | 62 | if (! ($property instanceof ToOneAssociationMetadata && $property->isOwningSide())) { |
|
1087 | 62 | continue; |
|
1088 | } |
||
1089 | 62 | ||
1090 | 62 | $targetClass = $this->em->getClassMetadata($property->getTargetEntity()); |
|
1091 | 26 | ||
1092 | if ( ! $calc->hasNode($targetClass->getClassName())) { |
||
1093 | $calc->addNode($targetClass->getClassName(), $targetClass); |
||
1094 | 62 | ||
1095 | $newNodes[] = $targetClass; |
||
1096 | } |
||
1097 | 62 | ||
1098 | 62 | $weight = ! array_filter( |
|
1099 | 62 | $property->getJoinColumns(), |
|
1100 | 62 | function (JoinColumnMetadata $joinColumn) { return $joinColumn->isNullable(); } |
|
1101 | ); |
||
1102 | |||
1103 | $calc->addDependency($targetClass->getClassName(), $class->getClassName(), $weight); |
||
1104 | |||
1105 | // If the target class has mapped subclasses, these share the same dependency. |
||
1106 | 62 | if ( ! $targetClass->getSubClasses()) { |
|
1107 | 52 | continue; |
|
1108 | } |
||
1109 | |||
1110 | 62 | foreach ($targetClass->getSubClasses() as $subClassName) { |
|
1111 | 62 | $targetSubClass = $this->em->getClassMetadata($subClassName); |
|
1112 | |||
1113 | if ( ! $calc->hasNode($subClassName)) { |
||
1114 | 61 | $calc->addNode($targetSubClass->getClassName(), $targetSubClass); |
|
1115 | |||
1116 | $newNodes[] = $targetSubClass; |
||
1117 | } |
||
1118 | |||
1119 | $calc->addDependency($targetSubClass->getClassName(), $class->getClassName(), 1); |
||
1120 | } |
||
1121 | } |
||
1122 | } |
||
1123 | 1008 | ||
1124 | return $calc->sort(); |
||
1125 | 1008 | } |
|
1126 | 1008 | ||
1127 | /** |
||
1128 | * Schedules an entity for insertion into the database. |
||
1129 | 1008 | * If the entity already has an identifier, it will be added to the identity map. |
|
1130 | * |
||
1131 | * @param object $entity The entity to schedule for insertion. |
||
1132 | * |
||
1133 | * @return void |
||
1134 | * |
||
1135 | * @throws ORMInvalidArgumentException |
||
1136 | 1008 | * @throws \InvalidArgumentException |
|
1137 | */ |
||
1138 | 1008 | public function scheduleForInsert($entity) |
|
1167 | 839 | ||
1168 | /** |
||
1169 | * Checks whether an entity is scheduled for insertion. |
||
1170 | 839 | * |
|
1171 | 832 | * @param object $entity |
|
1172 | * |
||
1173 | * @return boolean |
||
1174 | 217 | */ |
|
1175 | 217 | public function isScheduledForInsert($entity) |
|
1179 | |||
1180 | 189 | /** |
|
1181 | * Schedules an entity for being updated. |
||
1182 | * |
||
1183 | 217 | * @param object $entity The entity to schedule for being updated. |
|
1184 | * |
||
1185 | * @return void |
||
1186 | * |
||
1187 | * @throws ORMInvalidArgumentException |
||
1188 | 1008 | */ |
|
1189 | public function scheduleForUpdate($entity) : void |
||
1205 | |||
1206 | 1032 | /** |
|
1207 | * INTERNAL: |
||
1208 | * Schedules an extra update that will be executed immediately after the |
||
1209 | * regular entity updates within the currently running commit cycle. |
||
1210 | 1032 | * |
|
1211 | 1 | * Extra updates for entities are stored as (entity, changeset) tuples. |
|
1212 | * |
||
1213 | 1032 | * @ignore |
|
1214 | 1 | * |
|
1215 | * @param object $entity The entity for which to schedule an extra update. |
||
1216 | * @param array $changeset The changeset of the entity (what to update). |
||
1217 | 1032 | * |
|
1218 | 1 | * @return void |
|
1219 | */ |
||
1220 | public function scheduleExtraUpdate($entity, array $changeset) : void |
||
1233 | |||
1234 | /** |
||
1235 | * Checks whether an entity is registered as dirty in the unit of work. |
||
1236 | * Note: Is not very useful currently as dirty entities are only registered |
||
1237 | * at commit time. |
||
1238 | * |
||
1239 | 631 | * @param object $entity |
|
1240 | * |
||
1241 | 631 | * @return boolean |
|
1242 | */ |
||
1243 | public function isScheduledForUpdate($entity) : bool |
||
1247 | |||
1248 | /** |
||
1249 | * Checks whether an entity is registered to be checked in the unit of work. |
||
1250 | * |
||
1251 | * @param object $entity |
||
1252 | * |
||
1253 | 1 | * @return boolean |
|
1254 | */ |
||
1255 | 1 | public function isScheduledForDirtyCheck($entity) : bool |
|
1261 | 1 | ||
1262 | /** |
||
1263 | * INTERNAL: |
||
1264 | * Schedules an entity for deletion. |
||
1265 | 1 | * |
|
1266 | 1 | * @param object $entity |
|
1267 | * |
||
1268 | 1 | * @return void |
|
1269 | */ |
||
1270 | public function scheduleForDelete($entity) |
||
1297 | |||
1298 | /** |
||
1299 | * Checks whether an entity is registered as removed/deleted with the unit |
||
1300 | * of work. |
||
1301 | * |
||
1302 | * @param object $entity |
||
1303 | * |
||
1304 | * @return boolean |
||
1305 | */ |
||
1306 | public function isScheduledForDelete($entity) |
||
1310 | |||
1311 | /** |
||
1312 | * Checks whether an entity is scheduled for insertion, update or deletion. |
||
1313 | * |
||
1314 | * @param object $entity |
||
1315 | * |
||
1316 | * @return boolean |
||
1317 | */ |
||
1318 | public function isEntityScheduled($entity) |
||
1326 | |||
1327 | /** |
||
1328 | * INTERNAL: |
||
1329 | * Registers an entity in the identity map. |
||
1330 | * Note that entities in a hierarchy are registered with the class name of |
||
1331 | * the root entity. |
||
1332 | * |
||
1333 | * @ignore |
||
1334 | 65 | * |
|
1335 | * @param object $entity The entity to register. |
||
1336 | 65 | * |
|
1337 | * @return boolean TRUE if the registration was successful, FALSE if the identity of |
||
1338 | 65 | * the entity in question is already managed. |
|
1339 | 1 | * |
|
1340 | * @throws ORMInvalidArgumentException |
||
1341 | */ |
||
1342 | public function addToIdentityMap($entity) |
||
1362 | |||
1363 | /** |
||
1364 | * Gets the state of an entity with regard to the current unit of work. |
||
1365 | * |
||
1366 | * @param object $entity |
||
1367 | * @param int|null $assume The state to assume if the state is not yet known (not MANAGED or REMOVED). |
||
1368 | * This parameter can be set to improve performance of entity state detection |
||
1369 | * by potentially avoiding a database lookup if the distinction between NEW and DETACHED |
||
1370 | 17 | * is either known or does not matter for the caller of the method. |
|
1371 | * |
||
1372 | 17 | * @return int The entity state. |
|
1373 | */ |
||
1374 | public function getEntityState($entity, $assume = null) |
||
1446 | 1039 | ||
1447 | 1035 | /** |
|
1448 | * INTERNAL: |
||
1449 | * Removes an entity from the identity map. This effectively detaches the |
||
1450 | * entity from the persistence management of Doctrine. |
||
1451 | * |
||
1452 | * @ignore |
||
1453 | * |
||
1454 | 13 | * @param object $entity |
|
1455 | 13 | * |
|
1456 | * @return boolean |
||
1457 | 13 | * |
|
1458 | 5 | * @throws ORMInvalidArgumentException |
|
1459 | */ |
||
1460 | public function removeFromIdentityMap($entity) |
||
1483 | |||
1484 | 4 | /** |
|
1485 | * INTERNAL: |
||
1486 | 5 | * Gets an entity in the identity map by its identifier hash. |
|
1487 | * |
||
1488 | * @ignore |
||
1489 | * |
||
1490 | * @param string $idHash |
||
1491 | * @param string $rootClassName |
||
1492 | * |
||
1493 | * @return object |
||
1494 | */ |
||
1495 | public function getByIdHash($idHash, $rootClassName) |
||
1499 | |||
1500 | /** |
||
1501 | * INTERNAL: |
||
1502 | * Tries to get an entity by its identifier hash. If no entity is found for |
||
1503 | * the given hash, FALSE is returned. |
||
1504 | 5 | * |
|
1505 | * @ignore |
||
1506 | * |
||
1507 | * @param mixed $idHash (must be possible to cast it to string) |
||
1508 | * @param string $rootClassName |
||
1509 | * |
||
1510 | * @return object|bool The found entity or FALSE. |
||
1511 | */ |
||
1512 | public function tryGetByIdHash($idHash, $rootClassName) |
||
1520 | |||
1521 | 76 | /** |
|
1522 | * Checks whether an entity is registered in the identity map of this UnitOfWork. |
||
1523 | 76 | * |
|
1524 | 76 | * @param object $entity |
|
1525 | 76 | * |
|
1526 | * @return boolean |
||
1527 | 76 | */ |
|
1528 | public function isInIdentityMap($entity) |
||
1541 | |||
1542 | /** |
||
1543 | * INTERNAL: |
||
1544 | * Checks whether an identifier hash exists in the identity map. |
||
1545 | * |
||
1546 | * @ignore |
||
1547 | * |
||
1548 | * @param string $idHash |
||
1549 | * @param string $rootClassName |
||
1550 | * |
||
1551 | * @return boolean |
||
1552 | */ |
||
1553 | public function containsIdHash($idHash, $rootClassName) |
||
1557 | |||
1558 | 6 | /** |
|
1559 | * Persists an entity as part of the current unit of work. |
||
1560 | * |
||
1561 | * @param object $entity The entity to persist. |
||
1562 | * |
||
1563 | * @return void |
||
1564 | */ |
||
1565 | public function persist($entity) |
||
1571 | |||
1572 | /** |
||
1573 | 34 | * Persists an entity as part of the current unit of work. |
|
1574 | * |
||
1575 | 34 | * This method is internally called during persist() cascades as it tracks |
|
1576 | * the already visited entities to prevent infinite recursions. |
||
1577 | 34 | * |
|
1578 | 34 | * @param object $entity The entity to persist. |
|
1579 | 34 | * @param array $visited The already visited entities. |
|
1580 | * |
||
1581 | * @return void |
||
1582 | * |
||
1583 | * @throws ORMInvalidArgumentException |
||
1584 | * @throws UnexpectedValueException |
||
1585 | */ |
||
1586 | private function doPersist($entity, array &$visited) |
||
1634 | 1028 | ||
1635 | 1021 | /** |
|
1636 | * Deletes an entity as part of the current unit of work. |
||
1637 | * |
||
1638 | * @param object $entity The entity to remove. |
||
1639 | * |
||
1640 | * @return void |
||
1641 | */ |
||
1642 | public function remove($entity) |
||
1648 | |||
1649 | /** |
||
1650 | * Deletes an entity as part of the current unit of work. |
||
1651 | 1028 | * |
|
1652 | * This method is internally called during delete() cascades as it tracks |
||
1653 | 1028 | * the already visited entities to prevent infinite recursions. |
|
1654 | * |
||
1655 | 1028 | * @param object $entity The entity to delete. |
|
1656 | 109 | * @param array $visited The map of the already visited entities. |
|
1657 | * |
||
1658 | * @return void |
||
1659 | 1028 | * |
|
1660 | * @throws ORMInvalidArgumentException If the instance is a detached entity. |
||
1661 | 1028 | * @throws UnexpectedValueException |
|
1662 | */ |
||
1663 | private function doRemove($entity, array &$visited) |
||
1703 | |||
1704 | /** |
||
1705 | * Refreshes the state of the given entity from the database, overwriting |
||
1706 | * any local, unpersisted changes. |
||
1707 | 64 | * |
|
1708 | * @param object $entity The entity to refresh. |
||
1709 | 64 | * |
|
1710 | * @return void |
||
1711 | 64 | * |
|
1712 | 64 | * @throws InvalidArgumentException If the entity is not MANAGED. |
|
1713 | */ |
||
1714 | public function refresh($entity) |
||
1715 | { |
||
1716 | $visited = []; |
||
1717 | |||
1720 | |||
1721 | /** |
||
1722 | * Executes a refresh operation on an entity. |
||
1723 | * |
||
1724 | * @param object $entity The entity to refresh. |
||
1725 | * @param array $visited The already visited entities during cascades. |
||
1726 | * |
||
1727 | * @return void |
||
1728 | 64 | * |
|
1729 | * @throws ORMInvalidArgumentException If the entity is not MANAGED. |
||
1730 | 64 | */ |
|
1731 | private function doRefresh($entity, array &$visited) |
||
1754 | 64 | ||
1755 | 8 | /** |
|
1756 | * Cascades a refresh operation to associated entities. |
||
1757 | * |
||
1758 | 64 | * @param object $entity |
|
1759 | 64 | * @param array $visited |
|
1760 | * |
||
1761 | * @return void |
||
1762 | */ |
||
1763 | private function cascadeRefresh($entity, array &$visited) |
||
1796 | |||
1797 | /** |
||
1798 | * Cascades the save operation to associated entities. |
||
1799 | * |
||
1800 | * @param object $entity |
||
1801 | * @param array $visited |
||
1802 | * |
||
1803 | 40 | * @return void |
|
1804 | */ |
||
1805 | 40 | private function cascadePersist($entity, array &$visited) |
|
1862 | 1 | ||
1863 | /** |
||
1864 | 1 | * Cascades the delete operation to associated entities. |
|
1865 | * |
||
1866 | * @param object $entity |
||
1867 | * @param array $visited |
||
1868 | 38 | * |
|
1869 | 4 | * @return void |
|
1870 | 4 | */ |
|
1871 | private function cascadeRemove($entity, array &$visited) |
||
1909 | |||
1910 | /** |
||
1911 | * Acquire a lock on the given entity. |
||
1912 | 38 | * |
|
1913 | * @param object $entity |
||
1914 | 38 | * @param int $lockMode |
|
1915 | * @param int $lockVersion |
||
1916 | * |
||
1917 | * @return void |
||
1918 | * |
||
1919 | * @throws ORMInvalidArgumentException |
||
1920 | * @throws TransactionRequiredException |
||
1921 | * @throws OptimisticLockException |
||
1922 | */ |
||
1923 | public function lock($entity, $lockMode, $lockVersion = null) |
||
1976 | |||
1977 | 15 | /** |
|
1978 | * Clears the UnitOfWork. |
||
1979 | * |
||
1980 | * @return void |
||
1981 | 15 | */ |
|
1982 | public function clear() |
||
2007 | |||
2008 | /** |
||
2009 | * INTERNAL: |
||
2010 | * Schedules an orphaned entity for removal. The remove() operation will be |
||
2011 | * invoked on that entity at the beginning of the next commit of this |
||
2012 | * UnitOfWork. |
||
2013 | * |
||
2014 | * @ignore |
||
2015 | * |
||
2016 | * @param object $entity |
||
2017 | * |
||
2018 | 16 | * @return void |
|
2019 | */ |
||
2020 | 16 | public function scheduleOrphanRemoval($entity) |
|
2024 | |||
2025 | /** |
||
2026 | * INTERNAL: |
||
2027 | * Cancels a previously scheduled orphan removal. |
||
2028 | * |
||
2029 | * @ignore |
||
2030 | * |
||
2031 | * @param object $entity |
||
2032 | * |
||
2033 | * @return void |
||
2034 | */ |
||
2035 | 16 | public function cancelOrphanRemoval($entity) |
|
2039 | 16 | ||
2040 | /** |
||
2041 | * INTERNAL: |
||
2042 | * Schedules a complete collection for removal when this UnitOfWork commits. |
||
2043 | 16 | * |
|
2044 | * @param PersistentCollection $coll |
||
2045 | 16 | * |
|
2046 | * @return void |
||
2047 | 16 | */ |
|
2048 | public function scheduleCollectionDeletion(PersistentCollection $coll) |
||
2058 | |||
2059 | /** |
||
2060 | * @param PersistentCollection $coll |
||
2061 | * |
||
2062 | * @return bool |
||
2063 | */ |
||
2064 | public function isCollectionScheduledForDeletion(PersistentCollection $coll) |
||
2068 | |||
2069 | 16 | /** |
|
2070 | * INTERNAL: |
||
2071 | 16 | * Creates a new instance of the mapped class, without invoking the constructor. |
|
2072 | 16 | * This is only meant to be used internally, and should not be consumed by end users. |
|
2073 | * |
||
2074 | * @ignore |
||
2075 | * |
||
2076 | 16 | * @param ClassMetadata $class |
|
2077 | 5 | * |
|
2078 | * @return EntityManagerAware|object |
||
2079 | */ |
||
2080 | 5 | public function newInstance(ClassMetadata $class) |
|
2090 | 5 | ||
2091 | /** |
||
2092 | * INTERNAL: |
||
2093 | * Creates an entity. Used for reconstitution of persistent entities. |
||
2094 | * |
||
2095 | * Internal note: Highly performance-sensitive method. |
||
2096 | 5 | * |
|
2097 | * @ignore |
||
2098 | * |
||
2099 | * @param string $className The name of the entity class. |
||
2100 | 16 | * @param array $data The data for the entity. |
|
2101 | * @param array $hints Any hints to account for during reconstitution/lookup of the entity. |
||
2102 | * |
||
2103 | * @return object The managed entity instance. |
||
2104 | * |
||
2105 | * @todo Rename: getOrCreateEntity |
||
2106 | */ |
||
2107 | public function createEntity($className, array $data, &$hints = []) |
||
2401 | 1218 | ||
2402 | 7 | /** |
|
2403 | * @return void |
||
2404 | 1218 | */ |
|
2405 | public function triggerEagerLoads() |
||
2427 | |||
2428 | /** |
||
2429 | * Initializes (loads) an uninitialized persistent collection of an entity. |
||
2430 | * |
||
2431 | * @param \Doctrine\ORM\PersistentCollection $collection The collection to initialize. |
||
2432 | * |
||
2433 | 112 | * @return void |
|
2434 | * |
||
2435 | 112 | * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733. |
|
2436 | 112 | */ |
|
2437 | public function loadCollection(PersistentCollection $collection) |
||
2450 | |||
2451 | /** |
||
2452 | 13 | * Gets the identity map of the UnitOfWork. |
|
2453 | * |
||
2454 | 13 | * @return array |
|
2455 | 13 | */ |
|
2456 | public function getIdentityMap() |
||
2460 | |||
2461 | /** |
||
2462 | * Gets the original data of an entity. The original data is the data that was |
||
2463 | * present at the time the entity was reconstituted from the database. |
||
2464 | * |
||
2465 | * @param object $entity |
||
2466 | * |
||
2467 | * @return array |
||
2468 | */ |
||
2469 | public function getOriginalEntityData($entity) |
||
2477 | 4 | ||
2478 | /** |
||
2479 | * @ignore |
||
2480 | 668 | * |
|
2481 | * @param object $entity |
||
2482 | * @param array $data |
||
2483 | * |
||
2484 | * @return void |
||
2485 | */ |
||
2486 | public function setOriginalEntityData($entity, array $data) |
||
2490 | |||
2491 | /** |
||
2492 | * INTERNAL: |
||
2493 | * Sets a property value of the original data array of an entity. |
||
2494 | * |
||
2495 | * @ignore |
||
2496 | * |
||
2497 | * @param string $oid |
||
2498 | * @param string $property |
||
2499 | 806 | * @param mixed $value |
|
2500 | * |
||
2501 | 806 | * @return void |
|
2502 | */ |
||
2503 | public function setOriginalEntityProperty($oid, $property, $value) |
||
2507 | 806 | ||
2508 | 310 | /** |
|
2509 | 310 | * Gets the identifier of an entity. |
|
2510 | * The returned value is always an array of identifier values. If the entity |
||
2511 | * has a composite identifier then the identifier values are in the same |
||
2512 | 310 | * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames(). |
|
2513 | 310 | * |
|
2514 | 310 | * @param object $entity |
|
2515 | 310 | * |
|
2516 | 310 | * @return array The identifier values. |
|
2517 | */ |
||
2518 | public function getEntityIdentifier($entity) |
||
2522 | 2 | ||
2523 | 2 | /** |
|
2524 | * Processes an entity instance to extract their identifier values. |
||
2525 | * |
||
2526 | 2 | * @param object $entity The entity instance. |
|
2527 | * |
||
2528 | * @return mixed A scalar value. |
||
2529 | 308 | * |
|
2530 | 21 | * @throws \Doctrine\ORM\ORMInvalidArgumentException |
|
2531 | */ |
||
2532 | 21 | public function getSingleIdentifierValue($entity) |
|
2547 | |||
2548 | 111 | /** |
|
2549 | 3 | * @param array $id |
|
2550 | * @param string $rootClassName |
||
2551 | * |
||
2552 | 308 | * @return GhostObjectInterface|object |
|
2553 | */ |
||
2554 | private function tryGetByIdOrLoadProxy(array $id, string $rootClassName) |
||
2580 | |||
2581 | /** |
||
2582 | 702 | * Tries to find an entity with the given identifier in the identity map of |
|
2583 | * this UnitOfWork. |
||
2584 | 702 | * |
|
2585 | * @param mixed $id The entity identifier to look for. |
||
2586 | * @param string $rootClassName The name of the root class of the mapped entity hierarchy. |
||
2587 | * |
||
2588 | * @return object|bool Returns the entity with the specified identifier if it exists in |
||
2589 | 702 | * this UnitOfWork, FALSE otherwise. |
|
2590 | 33 | */ |
|
2591 | public function tryGetById($id, $rootClassName) |
||
2599 | 564 | ||
2600 | /** |
||
2601 | * Schedules an entity for dirty-checking at commit-time. |
||
2602 | 564 | * |
|
2603 | 485 | * @param object $entity The entity to schedule for dirty-checking. |
|
2604 | * |
||
2605 | * @return void |
||
2606 | 64 | */ |
|
2607 | public function scheduleForSynchronization($entity) |
||
2613 | 2 | ||
2614 | /** |
||
2615 | * Checks whether the UnitOfWork has any pending insertions. |
||
2616 | * |
||
2617 | 62 | * @return boolean TRUE if this UnitOfWork has pending insertions, FALSE otherwise. |
|
2618 | */ |
||
2619 | 62 | public function hasPendingInsertions() |
|
2623 | 485 | ||
2624 | 38 | /** |
|
2625 | 38 | * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the |
|
2626 | * number of entities in the identity map. |
||
2627 | 38 | * |
|
2628 | * @return integer |
||
2629 | */ |
||
2630 | 478 | public function size() |
|
2634 | 478 | ||
2635 | 478 | /** |
|
2636 | * Gets the EntityPersister for an Entity. |
||
2637 | 478 | * |
|
2638 | * @param string $entityName The name of the Entity. |
||
2639 | 287 | * |
|
2640 | * @return \Doctrine\ORM\Persisters\Entity\EntityPersister |
||
2641 | 287 | */ |
|
2642 | public function getEntityPersister($entityName) |
||
2678 | |||
2679 | /** |
||
2680 | * Gets a collection persister for a collection-valued association. |
||
2681 | 166 | * |
|
2682 | * @param ToManyAssociationMetadata $association |
||
2683 | 192 | * |
|
2684 | * @return \Doctrine\ORM\Persisters\Collection\CollectionPersister |
||
2685 | */ |
||
2686 | public function getCollectionPersister(ToManyAssociationMetadata $association) |
||
2711 | |||
2712 | 163 | /** |
|
2713 | 163 | * INTERNAL: |
|
2714 | 163 | * Registers an entity as managed. |
|
2715 | * |
||
2716 | * @param object $entity The entity. |
||
2717 | 163 | * @param array $id Map containing identifier field names as key and its associated values. |
|
2718 | 163 | * @param array $data The original entity data. |
|
2719 | * |
||
2720 | * @return void |
||
2721 | */ |
||
2722 | 163 | public function registerManaged($entity, array $id, array $data) |
|
2737 | |||
2738 | /** |
||
2739 | 486 | * INTERNAL: |
|
2740 | * Clears the property changeset of the entity with the given OID. |
||
2741 | * |
||
2742 | * @param string $oid The entity's OID. |
||
2743 | * |
||
2744 | 486 | * @return void |
|
2745 | */ |
||
2746 | 3 | public function clearEntityChangeSet($oid) |
|
2750 | |||
2751 | 3 | /* PropertyChangedListener implementation */ |
|
2752 | |||
2753 | /** |
||
2754 | * Notifies this UnitOfWork of a property change in an entity. |
||
2755 | 486 | * |
|
2756 | 486 | * @param object $entity The entity that owns the property. |
|
2757 | 486 | * @param string $propertyName The name of the property that changed. |
|
2758 | * @param mixed $oldValue The old value of the property. |
||
2759 | 486 | * @param mixed $newValue The new value of the property. |
|
2760 | 486 | * |
|
2761 | * @return void |
||
2762 | 486 | */ |
|
2763 | 4 | public function propertyChanged($entity, $propertyName, $oldValue, $newValue) |
|
2780 | |||
2781 | /** |
||
2782 | * Gets the currently scheduled entity insertions in this UnitOfWork. |
||
2783 | 861 | * |
|
2784 | * @return array |
||
2785 | 861 | */ |
|
2786 | 861 | public function getScheduledEntityInsertions() |
|
2790 | 6 | ||
2791 | 6 | /** |
|
2792 | * Gets the currently scheduled entity updates in this UnitOfWork. |
||
2793 | 6 | * |
|
2794 | 6 | * @return array |
|
2795 | */ |
||
2796 | public function getScheduledEntityUpdates() |
||
2800 | 6 | ||
2801 | 6 | /** |
|
2802 | * Gets the currently scheduled entity deletions in this UnitOfWork. |
||
2803 | * |
||
2804 | 6 | * @return array |
|
2805 | */ |
||
2806 | public function getScheduledEntityDeletions() |
||
2810 | |||
2811 | /** |
||
2812 | * Gets the currently scheduled complete collection deletions |
||
2813 | * |
||
2814 | * @return array |
||
2815 | 142 | */ |
|
2816 | public function getScheduledCollectionDeletions() |
||
2820 | 142 | ||
2821 | 142 | /** |
|
2822 | 76 | * Gets the currently scheduled collection inserts, updates and deletes. |
|
2823 | 76 | * |
|
2824 | * @return array |
||
2825 | 80 | */ |
|
2826 | 80 | public function getScheduledCollectionUpdates() |
|
2830 | 142 | ||
2831 | 142 | /** |
|
2832 | * Helper method to initialize a lazy loading proxy or persistent collection. |
||
2833 | * |
||
2834 | * @param object $obj |
||
2835 | * |
||
2836 | * @return void |
||
2837 | */ |
||
2838 | 2 | public function initializeObject($obj) |
|
2850 | |||
2851 | 115 | /** |
|
2852 | * Helper method to show an object as string. |
||
2853 | 115 | * |
|
2854 | * @param object $obj |
||
2855 | 115 | * |
|
2856 | 112 | * @return string |
|
2857 | 115 | */ |
|
2858 | private static function objToStr($obj) |
||
2862 | |||
2863 | /** |
||
2864 | * Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit(). |
||
2865 | * |
||
2866 | * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information |
||
2867 | * on this object that might be necessary to perform a correct update. |
||
2868 | * |
||
2869 | * @param object $object |
||
2870 | * |
||
2871 | * @return void |
||
2872 | * |
||
2873 | * @throws ORMInvalidArgumentException |
||
2874 | */ |
||
2875 | public function markReadOnly($object) |
||
2883 | |||
2884 | /** |
||
2885 | 313 | * Is this entity read only? |
|
2886 | * |
||
2887 | 313 | * @param object $object |
|
2888 | 313 | * |
|
2889 | * @return bool |
||
2890 | * |
||
2891 | * @throws ORMInvalidArgumentException |
||
2892 | */ |
||
2893 | public function isReadOnly($object) |
||
2901 | |||
2902 | 842 | /** |
|
2903 | * Perform whatever processing is encapsulated here after completion of the transaction. |
||
2904 | */ |
||
2905 | private function afterTransactionComplete() |
||
2911 | |||
2912 | /** |
||
2913 | * Perform whatever processing is encapsulated here after completion of the rolled-back. |
||
2914 | 126 | */ |
|
2915 | private function afterTransactionRolledBack() |
||
2921 | |||
2922 | 126 | /** |
|
2923 | 113 | * Performs an action after the transaction. |
|
2924 | 126 | * |
|
2925 | * @param callable $callback |
||
2926 | 126 | */ |
|
2927 | private function performCallbackOnCachedPersister(callable $callback) |
||
2939 | 522 | ||
2940 | private function dispatchOnFlushEvent() |
||
2946 | |||
2947 | private function dispatchPostFlushEvent() |
||
2953 | |||
2954 | /** |
||
2955 | * Verifies if two given entities actually are the same based on identifier comparison |
||
2956 | * |
||
2957 | 5 | * @param object $entity1 |
|
2958 | * @param object $entity2 |
||
2959 | 5 | * |
|
2960 | * @return bool |
||
2961 | 5 | */ |
|
2962 | 5 | private function isIdentifierEquals($entity1, $entity2) |
|
2989 | |||
2990 | /** |
||
2991 | * This method called by hydrators, and indicates that hydrator totally completed current hydration cycle. |
||
2992 | * Unit of work able to fire deferred events, related to loading events here. |
||
2993 | * |
||
2994 | 1063 | * @internal should be called internally from object hydrators |
|
2995 | */ |
||
2996 | 1063 | public function hydrationComplete() |
|
3000 | } |
||
3001 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.