Skip to content

Commit

Permalink
add comparison event strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
ixarlie committed Apr 4, 2018
1 parent 87ee409 commit ed23448
Show file tree
Hide file tree
Showing 5 changed files with 330 additions and 3 deletions.
114 changes: 114 additions & 0 deletions lib/Doctrine/ORM/Event/OnComparisonEventArgs.php
@@ -0,0 +1,114 @@
<?php
/*
* 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\ORM\Event;

use Doctrine\ORM\EntityManagerInterface;

/**
* Class OnComparisonEventArgs.
*
* @author Carlos Dominguez <ixarlie@gmail.com>
*/
class OnComparisonEventArgs extends LifecycleEventArgs
{
/**
* @var string
*/
private $propertyName;

/**
* @var mixed
*/
private $originalValue;

/**
* @var mixed
*/
private $actualValue;

/**
* Returns a negative integer, zero, or a positive integer as the actualValue is less than, equal to,
* or greater than the specified originalValue.
* @var int
*/
private $comparisonResult;

/**
* Constructor.
*
* @param EntityManagerInterface $em
* @param object $entity
* @param string $propertyName
* @param mixed $originalValue
* @param mixed $actualValue
*/
public function __construct(
EntityManagerInterface $em,
$entity,
string $propertyName,
$originalValue,
$actualValue
) {
parent::__construct($entity, $em);
$this->propertyName = $propertyName;
$this->originalValue = $originalValue;
$this->actualValue = $actualValue;
}

/**
* @return string
*/
public function getPropertyName()
{
return $this->propertyName;
}

/**
* @return mixed
*/
public function getOriginalValue()
{
return $this->originalValue;
}

/**
* @return mixed
*/
public function getActualValue()
{
return $this->actualValue;
}

/**
* @return int|null
*/
public function getComparisonResult()
{
return $this->comparisonResult;
}

/**
* @param int $comparisonResult
*/
public function setComparisonResult(int $comparisonResult)
{
$this->comparisonResult = $comparisonResult;
}
}
6 changes: 6 additions & 0 deletions lib/Doctrine/ORM/Events.php
Expand Up @@ -164,4 +164,10 @@ private function __construct()
* @var string
*/
const onClear = 'onClear';

/**
* The onComparison event occurs when the UnitOfWork#computeChangeSet or UnitOfWork#recomputeSingleEntityChangeSet
* operations are invoked.
*/
const onComparison = 'onComparison';
}
23 changes: 20 additions & 3 deletions lib/Doctrine/ORM/UnitOfWork.php
Expand Up @@ -692,8 +692,15 @@ public function computeChangeSet(ClassMetadata $class, $entity)

$orgValue = $originalData[$propName];

$event = new Event\OnComparisonEventArgs($this->em, $entity, $propName, $orgValue, $actualValue);
if ($this->evm->hasListeners(Events::onComparison)) {
$this->evm->dispatchEvent(Events::onComparison, $event);
}

