Skip to content

Commit

Permalink
Implement ObjectManager::isUninitializedObject (#2569)
Browse files Browse the repository at this point in the history
  • Loading branch information
malarzm authored Nov 2, 2023
1 parent 0aa0e7b commit b7c7838
Show file tree
Hide file tree
Showing 13 changed files with 65 additions and 53 deletions.
8 changes: 8 additions & 0 deletions lib/Doctrine/ODM/MongoDB/DocumentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,14 @@ public function initializeObject($obj)
$this->unitOfWork->initializeObject($obj);
}

/**
* Helper method to check whether a lazy loading proxy or persistent collection has been initialized.
*/
public function isUninitializedObject(object $obj): bool
{
return $this->unitOfWork->isUninitializedObject($obj);
}

/**
* Gets the UnitOfWork used by the DocumentManager to coordinate operations.
*/
Expand Down
5 changes: 2 additions & 3 deletions lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
use MongoDB\Driver\Exception\WriteException;
use MongoDB\Driver\WriteConcern;
use MongoDB\GridFS\Bucket;
use ProxyManager\Proxy\GhostObjectInterface;
use stdClass;

use function array_combine;
Expand Down Expand Up @@ -749,7 +748,7 @@ private function loadReferenceManyCollectionOwningSide(PersistentCollectionInter
}

