Skip to content

Commit

Permalink
Feat: Is about to* (#1)
Browse files Browse the repository at this point in the history
* feat: add isAboutToBe* methods

* feat: introduce tap method
  • Loading branch information
bpolaszek committed Dec 9, 2023
1 parent 129546b commit c4366d5
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 31 deletions.
47 changes: 47 additions & 0 deletions src/Tracker/EntityAwareTracker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace BenTools\DoctrineChangeSet\Tracker;

use Doctrine\ORM\UnitOfWork;

/**
* @internal
*/
final readonly class EntityAwareTracker
{
public function __construct(
private EntityTracker $tracker,
private object $entity,
) {
}

public function isAboutToBeInserted(): bool
{
return $this->tracker->isAboutToBeInserted($this->entity);
}

public function isAboutToBeUpdated(): bool
{
return $this->tracker->isAboutToBeUpdated($this->entity);
}

public function isAboutToBeRemoved(): bool
{
return $this->tracker->isAboutToBeRemoved($this->entity);
}

public function hasChanged(
string $property = null,
mixed $oldValue = new AnyValue(),
mixed $newValue = new AnyValue(),
): bool {
return $this->tracker->hasChanged($this->entity, $property, $oldValue, $newValue);
}

public function getChangeSet(string $property, UnitOfWork $uow = null): PropertyChangeSet
{
return $this->tracker->getChangeSet($this->entity, $property, $uow);
}
}
22 changes: 22 additions & 0 deletions src/Tracker/EntityTracker.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,28 @@ public function __construct(
) {
}

public function tap(object $entity): EntityAwareTracker
{
return new EntityAwareTracker($this, $entity);
}

public function isAboutToBeInserted(object $entity): bool
{
return $this->getUnitOfWork($entity)->isScheduledForInsert($entity);
}

public function isAboutToBeUpdated(object $entity): bool
{
$uow = $this->getUnitOfWork($entity);

return $uow->isScheduledForUpdate($entity) || $uow->isScheduledForDirtyCheck($entity);
}

public function isAboutToBeRemoved(object $entity): bool
{
return $this->getUnitOfWork($entity)->isScheduledForDelete($entity);
}

public function hasChanged(
object $entity,
string $property = null,
Expand Down
94 changes: 63 additions & 31 deletions tests/EntityTrackerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use SampleApp\Entity\Metadata;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

use function assert;
use function BenTools\DoctrineChangeSet\Tracker\oneOf;
use function BenTools\DoctrineChangeSet\Tracker\whatever;

Expand All @@ -34,23 +35,29 @@
$em->persist($book);

// Then
expect($entityChangeSet->hasChanged($book))->toBeTrue()
->and($entityChangeSet->hasChanged($book, 'title'))->toBeTrue()
->and($entityChangeSet->hasChanged($book, 'isbn'))->toBeTrue()
->and($entityChangeSet->hasChanged($book, 'createdAt'))->toBeTrue()
->and($entityChangeSet->hasChanged($book, 'updatedAt'))->toBeTrue()
->and($entityChangeSet->hasChanged($book, 'trackedData'))->toBeTrue();
expect($entityChangeSet->tap($book)->hasChanged())->toBeTrue()
->and($entityChangeSet->tap($book)->isAboutToBeInserted())->toBeTrue()
->and($entityChangeSet->tap($book)->isAboutToBeUpdated())->toBeFalse()
->and($entityChangeSet->tap($book)->isAboutToBeRemoved())->toBeFalse()
->and($entityChangeSet->tap($book)->hasChanged('title'))->toBeTrue()
->and($entityChangeSet->tap($book)->hasChanged('isbn'))->toBeTrue()
->and($entityChangeSet->tap($book)->hasChanged('createdAt'))->toBeTrue()
->and($entityChangeSet->tap($book)->hasChanged('updatedAt'))->toBeTrue()
->and($entityChangeSet->tap($book)->hasChanged('trackedData'))->toBeTrue();

// When
$em->flush();

// Then
expect($entityChangeSet->hasChanged($book))->toBeFalse()
->and($entityChangeSet->hasChanged($book, 'title'))->toBeFalse()
->and($entityChangeSet->hasChanged($book, 'isbn'))->toBeFalse()
->and($entityChangeSet->hasChanged($book, 'createdAt'))->toBeFalse()
->and($entityChangeSet->hasChanged($book, 'updatedAt'))->toBeFalse()
->and($entityChangeSet->hasChanged($book, 'trackedData'))->toBeFalse();
expect($entityChangeSet->tap($book)->hasChanged())->toBeFalse()
->and($entityChangeSet->tap($book)->isAboutToBeInserted())->toBeFalse()
->and($entityChangeSet->tap($book)->isAboutToBeUpdated())->toBeFalse()
->and($entityChangeSet->tap($book)->isAboutToBeRemoved())->toBeFalse()
->and($entityChangeSet->tap($book)->hasChanged('title'))->toBeFalse()
->and($entityChangeSet->tap($book)->hasChanged('isbn'))->toBeFalse()
->and($entityChangeSet->tap($book)->hasChanged('createdAt'))->toBeFalse()
->and($entityChangeSet->tap($book)->hasChanged('updatedAt'))->toBeFalse()
->and($entityChangeSet->tap($book)->hasChanged('trackedData'))->toBeFalse();
});
test('an unchanged entity is not reported as changed', function () {
$container = $this->getContainer(); // @phpstan-ignore-line
Expand All @@ -65,11 +72,14 @@
assert($book instanceof Book);

// Then
expect($entityChangeSet->hasChanged($book))->toBeFalse()
->and($entityChangeSet->hasChanged($book, 'title'))->toBeFalse()
->and($entityChangeSet->hasChanged($book, 'isbn'))->toBeFalse()
->and($entityChangeSet->hasChanged($book, 'createdAt'))->toBeFalse()
->and($entityChangeSet->hasChanged($book, 'updatedAt'))->toBeFalse();
expect($entityChangeSet->tap($book)->hasChanged())->toBeFalse()
->and($entityChangeSet->tap($book)->isAboutToBeInserted())->toBeFalse()
->and($entityChangeSet->tap($book)->isAboutToBeUpdated())->toBeFalse()
->and($entityChangeSet->tap($book)->isAboutToBeRemoved())->toBeFalse()
->and($entityChangeSet->tap($book)->hasChanged('title'))->toBeFalse()
->and($entityChangeSet->tap($book)->hasChanged('isbn'))->toBeFalse()
->and($entityChangeSet->tap($book)->hasChanged('createdAt'))->toBeFalse()
->and($entityChangeSet->tap($book)->hasChanged('updatedAt'))->toBeFalse();
});
test('a changed entity is reported as changed', function () {
$container = $this->getContainer(); // @phpstan-ignore-line
Expand All @@ -86,39 +96,42 @@
$book->updatedAt = new DateTimeImmutable();

// Then
expect($entityChangeSet->hasChanged($book))->toBeTrue()
->and($entityChangeSet->hasChanged($book, 'title'))->toBeFalse()
->and($entityChangeSet->hasChanged($book, 'isbn'))->toBeTrue()
->and($entityChangeSet->hasChanged($book, 'createdAt'))->toBeFalse()
->and($entityChangeSet->hasChanged($book, 'updatedAt'))->toBeTrue()
expect($entityChangeSet->tap($book)->hasChanged())->toBeTrue()
->and($entityChangeSet->tap($book)->isAboutToBeInserted())->toBeFalse()
->and($entityChangeSet->tap($book)->isAboutToBeUpdated())->toBeTrue()
->and($entityChangeSet->tap($book)->isAboutToBeRemoved())->toBeFalse()
->and($entityChangeSet->tap($book)->hasChanged('title'))->toBeFalse()
->and($entityChangeSet->tap($book)->hasChanged('isbn'))->toBeTrue()
->and($entityChangeSet->tap($book)->hasChanged('createdAt'))->toBeFalse()
->and($entityChangeSet->tap($book)->hasChanged('updatedAt'))->toBeTrue()
->and(
$entityChangeSet->getChangeSet($book, 'isbn')
$entityChangeSet->tap($book)->getChangeSet('isbn')
->hasChangedFor('0000000000001', '0000000000002')
)
->toBeTrue()
->and(
$entityChangeSet->getChangeSet($book, 'isbn')->hasChangedFor(
$entityChangeSet->tap($book)->getChangeSet('isbn')->hasChangedFor(
'0000000000001',
oneOf('0000000000002', '0000000000003')
)
)
->toBeTrue()
->and(
$entityChangeSet->getChangeSet($book, 'isbn')->hasChangedFor(
$entityChangeSet->tap($book)->getChangeSet('isbn')->hasChangedFor(
whatever()->except('0000000000002'),
oneOf('0000000000002', '0000000000003')
)
)
->toBeTrue()
->and(
$entityChangeSet->getChangeSet($book, 'isbn')->hasChangedFor(
$entityChangeSet->tap($book)->getChangeSet('isbn')->hasChangedFor(
whatever()->except('0000000000001'),
whatever()
)
)
->toBeFalse()
->and(
$entityChangeSet->getChangeSet($book, 'isbn')->hasChangedFor(
$entityChangeSet->tap($book)->getChangeSet('isbn')->hasChangedFor(
whatever(),
oneOf('0000000000001', '0000000000003')
)
Expand All @@ -142,15 +155,18 @@
$book->title = 'PHP For Dummies';

// Then
expect($entityChangeSet->hasChanged($book))->toBeTrue()
->and($entityChangeSet->hasChanged($book, 'title'))->toBeTrue()
->and($entityChangeSet->hasChanged($book, 'trackedData'))->toBeFalse();
expect($entityChangeSet->tap($book)->hasChanged())->toBeTrue()
->and($entityChangeSet->tap($book)->isAboutToBeInserted())->toBeFalse()
->and($entityChangeSet->tap($book)->isAboutToBeUpdated())->toBeTrue()
->and($entityChangeSet->tap($book)->isAboutToBeRemoved())->toBeFalse()
->and($entityChangeSet->tap($book)->hasChanged('title'))->toBeTrue()
->and($entityChangeSet->tap($book)->hasChanged('trackedData'))->toBeFalse();

// When
$book->trackedData->foo = 'bar';

// Then
expect($entityChangeSet->hasChanged($book, 'trackedData'))->toBeTrue();
expect($entityChangeSet->tap($book)->hasChanged('trackedData'))->toBeTrue();

// When
$em->flush();
Expand All @@ -160,3 +176,19 @@
// Then
expect($book->trackedData->foo)->toEqual('bar');
});

it('reports an entity as scheduled for removal', function () {
$container = $this->getContainer(); // @phpstan-ignore-line
/** @var EntityTracker $entityChangeSet */
$entityChangeSet = $container->get(EntityTracker::class);

/** @var EntityManagerInterface $em */
$em = $container->get(EntityManagerInterface::class);

// Given
$book = $em->getRepository(Book::class)->findOneBy([]);
assert($book instanceof Book);

$em->remove($book);
expect($entityChangeSet->tap($book)->isAboutToBeRemoved())->toBeTrue();
});

0 comments on commit c4366d5

Please sign in to comment.