// skip if value haven't changed
if ($orgValue === $actualValue) {
if (0 === $event->getComparisonResult() ||
(null === $event->getComparisonResult() && $orgValue === $actualValue)
) {
continue;
}

Expand Down Expand Up @@ -1021,9 +1028,19 @@ public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity)
foreach ($actualData as $propName => $actualValue) {
$orgValue = $originalData[$propName] ?? null;

if ($orgValue !== $actualValue) {
$changeSet[$propName] = [$orgValue, $actualValue];
$event = new Event\OnComparisonEventArgs($this->em, $entity, $propName, $orgValue, $actualValue);
if ($this->evm->hasListeners(Events::onComparison)) {
$this->evm->dispatchEvent(Events::onComparison, $event);
}

// skip if value haven't changed
if (0 === $event->getComparisonResult() ||
(null === $event->getComparisonResult() && $orgValue === $actualValue)
) {
continue;
}

$changeSet[$propName] = [$orgValue, $actualValue];
}

if ($changeSet) {
Expand Down
44 changes: 44 additions & 0 deletions tests/Doctrine/Tests/ORM/Event/OnComparisonEventArgsTest.php
@@ -0,0 +1,44 @@
<?php

namespace Doctrine\Tests\ORM\Event;

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\OnComparisonEventArgs;
use Doctrine\Tests\Models\Quote\City;
use PHPUnit\Framework\TestCase;

/**
* Tests for {@see \Doctrine\ORM\Event\OnComparisonEventArgsTest}
*
* @covers \Doctrine\ORM\Event\OnComparisonEventArgsTest
*/
class OnComparisonEventArgsTest extends TestCase
{
public function testOnComparisonEventArgs()
{
/* @var $objectManager \Doctrine\Common\Persistence\ObjectManager */
$objectManager = $this->createMock(EntityManagerInterface::class);
$entity = new City('foo');

$args = new OnComparisonEventArgs($objectManager, $entity, 'name', $entity->name, 'bar');
$this->assertInstanceOf(LifecycleEventArgs::class, $args);

$this->assertSame('foo', $args->getOriginalValue());
$this->assertSame('bar', $args->getActualValue());
$this->assertSame('name', $args->getPropertyName());
$this->assertSame($objectManager, $args->getObjectManager());
$this->assertSame($entity, $args->getObject());

$this->assertNull($args->getComparisonResult());

$args->setComparisonResult(-1);
$this->assertSame(-1, $args->getComparisonResult());

$args->setComparisonResult(0);
$this->assertSame(0, $args->getComparisonResult());

$args->setComparisonResult(1);
$this->assertSame(1, $args->getComparisonResult());
}
}
146 changes: 146 additions & 0 deletions tests/Doctrine/Tests/ORM/Functional/ComparisonEventTest.php
@@ -0,0 +1,146 @@
<?php

namespace Doctrine\Tests\ORM\Functional;

use Doctrine\ORM\Event\OnComparisonEventArgs;
use Doctrine\ORM\Events;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\OrmFunctionalTestCase;

/**
* ComparisonEventTest
*
* @author Carlos Dominguez <ixarlie@gmail.com>
*/
class ComparisonEventTest extends OrmFunctionalTestCase
{
/**
* @var ComparisonListener
*/
private $listener;

protected function setUp()
{
$this->useModelSet('cms');
parent::setUp();
$this->listener = new ComparisonListener();
$evm = $this->_em->getEventManager();
$evm->addEventListener(Events::onComparison, $this->listener);
}

public function testListenerShouldBeNotified()
{
$this->_em->persist($this->createNewValidUser());
$this->_em->flush();
$this->assertFalse($this->listener->wasNotified);
$this->assertCount(0, $this->listener->receivedArgs);
}

public function testListenerShouldBeNotifierComputeChangeSet()
{
$user = $this->createNewValidUser();
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();

$user = $this->_em->find(CmsUser::class, $user->getId());
$originalValue = $user->username;
$user->username = 'foo';

$this->_em->getUnitOfWork()->computeChangeSet(
$this->_em->getClassMetadata(CmsUser::class),
$user
);

$this->assertCount(1, $this->listener->receivedArgs);
/** @var OnComparisonEventArgs $args */
$args = $this->listener->receivedArgs[0];
$this->assertSame($this->_em, $args->getObjectManager());
$this->assertSame($user, $args->getObject());
$this->assertSame('username', $args->getPropertyName());
$this->assertSame($originalValue, $args->getOriginalValue());
$this->assertSame('foo', $args->getActualValue());
$this->assertNotSame(0, $args->getComparisonResult());

$this->assertTrue($this->listener->wasNotified);

$changeSet = $this->_em->getUnitOfWork()->getEntityChangeSet($user);
$this->assertCount(1, $changeSet);
$this->assertArrayHasKey('username', $changeSet);
$this->assertSame($originalValue, $changeSet['username'][0]);
$this->assertSame($user->username, $changeSet['username'][1]);
}

public function testListenerShouldBeNotifierRecomputeChangeSet()
{
$user = $this->createNewValidUser();
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();

$user = $this->_em->find(CmsUser::class, $user->getId());
$originalValue = $user->username;
$user->username = 'foo';

$this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet(
$this->_em->getClassMetadata(CmsUser::class),
$user
);

$this->assertCount(1, $this->listener->receivedArgs);
/** @var OnComparisonEventArgs $args */
$args = $this->listener->receivedArgs[0];
$this->assertSame($this->_em, $args->getObjectManager());
$this->assertSame($user, $args->getObject());
$this->assertSame('username', $args->getPropertyName());
$this->assertSame($originalValue, $args->getOriginalValue());
$this->assertSame('foo', $args->getActualValue());
$this->assertNotSame(0, $args->getComparisonResult());

$this->assertTrue($this->listener->wasNotified);

$changeSet = $this->_em->getUnitOfWork()->getEntityChangeSet($user);
$this->assertCount(1, $changeSet);
$this->assertArrayHasKey('username', $changeSet);
$this->assertSame($originalValue, $changeSet['username'][0]);
$this->assertSame($user->username, $changeSet['username'][1]);
}

/**
* @return CmsUser
*/
private function createNewValidUser()
{
$user = new CmsUser();
$user->username = 'dfreudenberger';
$user->name = 'Daniel Freudenberger';
return $user;
}
}

class ComparisonListener
{
/**
* @var bool
*/
public $wasNotified = false;

/**
* @var OnComparisonEventArgs
*/
public $receivedArgs = [];

/**
* @param OnComparisonEventArgs $args
*/
public function onComparison(OnComparisonEventArgs $args)
{
$this->wasNotified = true;
if ($args->getOriginalValue() === $args->getActualValue()) {
$args->setComparisonResult(0);
} else {
$args->setComparisonResult(1);
$this->receivedArgs[] = $args;
}
}
}

0 comments on commit ed23448

Please sign in to comment.