// only query for the referenced object if it is not already initialized or the collection is sorted
if (! (($reference instanceof GhostObjectInterface && ! $reference->isProxyInitialized())) && ! $sorted) {
if (! $this->uow->isUninitializedObject($reference) && ! $sorted) {
continue;
}

Expand Down Expand Up @@ -787,7 +786,7 @@ private function loadReferenceManyCollectionOwningSide(PersistentCollectionInter
$documents = $cursor->toArray();
foreach ($documents as $documentData) {
$document = $this->uow->getById($documentData['_id'], $class);
if ($document instanceof GhostObjectInterface && ! $document->isProxyInitialized()) {
if ($this->uow->isUninitializedObject($document)) {
$data = $this->hydratorFactory->hydrate($document, $documentData);
$this->uow->setOriginalDocumentData($document, $data);
}
Expand Down
5 changes: 2 additions & 3 deletions lib/Doctrine/ODM/MongoDB/Query/ReferencePrimer.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use Doctrine\ODM\MongoDB\UnitOfWork;
use InvalidArgumentException;
use LogicException;
use ProxyManager\Proxy\GhostObjectInterface;
use Traversable;

use function array_push;
Expand Down Expand Up @@ -138,7 +137,7 @@ public function primeReferences(ClassMetadata $class, $documents, string $fieldN
continue;
}

if ($mapping['type'] === ClassMetadata::ONE && $fieldValue instanceof GhostObjectInterface && ! $fieldValue->isProxyInitialized()) {
if ($mapping['type'] === ClassMetadata::ONE && $this->uow->isUninitializedObject($fieldValue)) {
$refClass = $this->dm->getClassMetadata($fieldValue::class);
$id = $this->uow->getDocumentIdentifier($fieldValue);
$groupedIds[$refClass->name][serialize($id)] = $id;
Expand Down Expand Up @@ -269,7 +268,7 @@ private function addManyReferences(PersistentCollectionInterface $persistentColl

$document = $this->uow->tryGetById($id, $class);

if ($document && ! (($document instanceof GhostObjectInterface && ! $document->isProxyInitialized()))) {
if ($document && ! $this->uow->isUninitializedObject($document)) {
continue;
}

Expand Down
46 changes: 26 additions & 20 deletions lib/Doctrine/ODM/MongoDB/UnitOfWork.php
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ public function computeChangeSets(): void

foreach ($documentsToProcess as $document) {
// Ignore uninitialized proxy objects
if ($document instanceof GhostObjectInterface && ! $document->isProxyInitialized()) {
if ($this->isUninitializedObject($document)) {
continue;
}

Expand Down Expand Up @@ -1061,7 +1061,7 @@ private function computeAssociationChanges(object $parentDocument, array $assoc,
public function recomputeSingleDocumentChangeSet(ClassMetadata $class, object $document): void
{
// Ignore uninitialized proxy objects
if ($document instanceof GhostObjectInterface && ! $document->isProxyInitialized()) {
if ($this->isUninitializedObject($document)) {
return;
}

Expand Down Expand Up @@ -1489,10 +1489,7 @@ public function addToIdentityMap(object $document): bool

$this->identityMap[$class->name][$id] = $document;

if (
$document instanceof NotifyPropertyChanged &&
( ! $document instanceof GhostObjectInterface || $document->isProxyInitialized())
) {
if ($document instanceof NotifyPropertyChanged && ! $this->isUninitializedObject($document)) {
$document->addPropertyChangedListener($this);
}

Expand Down Expand Up @@ -1881,8 +1878,8 @@ private function doMerge(object $document, array &$visited, ?object $prevManaged
$managedCopy = $document;

if ($this->getDocumentState($document, self::STATE_DETACHED) !== self::STATE_MANAGED) {
if ($document instanceof GhostObjectInterface && ! $document->isProxyInitialized()) {
$document->initializeProxy();
if ($this->isUninitializedObject($document)) {
$this->initializeObject($document);
}

$identifier = $class->getIdentifier();
Expand All @@ -1899,8 +1896,8 @@ private function doMerge(object $document, array &$visited, ?object $prevManaged
throw new InvalidArgumentException('Removed entity detected during merge. Cannot merge with a removed entity.');
}

if ($managedCopy instanceof GhostObjectInterface && ! $managedCopy->isProxyInitialized()) {
$managedCopy->initializeProxy();
if ($managedCopy && $this->isUninitializedObject($managedCopy)) {
$this->initializeObject($managedCopy);
}
}

Expand Down Expand Up @@ -1946,7 +1943,7 @@ private function doMerge(object $document, array &$visited, ?object $prevManaged

if ($other === null) {
$prop->setValue($managedCopy, null);
} elseif ($other instanceof GhostObjectInterface && ! $other->isProxyInitialized()) {
} elseif ($this->isUninitializedObject($other)) {
// Do not merge fields marked lazy that have not been fetched
continue;
} elseif (! $assoc2['isCascadeMerge']) {
Expand Down Expand Up @@ -2305,9 +2302,7 @@ private function cascadeRemove(object $document, array &$visited): void
continue;
}

if ($document instanceof GhostObjectInterface && ! $document->isProxyInitialized()) {
$document->initializeProxy();
}
$this->initializeObject($document);

$relatedDocuments = $class->reflFields[$mapping['fieldName']]->getValue($document);
if ($relatedDocuments instanceof Collection || is_array($relatedDocuments)) {
Expand Down Expand Up @@ -2459,10 +2454,7 @@ private function fixPersistentCollectionOwnership(PersistentCollectionInterface
if ($owner === null) { // cloned
$coll->setOwner($document, $class->fieldMappings[$propName]);
} elseif ($owner !== $document) { // no clone, we have to fix
if (! $coll->isInitialized()) {
$coll->initialize(); // we have to do this otherwise the cols share state
}

$this->initializeObject($coll); // we have to do this otherwise the cols share state
$newValue = clone $coll;
$newValue->setOwner($document, $class->fieldMappings[$propName]);
$class->reflFields[$propName]->setValue($document, $newValue);
Expand Down Expand Up @@ -2807,7 +2799,7 @@ public function getOrCreateDocument(string $className, array $data, array &$hint
/** @psalm-var T $document */
$document = $this->identityMap[$class->name][$serializedId];
$oid = spl_object_hash($document);
if ($document instanceof GhostObjectInterface && ! $document->isProxyInitialized()) {
if ($this->isUninitializedObject($document)) {
$document->setProxyInitializer(null);
$overrideLocalValues = true;
if ($document instanceof NotifyPropertyChanged) {
Expand Down Expand Up @@ -3088,13 +3080,27 @@ public function getScheduledCollectionUpdates(): array
*/
public function initializeObject(object $obj): void
{
if ($obj instanceof GhostObjectInterface) {
if ($obj instanceof GhostObjectInterface && $obj->isProxyInitialized() === false) {
$obj->initializeProxy();
} elseif ($obj instanceof PersistentCollectionInterface) {
$obj->initialize();
}
}

/**
* Helper method to check whether a lazy loading proxy or persistent collection has been initialized.
*
* @internal
*/
public function isUninitializedObject(object $obj): bool
{
return match (true) {
$obj instanceof GhostObjectInterface => $obj->isProxyInitialized() === false,
$obj instanceof PersistentCollectionInterface => $obj->isInitialized() === false,
default => false
};
}

private function objToStr(object $obj): string
{
return method_exists($obj, '__toString') ? (string) $obj : $obj::class . '@' . spl_object_hash($obj);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function testGetIdentifierValue(): void
$userTest = $test->getUser();
self::assertEquals($user->getId(), $userTest->getId());
self::assertInstanceOf(LazyLoadingInterface::class, $userTest);
self::assertFalse($userTest->isProxyInitialized());
self::assertTrue($this->uow->isUninitializedObject($userTest));

$this->dm->clear();

Expand All @@ -43,10 +43,10 @@ public function testGetIdentifierValue(): void
self::assertEquals($user->getId(), $class->getIdentifierValue($user));
self::assertEquals($user->getId(), $class->getFieldValue($foundUser, 'id'));
self::assertInstanceOf(LazyLoadingInterface::class, $foundUser);
self::assertFalse($foundUser->isProxyInitialized());
self::assertTrue($this->uow->isUninitializedObject($foundUser));

self::assertEquals('jwage', $foundUser->getUsername());
self::assertTrue($foundUser->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($foundUser));
}

public function testIdentifiersAreSet(): void
Expand Down
20 changes: 10 additions & 10 deletions tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public function testPrimeReferencesWithDBRefObjects(): void

foreach ($qb->getQuery() as $user) {
self::assertInstanceOf(GhostObjectInterface::class, $user->getAccount());
self::assertTrue($user->getAccount()->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($user->getAccount()));

self::assertCount(2, $user->getGroups());

Expand Down Expand Up @@ -134,7 +134,7 @@ public function testPrimeReferencesWithSimpleReferences(): void

foreach ($qb->getQuery() as $simpleUser) {
self::assertInstanceOf(GhostObjectInterface::class, $simpleUser->getUser());
self::assertTrue($simpleUser->getUser()->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($simpleUser->getUser()));

self::assertCount(2, $simpleUser->getUsers());

Expand Down Expand Up @@ -197,7 +197,7 @@ public function testPrimeReferencesNestedInNamedEmbeddedReference(): void
self::assertInstanceOf(EmbeddedWhichReferences::class, $embeddedDoc);

self::assertInstanceOf(GhostObjectInterface::class, $embeddedDoc->referencedDoc);
self::assertTrue($embeddedDoc->referencedDoc->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($embeddedDoc->referencedDoc));

self::assertCount(2, $embeddedDoc->referencedDocs);
foreach ($embeddedDoc->referencedDocs as $referencedDoc) {
Expand Down Expand Up @@ -253,7 +253,7 @@ public function testPrimeReferencesWithDifferentStoreAsReferences(): void
$user = $referenceUser->getUser();
self::assertInstanceOf(User::class, $user);
self::assertInstanceOf(GhostObjectInterface::class, $user);
self::assertTrue($user->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($user));

self::assertCount(1, $referenceUser->getUsers());

Expand All @@ -265,7 +265,7 @@ public function testPrimeReferencesWithDifferentStoreAsReferences(): void
$parentUser = $referenceUser->getParentUser();
self::assertInstanceOf(GhostObjectInterface::class, $parentUser);
self::assertInstanceOf(User::class, $parentUser);
self::assertTrue($parentUser->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($parentUser));

self::assertCount(1, $referenceUser->getParentUsers());

Expand All @@ -277,7 +277,7 @@ public function testPrimeReferencesWithDifferentStoreAsReferences(): void
$otherUser = $referenceUser->getOtherUser();
self::assertInstanceOf(User::class, $otherUser);
self::assertInstanceOf(GhostObjectInterface::class, $otherUser);
self::assertTrue($otherUser->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($otherUser));

self::assertCount(1, $referenceUser->getOtherUsers());

Expand Down Expand Up @@ -332,7 +332,7 @@ public function testPrimeReferencesWithDiscriminatedReferenceOne(): void

foreach ($qb->getQuery() as $agent) {
self::assertInstanceOf(GhostObjectInterface::class, $agent->server);
self::assertTrue($agent->server->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($agent->server));
}
}

Expand Down Expand Up @@ -523,7 +523,7 @@ public function testPrimeEmbeddedReferenceTwoLevelsDeep(): void

self::assertInstanceOf(GhostObjectInterface::class, $currency);
self::assertInstanceOf(Currency::class, $currency);
self::assertTrue($currency->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($currency));
}

public function testPrimeReferencesInReferenceMany(): void
Expand All @@ -550,7 +550,7 @@ public function testPrimeReferencesInReferenceMany(): void

$comment = $post->comments->first();
self::assertInstanceOf(GhostObjectInterface::class, $comment->author);
self::assertTrue($comment->author->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($comment->author));
}

public function testPrimeReferencesInReferenceManyWithRepositoryMethodEager(): void
Expand All @@ -577,6 +577,6 @@ public function testPrimeReferencesInReferenceManyWithRepositoryMethodEager(): v

$comment = $post->repoCommentsWithPrimer->first();
self::assertInstanceOf(GhostObjectInterface::class, $comment->author);
self::assertTrue($comment->author->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($comment->author));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public function testLazyLoadedWithNotifyPropertyChanged(): void
$user = $this->dm->find($user::class, $user->getId());
$profile = $user->getProfileNotify();
self::assertInstanceOf(GhostObjectInterface::class, $profile);
self::assertFalse($profile->isProxyInitialized());
self::assertTrue($this->uow->isUninitializedObject($profile));

$user->getProfileNotify()->setLastName('Malarz');
$this->dm->flush();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ public function testProxy(): void
self::assertNotNull($user);
self::assertInstanceOf(User::class, $user);
self::assertInstanceOf(GhostObjectInterface::class, $user);
self::assertFalse($user->isProxyInitialized());
self::assertTrue($this->uow->isUninitializedObject($user));
self::assertEquals('jwage', $user->getUsername());
self::assertTrue($user->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($user));
}

public function testPersistentCollectionOwningSide(): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function testPrimeWithGetSingleResult(): void

self::assertInstanceOf(GH520Document::class, $document);
self::assertInstanceOf(GhostObjectInterface::class, $document->ref);
self::assertTrue($document->ref->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($document->ref));
}

public function testPrimeWithGetSingleResultWillNotPrimeEntireResultSet(): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ public function testReferenceManyOwningSidePreparesFilterCriteria(): void
self::assertCount(2, $user1following);

self::assertInstanceOf(GhostObjectInterface::class, $user1following[0]);
self::assertTrue($user1following[0]->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($user1following[0]));
self::assertEquals($user2->getId(), $user1following[0]->getId());

self::assertInstanceOf(GhostObjectInterface::class, $user1following[1]);
self::assertFalse($user1following[1]->isProxyInitialized());
self::assertTrue($this->uow->isUninitializedObject($user1following[1]));
self::assertEquals($user3->getId(), $user1following[1]->getId());

$this->expectException(DocumentNotFoundException::class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ public function testReferenceManyOwningSidePreparesFilterCriteriaForDifferentCla
self::assertCount(2, $user1likes);

self::assertInstanceOf(GhostObjectInterface::class, $user1likes[0]);
self::assertTrue($user1likes[0]->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($user1likes[0]));
self::assertEquals($thing1->getId(), $user1likes[0]->getId());

self::assertInstanceOf(GhostObjectInterface::class, $user1likes[1]);
self::assertFalse($user1likes[1]->isProxyInitialized());
self::assertTrue($this->uow->isUninitializedObject($user1likes[1]));
self::assertEquals($thing2->getId(), $user1likes[1]->getId());

$this->expectException(DocumentNotFoundException::class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ public function testA(Closure $idGenerator): void

self::assertInstanceOf(GhostObjectInterface::class, $parent->refOne);
self::assertInstanceOf(GH852Document::class, $parent->refOne);
self::assertFalse($parent->refOne->isProxyInitialized());
self::assertTrue($this->uow->isUninitializedObject($parent->refOne));
self::assertEquals($idGenerator('childA'), $parent->refOne->id);
self::assertEquals('childA', $parent->refOne->name);
self::assertTrue($parent->refOne->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($parent->refOne));

self::assertCount(2, $parent->refMany);

Expand All @@ -63,13 +63,13 @@ public function testA(Closure $idGenerator): void
*/
self::assertInstanceOf(GhostObjectInterface::class, $parent->refMany[0]);
self::assertInstanceOf(GH852Document::class, $parent->refMany[0]);
self::assertTrue($parent->refMany[0]->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($parent->refMany[0]));
self::assertEquals($idGenerator('childB'), $parent->refMany[0]->id);
self::assertEquals('childB', $parent->refMany[0]->name);

self::assertInstanceOf(GhostObjectInterface::class, $parent->refMany[1]);
self::assertInstanceOf(GH852Document::class, $parent->refMany[1]);
self::assertTrue($parent->refMany[1]->isProxyInitialized());
self::assertFalse($this->uow->isUninitializedObject($parent->refMany[1]));
self::assertEquals($idGenerator('childC'), $parent->refMany[1]->id);
self::assertEquals('childC', $parent->refMany[1]->name);

Expand Down
Loading

0 comments on commit b7c7838

Please sign in to comment.