From 17b996da8cf7bfd75e956e44a5bc80eb9faa32b8 Mon Sep 17 00:00:00 2001 From: Darien Hager Date: Tue, 29 Sep 2015 18:44:47 -0700 Subject: [PATCH] Speculative fix: Defer any errors for missing cascade-persist until object graph has been better-explored --- lib/Doctrine/ORM/UnitOfWork.php | 39 +++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 4f8ce32db..7d4d8d80a 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -178,6 +178,19 @@ class UnitOfWork implements PropertyChangedListener */ private $entityDeletions = []; + /** + * New entities that were discovered through relationships that were not + * marked as cascade-persist. During flush, this array is populated and + * then pruned of any entities that were discovered through a valid + * cascade-persist path. (Leftovers cause an error.) + * + * Keys are OIDs, payload is a two-item array describing the association + * and the entity. + * + * @var array + */ + private $newEntitiesWithoutCascade = array(); + /** * All pending collection deletions. * @@ -427,6 +440,7 @@ class UnitOfWork implements PropertyChangedListener $this->entityDeletions = $this->extraUpdates = $this->collectionUpdates = + $this->newEntitiesWithoutCascade = $this->collectionDeletions = $this->visitedCollections = $this->orphanRemovals = []; @@ -815,6 +829,16 @@ class UnitOfWork implements PropertyChangedListener } } } + + /** + * Filter out any entities that we (successfully) managed to schedule + * for insertion. + */ + $entitiesNeedingCascadePersist = array_diff_key($this->newEntitiesWithoutCascade, $this->entityInsertions); + if(count($entitiesNeedingCascadePersist) > 0){ + list($assoc,$entity) = array_values($entitiesNeedingCascadePersist)[0]; + throw ORMInvalidArgumentException::newEntityFoundThroughRelationship($assoc, $entity); + } } /** @@ -861,11 +885,17 @@ class UnitOfWork implements PropertyChangedListener switch ($state) { case self::STATE_NEW: if ( ! $assoc['isCascadePersist']) { - throw ORMInvalidArgumentException::newEntityFoundThroughRelationship($assoc, $entry); + /* + * For now just record the details, because this may + * not be an issue if we later discover another pathway + * through the object-graph where cascade-persistence + * is enabled for this object. + */ + $this->newEntitiesWithoutCascade[spl_object_hash($entry)] = array($assoc,$entry); + }else { + $this->persistNew($targetClass, $entry); + $this->computeChangeSet($targetClass, $entry); } - - $this->persistNew($targetClass, $entry); - $this->computeChangeSet($targetClass, $entry); break; case self::STATE_REMOVED: @@ -2411,6 +2441,7 @@ class UnitOfWork implements PropertyChangedListener $this->entityInsertions = $this->entityUpdates = $this->entityDeletions = + $this->newEntitiesWithoutCascade = $this->collectionDeletions = $this->collectionUpdates = $this->extraUpdates =