Skip to content

Commit

Permalink
Merge pull request #1336 from alcaeus/proxy-initialization-failed-event
Browse files Browse the repository at this point in the history
Dispatch an event when proxy initialization fails
  • Loading branch information
alcaeus committed Jan 24, 2016
2 parents 957ab47 + 04f4555 commit 453ef86
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 13 deletions.
33 changes: 33 additions & 0 deletions docs/en/reference/events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ the life-time of their registered documents.
-
onClear - The onClear event occurs after the UnitOfWork has had
its state cleared.
-
documentNotFound - The documentNotFound event occurs when a proxy object
could not be initialized. This event is not a lifecycle callback.

You can access the Event constants from the ``Events`` class in the
ODM package.
Expand Down Expand Up @@ -557,6 +560,36 @@ Define the ``EventTest`` class with a ``onClear()`` method:
}
}
documentNotFound
~~~~~~~~~~~~~~~~

.. code-block:: php
<?php
$test = new EventTest();
$evm = $dm->getEventManager();
$evm->addEventListener(Events::documentNotFound, $test);
Define the ``EventTest`` class with a ``documentNotFound()`` method:

.. code-block:: php
<?php
class EventTest
{
public function documentNotFound(\Doctrine\ODM\MongoDB\Event\DocumentNotFoundEventArgs $eventArgs)
{
$proxy = $eventArgs->getObject();
$identifier = $eventArgs->getIdentifier();
// do something
// To prevent the documentNotFound exception from being thrown, call the disableException() method:
$eventArgs->disableException();
}
}
postUpdate, postRemove, postPersist
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
88 changes: 88 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Event/DocumentNotFoundEventArgs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/

namespace Doctrine\ODM\MongoDB\Event;

use Doctrine\ODM\MongoDB\DocumentManager;

/**
* Provides event arguments for the documentNotFound event.
*
* @since 1.1
*/
class DocumentNotFoundEventArgs extends LifecycleEventArgs
{
/**
* @var string
*/
private $identifier;

/**
* @var bool
*/
private $disableException = false;

/**
* Constructor.
*
* @param object $document
* @param DocumentManager $dm
* @param string $identifier
*/
public function __construct($document, DocumentManager $dm, $identifier)
{
parent::__construct($document, $dm);
$this->identifier = $identifier;
}

/**
* Retrieve associated identifier.
*
* @return string
*/
public function getIdentifier()
{
return $this->identifier;
}

/**
* Indicates whether the proxy initialization exception is disabled.
*
* @return bool
*/
public function isExceptionDisabled()
{
return $this->disableException;
}

/**
* Disable the throwing of an exception
*
* This method indicates to the proxy initializer that the missing document
* has been handled and no exception should be thrown. This can't be reset.
*
* @param bool $disableException
*/
public function disableException($disableException = true)
{
$this->disableException = $disableException;
}
}
8 changes: 8 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Events.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,12 @@ private function __construct() {}
* @var string
*/
const onClear = 'onClear';

/**
* The documentNotFound event occurs if a proxy object could not be found in
* the database.
*
* @var string
*/
const documentNotFound = 'documentNotFound';
}
36 changes: 23 additions & 13 deletions lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
use Doctrine\Common\Proxy\Proxy as BaseProxy;
use Doctrine\Common\Persistence\Mapping\ClassMetadata as BaseClassMetadata;
use Doctrine\ODM\MongoDB\Persisters\DocumentPersister;
use Doctrine\ODM\MongoDB\Proxy\Proxy;
use Doctrine\ODM\MongoDB\Utility\LifecycleEventManager;
use ReflectionProperty;

/**
Expand All @@ -55,6 +55,11 @@ class ProxyFactory extends AbstractProxyFactory
*/
private $proxyNamespace;

/**
* @var \Doctrine\Common\EventManager
*/
private $lifecycleEventManager;

