Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:

strategy:
matrix:
php-versions: ['7.3', '7.4', '8.0']
php-versions: ['8.3', '8.4']
name: PHP ${{ matrix.php-versions }}
steps:
- uses: actions/checkout@v2
Expand Down
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
"license": "MIT",
"minimum-stability": "stable",
"require": {
"php": "^7.3|^8.0",
"php": "^8.3",
"doctrine/orm": "^2.4.0",
"hostnet/entity-tracker-component": "^1.2.0||^2.0.0",
"psr/log": "^1.0.0|^2.0.0|^3.0.0"
"hostnet/entity-tracker-component": "^2.3.0",
"psr/log": "^1.0.0|^2.0.0|^3.0.0",
"symfony/cache": "^6.4|^7.4"
},
"require-dev": {
"hostnet/phpcs-tool": "^9.1.0",
Expand Down
14 changes: 14 additions & 0 deletions src/Attributes/Revision.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
/**
* @copyright 2026-present Hostnet B.V.
*/
declare(strict_types=1);

namespace Hostnet\Component\EntityRevision\Attributes;

use Hostnet\Component\EntityTracker\Attributes\Tracked;

#[\Attribute(\Attribute::TARGET_CLASS)]
class Revision extends Tracked
{
}
93 changes: 50 additions & 43 deletions src/Listener/RevisionListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,45 +11,24 @@
use Hostnet\Component\EntityRevision\Factory\RevisionFactoryInterface;
use Hostnet\Component\EntityRevision\Resolver\RevisionResolverInterface;
use Hostnet\Component\EntityRevision\RevisionableInterface;
use Hostnet\Component\EntityRevision\RevisionInterface;
use Hostnet\Component\EntityTracker\Event\EntityChangedEvent;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\Cache\Adapter\ArrayAdapter;

class RevisionListener
{
/**
* @var RevisionResolverInterface
*/
private $resolver;

/**
* @var RevisionFactoryInterface
*/
private $factory;

/**
* @var LoggerInterface
*/
private $logger;

/**
* @var Revision
*/
private $revision;
private ?RevisionInterface $revision = null;

/**
* @param RevisionResolverInterface $resolver
* @param RevisionFactoryInterface $factory
* @param LoggerInterface $logger
*/
public function __construct(
RevisionResolverInterface $resolver,
RevisionFactoryInterface $factory,
LoggerInterface $logger = null
private RevisionResolverInterface $resolver,
private RevisionFactoryInterface $factory,
private ?LoggerInterface $logger = new NullLogger(),
private ?CacheItemPoolInterface $is_revision_cache = new ArrayAdapter()
) {
$this->resolver = $resolver;
$this->factory = $factory;
$this->logger = $logger ?: new NullLogger();
}

/**
Expand All @@ -58,10 +37,11 @@ public function __construct(
* Used to group all entities to the same revision
* in the same flush if they use @Revision
*
* @deprecated functionality was moved to entityChanged and postFlush
* @deprecated functionality was moved to entityChanged and postFlush, will be removed when removing
* doctrine/annotations.
* @param PreFlushEventArgs $event
*/
public function preFlush(PreFlushEventArgs $event)
public function preFlush(PreFlushEventArgs $event): void
{
trigger_error(__METHOD__ . ' is deprecated, please remove it from your event listener.', E_USER_DEPRECATED);
$this->revision = $this->factory->createRevision(new \DateTime());
Expand All @@ -74,18 +54,16 @@ public function preFlush(PreFlushEventArgs $event)
* in the same flush if they use @Revision. This method
* can safely be overwritten if you prefer a Revision
* per Request.
*
* @param PreFlushEventArgs $event
*/
public function postFlush(PostFlushEventArgs $event)
public function postFlush(PostFlushEventArgs $event): void
{
$this->revision = null;
}

/**
* @param EntityChangedEvent $event
*/
public function entityChanged(EntityChangedEvent $event)
public function entityChanged(EntityChangedEvent $event): void
{
if (!$this->shouldBePersisted($event)) {
return;
Expand All @@ -112,15 +90,12 @@ public function entityChanged(EntityChangedEvent $event)
*
* @param EntityChangedEvent $event
*/
private function shouldBePersisted(EntityChangedEvent $event)
private function shouldBePersisted(EntityChangedEvent $event): bool
{
if (!($entity = $event->getCurrentEntity()) instanceof RevisionableInterface) {
return false;
}

$em = $event->getEntityManager();
$entity = $event->getCurrentEntity();
$em = $event->getEntityManager();

if (null === $this->resolver->getRevisionAnnotation($em, $entity)) {
if (!$this->isRevision($em, $entity)) {
return false;
}

Expand All @@ -133,4 +108,36 @@ private function shouldBePersisted(EntityChangedEvent $event)

return true;
}

private function isRevision($em, $entity): bool
{
$cache_key = base64_encode('REVISION-' . get_class($entity));
$cached_item = $this->is_revision_cache->getItem($cache_key);

if ($cached_item->isHit()) {
return $cached_item->get();
}

if (!($entity instanceof RevisionableInterface)) {
return $this->save($cached_item, false);
}

if (null !== $this->resolver->getRevisionAttribute($em, $entity)) {
return $this->save($cached_item, true);
}

if (null !== $this->resolver->getRevisionAnnotation($em, $entity)) {

@phonixor phonixor Jun 17, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think i would have moved this logic to the combined resolver so it can answer ones... but i guess it doesn't matter as we will remove the annotation variant soonish

return $this->save($cached_item, true);
}

return $this->save($cached_item, false);
}

private function save(CacheItemInterface $item, bool $value): bool
{
$item->set($value);
$this->is_revision_cache->save($item);

return $value;
}
}
33 changes: 14 additions & 19 deletions src/Resolver/RevisionResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,35 @@
namespace Hostnet\Component\EntityRevision\Resolver;

use Doctrine\ORM\EntityManagerInterface;
use Hostnet\Component\EntityTracker\Provider\EntityAnnotationMetadataProvider;
use Hostnet\Component\EntityRevision\Attributes\Revision;
use Hostnet\Component\EntityRevision\Revision as RevisionAnnotation;
use Hostnet\Component\EntityTracker\Provider\EntityMetadataProvider;

class RevisionResolver implements RevisionResolverInterface
{
/**
* @var string
*/
private $annotation = 'Hostnet\Component\EntityRevision\Revision';

/**
* @var EntityAnnotationMetadataProvider
*/
private $provider;

/**
* @param EntityMetadataProvider $provider
*/
public function __construct(EntityAnnotationMetadataProvider $provider)
public function __construct(private EntityMetadataProvider $provider)
{
$this->provider = $provider;
}

/**
* @see \Hostnet\Component\EntityRevision\Resolver\RevisionResolverInterface::getRevisionAnnotation()
*
* @deprecated Please use the Revision attribute instead
*/
public function getRevisionAnnotation(EntityManagerInterface $em, $entity)
public function getRevisionAnnotation(EntityManagerInterface $em, $entity): ?RevisionAnnotation
{
return $this->provider->getAnnotationFromEntity($em, $entity, RevisionAnnotation::class);
}

public function getRevisionAttribute(EntityManagerInterface $em, $entity): ?Revision
{
return $this->provider->getAnnotationFromEntity($em, $entity, $this->annotation);
return $this->provider->getAttributeFromEntity(Revision::class, $em, $entity);
}

/**
* @see \Hostnet\Component\EntityRevision\Resolver\RevisionResolverInterface::getRevisionableFields()
*/
public function getRevisionableFields(EntityManagerInterface $em, $entity)
public function getRevisionableFields(EntityManagerInterface $em, $entity): array
{
$metadata = $em->getClassMetadata(get_class($entity));
return array_merge($metadata->getFieldNames(), $metadata->getAssociationNames());
Expand Down
15 changes: 11 additions & 4 deletions src/Resolver/RevisionResolverInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
namespace Hostnet\Component\EntityRevision\Resolver;

use Doctrine\ORM\EntityManagerInterface;
use Hostnet\Component\EntityRevision\Attributes\Revision;
use Hostnet\Component\EntityRevision\Revision as RevisionAnnotation;

interface RevisionResolverInterface
{
Expand All @@ -15,12 +17,17 @@ interface RevisionResolverInterface
*
* @return string[]
*/
public function getRevisionableFields(EntityManagerInterface $em, $entity);
public function getRevisionableFields(EntityManagerInterface $em, $entity): array;

/**
* Return the revision annotation
* Return the revision annotation or null
*
* @return Mutation
* @deprecated
*/
public function getRevisionAnnotation(EntityManagerInterface $em, $entity);
public function getRevisionAnnotation(EntityManagerInterface $em, $entity): ?RevisionAnnotation;

/**
* Return the revision attribute or null
*/
public function getRevisionAttribute(EntityManagerInterface $em, $entity): ?Revision;
}
2 changes: 2 additions & 0 deletions src/Revision.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
/**
* @Annotation
* @Target({"CLASS"})
*
* @deprecated Please use the attribute instead
*/
class Revision extends Tracked
{
Expand Down
3 changes: 3 additions & 0 deletions src/RevisionInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

namespace Hostnet\Component\EntityRevision;

/**
* @TODO: add returntypehints on next BC break, when removing doctrine/annotation support
*/
interface RevisionInterface
{
/**
Expand Down
3 changes: 3 additions & 0 deletions src/RevisionableInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

namespace Hostnet\Component\EntityRevision;

/**
* @TODO: add returntypehints on next BC break, when removing doctrine/annotation support
*/
interface RevisionableInterface
{
/**
Expand Down
19 changes: 19 additions & 0 deletions test/Attributes/RevisionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
/**
* @copyright 2026-present Hostnet B.V.
*/
declare(strict_types=1);

namespace Hostnet\Component\EntityRevision\Attributes;

use Hostnet\Component\EntityTracker\Attributes\Tracked;
use PHPUnit\Framework\TestCase;

class RevisionTest extends TestCase
{
public function testObject(): void
{
$revision = new Revision();
self::assertInstanceOf(Tracked::class, $revision);
}
}
Loading
Loading