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.