/**
* Initializes a new instance of the <tt>ProxyFactory</tt> class that is
* connected to the given <tt>DocumentManager</tt>.
Expand All @@ -68,9 +73,10 @@ class ProxyFactory extends AbstractProxyFactory
public function __construct(DocumentManager $documentManager, $proxyDir, $proxyNamespace, $autoGenerate = AbstractProxyFactory::AUTOGENERATE_NEVER)
{
$this->metadataFactory = $documentManager->getMetadataFactory();
$this->uow = $documentManager->getUnitOfWork();
$this->proxyNamespace = $proxyNamespace;
$proxyGenerator = new ProxyGenerator($proxyDir, $proxyNamespace);
$this->uow = $documentManager->getUnitOfWork();
$this->proxyNamespace = $proxyNamespace;
$this->lifecycleEventManager = new LifecycleEventManager($documentManager, $this->uow, $documentManager->getEventManager());
$proxyGenerator = new ProxyGenerator($proxyDir, $proxyNamespace);

$proxyGenerator->setPlaceholder('baseProxyInterface', Proxy::class);

Expand Down Expand Up @@ -121,10 +127,8 @@ private function createInitializer(
DocumentPersister $documentPersister,
ReflectionProperty $reflectionId
) {
$unitOfWork = $this->uow;

if ($classMetadata->getReflectionClass()->hasMethod('__wakeup')) {
return function (BaseProxy $proxy) use ($documentPersister, $reflectionId, $unitOfWork) {
return function (BaseProxy $proxy) use ($documentPersister, $reflectionId) {
$proxy->__setInitializer(null);
$proxy->__setCloner(null);

Expand All @@ -146,16 +150,18 @@ private function createInitializer(
$id = $reflectionId->getValue($proxy);

if (null === $documentPersister->load(array('_id' => $id), $proxy)) {
throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
if ( ! $this->lifecycleEventManager->documentNotFound($proxy, $id)) {
throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
}
}

if ($proxy instanceof NotifyPropertyChanged) {
$proxy->addPropertyChangedListener($unitOfWork);
$proxy->addPropertyChangedListener($this->uow);
}
};
}

return function (BaseProxy $proxy) use ($documentPersister, $reflectionId, $unitOfWork) {
return function (BaseProxy $proxy) use ($documentPersister, $reflectionId) {
$proxy->__setInitializer(null);
$proxy->__setCloner(null);

Expand All @@ -176,11 +182,13 @@ private function createInitializer(
$id = $reflectionId->getValue($proxy);

if (null === $documentPersister->load(array('_id' => $id), $proxy)) {
throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
if ( ! $this->lifecycleEventManager->documentNotFound($proxy, $id)) {
throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
}
}

if ($proxy instanceof NotifyPropertyChanged) {
$proxy->addPropertyChangedListener($unitOfWork);
$proxy->addPropertyChangedListener($this->uow);
}
};
}
Expand Down Expand Up @@ -213,7 +221,9 @@ private function createCloner(
$original = $documentPersister->load(array('_id' => $id));

if (null === $original) {
throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
if ( ! $this->lifecycleEventManager->documentNotFound($proxy, $id)) {
throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
}
}

foreach ($classMetadata->getReflectionClass()->getProperties() as $reflectionProperty) {
Expand Down
14 changes: 14 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Utility/LifecycleEventManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

use Doctrine\Common\EventManager;
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ODM\MongoDB\Event\DocumentNotFoundEventArgs;
use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs;
use Doctrine\ODM\MongoDB\Event\PreUpdateEventArgs;
use Doctrine\ODM\MongoDB\Events;
Expand Down Expand Up @@ -60,6 +61,19 @@ public function __construct(DocumentManager $dm, UnitOfWork $uow, EventManager $
$this->uow = $uow;
}

/**
* @param object $proxy
* @param mixed $id
* @return bool Returns whether the exceptionDisabled flag was set
*/
public function documentNotFound($proxy, $id)
{
$eventArgs = new DocumentNotFoundEventArgs($proxy, $this->dm, $id);
$this->evm->dispatchEvent(Events::documentNotFound, $eventArgs);

return $eventArgs->isExceptionDisabled();
}

/**
* Invokes postPersist callbacks and events for given document cascading them to embedded documents as well.
*
Expand Down
54 changes: 54 additions & 0 deletions tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Doctrine\ODM\MongoDB\Tests\Functional;

use Doctrine\ODM\MongoDB\Event\DocumentNotFoundEventArgs;
use Doctrine\ODM\MongoDB\Events;
use Documents\Address;
use Documents\Profile;
use Documents\ProfileNotify;
Expand Down Expand Up @@ -428,6 +430,42 @@ public function testDocumentNotFoundExceptionWithMongoBinDataId()
$test = $this->dm->find(get_class($test), $test->id);
$test->referenceOne->__load();
}

public function testDocumentNotFoundEvent()
{
$profile = new Profile();
$user = new User();
$user->setProfile($profile);

$this->dm->persist($profile);
$this->dm->persist($user);
$this->dm->flush();
$this->dm->clear();

$collection = $this->dm->getDocumentCollection(get_class($user));

$invalidId = new \MongoId('abcdefabcdefabcdefabcdef');

$collection->update(
array('_id' => new \MongoId($user->getId())),
array('$set' => array(
'profile.$id' => $invalidId,
))
);

$user = $this->dm->find(get_class($user), $user->getId());
$profile = $user->getProfile();

$closure = function (DocumentNotFoundEventArgs $eventArgs) use ($profile) {
$this->assertFalse($eventArgs->isExceptionDisabled());
$this->assertSame($profile, $eventArgs->getObject());
$eventArgs->disableException();
};

$this->dm->getEventManager()->addEventListener(Events::documentNotFound, new DocumentNotFoundListener($closure));

$profile->__load();
}
}

/** @ODM\Document */
Expand Down Expand Up @@ -464,3 +502,19 @@ class DocumentWithMongoBinDataId
/** @ODM\Id(strategy="none", options={"type"="bin"}) */
public $id;
}

class DocumentNotFoundListener
{
private $closure;

public function __construct(\Closure $closure)
{
$this->closure = $closure;
}

public function documentNotFound(DocumentNotFoundEventArgs $eventArgs)
{
$closure = $this->closure;
$closure($eventArgs);
}
}

0 comments on commit 453ef86

Please sign in to comment.