Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/en/reference/events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ the life-time of their registered documents.
``postLoad`` - The postLoad event occurs for a document after the
document has been loaded into the current DocumentManager from the
database or after the refresh operation has been applied to it.
-
``onReference`` - The onReference event occurs when a lazy document reference
is created. The data of the document are not loaded yet at this point; if
you access any mapped field of the document, a database query will be
triggered to load the document data. Which defeats the purpose of using
lazy references in the first place.
This can be used to inject dependencies into unmapped properties.
-
``loadClassMetadata`` - The loadClassMetadata event occurs after the
mapping metadata for a class has been loaded from a mapping source
Expand Down
12 changes: 12 additions & 0 deletions src/Events.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,18 @@ private function __construct()
*/
public const postLoad = 'postLoad';

/**
* The onReference event occurs when a lazy document reference is created.
*
* Note that the data of the document are not loaded yet at this point; if
* you access any mapped field of the document, a database query will be
* triggered to load the document data. Which defeats the purpose of using
* lazy references in the first place.
*
* This is a document lifecycle event.
*/
public const onReference = 'onReference';

/**
* The loadClassMetadata event occurs after the mapping metadata for a class
* has been loaded from a mapping source (annotations/xml).
Expand Down
12 changes: 7 additions & 5 deletions src/Proxy/Factory/LazyGhostProxyFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,18 +230,20 @@ private function getProxyFactory(string $className): Closure
$reflector = $reflector->getParentClass();
}

$className = $class->getName(); // aliases and case sensitivity
$entityPersister = $this->uow->getDocumentPersister($className);
$initializer = $this->createLazyInitializer($class, $entityPersister);
$proxyClassName = $this->loadProxyClass($class);
$className = $class->getName(); // aliases and case sensitivity
$entityPersister = $this->uow->getDocumentPersister($className);
$initializer = $this->createLazyInitializer($class, $entityPersister);
$proxyClassName = $this->loadProxyClass($class);
$lifecycleEventManager = $this->lifecycleEventManager;

$proxyFactory = Closure::bind(static function (mixed $identifier) use ($initializer, $skippedProperties, $class): InternalProxy {
$proxyFactory = Closure::bind(static function (mixed $identifier) use ($initializer, $skippedProperties, $class, $lifecycleEventManager): InternalProxy {
/** @see LazyGhostTrait::createLazyGhost() */
$proxy = static::createLazyGhost(static function (InternalProxy $object) use ($initializer, $identifier): void {
$initializer($object, $identifier);
}, $skippedProperties);

$class->setIdentifierValue($proxy, $identifier);
$lifecycleEventManager->onReference($proxy);

return $proxy;
}, null, $proxyClassName);
Expand Down
2 changes: 2 additions & 0 deletions src/Proxy/Factory/NativeLazyObjectFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ public function getProxy(ClassMetadata $metadata, $identifier): object
self::$lazyObjects[$proxy] = true;
}

$this->lifecycleEventManager->onReference($proxy);

return $proxy;
}

Expand Down
1 change: 1 addition & 0 deletions src/Proxy/Factory/StaticProxyFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public function getProxy(ClassMetadata $metadata, $identifier): GhostObjectInter
);

$metadata->setIdentifierValue($ghostObject, $identifier);
$this->lifecycleEventManager->onReference($ghostObject);

return $ghostObject;
}
Expand Down
5 changes: 5 additions & 0 deletions src/Utility/LifecycleEventManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ public function postUpdate(ClassMetadata $class, object $document, ?Session $ses
$this->cascadePostUpdate($class, $document, $session);
}

public function onReference(object $document): void
{
$this->evm->dispatchEvent(Events::onReference, new LifecycleEventArgs($document, $this->dm));
}

/**
* Invokes prePersist callbacks and events for given document.
*
Expand Down
32 changes: 32 additions & 0 deletions tests/Tests/Events/OnReferenceEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Tests\Events;

use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs;
use Doctrine\ODM\MongoDB\Events;
use Doctrine\ODM\MongoDB\Tests\BaseTestCase;
use Documents\User;

class OnReferenceEvent extends BaseTestCase
{
public function testOnReferenceEventIsDispatchedByGetReference(): void
{
$this->dm->getEventManager()->addEventListener(Events::onReference, $listener = new class {
public object $eventArgs;

public function onReference($eventArgs): void

Check failure on line 19 in tests/Tests/Events/OnReferenceEvent.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.4)

Method class@anonymous/tests/Tests/Events/OnReferenceEvent.php:16::onReference() has parameter $eventArgs with no type specified.
{
$this->eventArgs = $eventArgs;
}
});

$this->dm->getReference(User::class, '123456789012345678901234');

self::assertTrue(isset($listener->eventArgs), 'onReference event was not dispatched');
self::assertInstanceOf(LifecycleEventArgs::class, $listener->eventArgs);
self::assertInstanceOf(User::class, $listener->eventArgs->getObject());
self::assertTrue($this->dm->isUninitializedObject($listener->eventArgs->getObject()));
}
}
Loading