Skip to content

Commit f1eafc6

Browse files
authored
Doctrine ORM 3.x support (#207)
* Allow `doctrine/orm:^3.2` * Fix `Error: Call to undefined method Doctrine\ORM\EntityManager::create()` * Fix `Error: Class "Doctrine\ORM\Mapping\ClassMetadataInfo" not found` * Association mapping is an object in doctrine/orm 3.x
1 parent 6cd9580 commit f1eafc6

File tree

9 files changed

+38
-22
lines changed

9 files changed

+38
-22
lines changed

composer.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"require": {
1414
"php": ">=8.2",
1515
"doctrine/dbal": "^2.13.1|^3.2",
16-
"doctrine/orm": "^2.13",
16+
"doctrine/orm": "^2.13|^3.2",
1717
"symfony/cache": "^5.4|^6.0|^7.0",
1818
"symfony/event-dispatcher": "^5.4|^6.0|^7.0",
1919
"symfony/lock": "^5.4|^6.0|^7.0",
@@ -40,7 +40,7 @@
4040
},
4141
"scripts": {
4242
"test": "XDEBUG_MODE=coverage ./vendor/bin/phpunit --colors=always",
43-
"cs-fix": "tools/php-cs-fixer/vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --using-cache=no --verbose --ansi",
43+
"cs-fix": "tools/php-cs-fixer/vendor/bin/php-cs-fixer fix --using-cache=no --verbose --ansi",
4444
"cs-check": "tools/php-cs-fixer/vendor/bin/php-cs-fixer fix --using-cache=no --verbose --ansi --dry-run",
4545
"phpstan": "tools/phpstan/vendor/bin/phpstan --memory-limit=1G --ansi analyse src",
4646
"rector": "tools/rector/vendor/bin/rector",

rector.php

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
SymfonySetList::SYMFONY_54,
2828
PHPUnitSetList::PHPUNIT_110,
2929
DoctrineSetList::DOCTRINE_CODE_QUALITY,
30+
DoctrineSetList::DOCTRINE_ORM_29,
3031
DoctrineSetList::DOCTRINE_DBAL_40,
3132
])
3233
->withAttributesSets(

src/Provider/Doctrine/Auditing/Event/DoctrineSubscriber.php

+8-5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Doctrine\Common\EventSubscriber;
1111
use Doctrine\DBAL\Driver;
1212
use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;
13+
use Doctrine\ORM\EntityManagerInterface;
1314
use Doctrine\ORM\Event\OnFlushEventArgs;
1415
use Doctrine\ORM\Events;
1516

@@ -18,7 +19,10 @@ final class DoctrineSubscriber implements EventSubscriber
1819
/** @var Transaction[] */
1920
private array $transactions = [];
2021

21-
public function __construct(private readonly TransactionManagerInterface $transactionManager) {}
22+
public function __construct(
23+
private readonly TransactionManagerInterface $transactionManager,
24+
private readonly EntityManagerInterface $entityManager
25+
) {}
2226

2327
/**
2428
* It is called inside EntityManager#flush() after the changes to all the managed entities
@@ -28,16 +32,15 @@ public function __construct(private readonly TransactionManagerInterface $transa
2832
*/
2933
public function onFlush(OnFlushEventArgs $args): void
3034
{
31-
$entityManager = $args->getObjectManager();
32-
$entityManagerId = spl_object_id($entityManager);
35+
$entityManagerId = spl_object_id($this->entityManager);
3336

3437
// cached transaction model, if it holds same EM no need to create a new one
35-
$transaction = ($this->transactions[$entityManagerId] ??= new Transaction($entityManager));
38+
$transaction = ($this->transactions[$entityManagerId] ??= new Transaction($this->entityManager));
3639

3740
// Populate transaction
3841
$this->transactionManager->populate($transaction);
3942

40-
$driver = $entityManager->getConnection()->getDriver();
43+
$driver = $this->entityManager->getConnection()->getDriver();
4144

4245
if (!$driver instanceof DHDriver) {
4346
$driver = $this->getWrappedDriver($driver);

src/Provider/Doctrine/Auditing/Transaction/AuditTrait.php

+10-1
Original file line numberDiff line numberDiff line change
@@ -33,27 +33,35 @@ private function id(EntityManagerInterface $entityManager, object $entity): mixe
3333
}
3434

3535
if (isset($meta->fieldMappings[$pk])) {
36+
\assert(\is_string($meta->fieldMappings[$pk]['type']));
3637
$type = Type::getType($meta->fieldMappings[$pk]['type']);
3738

39+
\assert(\is_object($meta->getReflectionProperty($pk)));
40+
3841
return $this->value($entityManager, $type, $meta->getReflectionProperty($pk)->getValue($entity));
3942
}
4043

41-
/**
44+
/*
4245
* Primary key is not part of fieldMapping.
4346
*
4447
* @see https://github.com/DamienHarper/auditor-bundle/issues/40
4548
* @see https://www.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/composite-primary-keys.html#identity-through-foreign-entities
4649
* We try to get it from associationMapping (will throw a MappingException if not available)
4750
*/
51+
\assert(\is_object($meta->getReflectionProperty($pk)));
4852
$targetEntity = $meta->getReflectionProperty($pk)->getValue($entity);
4953

5054
$mapping = $meta->getAssociationMapping($pk);
5155

56+
\assert(\is_string($mapping['targetEntity']));
5257
$meta = $entityManager->getClassMetadata($mapping['targetEntity']);
5358
$pk = $meta->getSingleIdentifierFieldName();
59+
60+
\assert(\is_string($meta->fieldMappings[$pk]['type']));
5461
$type = Type::getType($meta->fieldMappings[$pk]['type']);
5562

5663
\assert(\is_object($targetEntity));
64+
\assert(\is_object($meta->getReflectionProperty($pk)));
5765

5866
return $this->value($entityManager, $type, $meta->getReflectionProperty($pk)->getValue($targetEntity));
5967
}
@@ -151,6 +159,7 @@ private function diff(EntityManagerInterface $entityManager, object $entity, arr
151159
&& $this->provider->isAuditedField($entity, $fieldName)
152160
) {
153161
$mapping = $meta->fieldMappings[$fieldName];
162+
\assert(\is_string($mapping['type']));
154163
$type = Type::getType($mapping['type']);
155164
$o = $this->value($entityManager, $type, $old);
156165
$n = $this->value($entityManager, $type, $new);

src/Provider/Doctrine/Auditing/Transaction/TransactionHydrator.php

+7-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use DH\Auditor\Provider\Doctrine\Model\Transaction;
1010
use DH\Auditor\Transaction\TransactionHydratorInterface;
1111
use Doctrine\ORM\EntityManagerInterface;
12+
use Doctrine\ORM\Mapping\AssociationMapping;
1213
use Doctrine\ORM\PersistentCollection;
1314

1415
final class TransactionHydrator implements TransactionHydratorInterface
@@ -86,8 +87,9 @@ private function hydrateWithScheduledCollectionUpdates(Transaction $transaction,
8687
if (null !== $owner && $this->provider->isAudited($owner)) {
8788
$mapping = $collection->getMapping();
8889

89-
if (!\is_array($mapping)) {
90-
continue;
90+
// TODO: backward compatibility code until we drop doctrine/orm <3.0
91+
if ($mapping instanceof AssociationMapping) {
92+
$mapping = $mapping->toArray();
9193
}
9294

9395
/** @var object $entity */
@@ -126,8 +128,9 @@ private function hydrateWithScheduledCollectionDeletions(Transaction $transactio
126128
if (null !== $owner && $this->provider->isAudited($owner)) {
127129
$mapping = $collection->getMapping();
128130

129-
if (!\is_array($mapping)) {
130-
continue;
131+
// TODO: backward compatibility code until we drop doctrine/orm <3.0
132+
if ($mapping instanceof AssociationMapping) {
133+
$mapping = $mapping->toArray();
131134
}
132135

133136
/** @var object $entity */

src/Provider/Doctrine/DoctrineProvider.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public function registerAuditingService(AuditingServiceInterface $service): Prov
7070
// Register subscribers
7171
$evm->addEventListener([Events::loadClassMetadata], new TableSchemaListener($this));
7272
$evm->addEventListener([ToolEvents::postGenerateSchemaTable], new CreateSchemaListener($this));
73-
$evm->addEventSubscriber(new DoctrineSubscriber($this->transactionManager));
73+
$evm->addEventSubscriber(new DoctrineSubscriber($this->transactionManager, $entityManager));
7474

7575
return $this;
7676
}

src/Provider/Doctrine/Persistence/Event/CreateSchemaListener.php

+5-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
use DH\Auditor\Provider\Doctrine\Service\AuditingService;
1010
use DH\Auditor\Provider\Doctrine\Service\StorageService;
1111
use DH\Auditor\Tests\Provider\Doctrine\Persistence\Event\CreateSchemaListenerTest;
12-
use Doctrine\ORM\Mapping\ClassMetadataInfo;
12+
use Doctrine\ORM\Mapping\ClassMetadata;
1313
use Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs;
1414

1515
/**
@@ -25,9 +25,9 @@ public function postGenerateSchemaTable(GenerateSchemaTableEventArgs $eventArgs)
2525

2626
// check inheritance type and returns if unsupported
2727
if (!\in_array($metadata->inheritanceType, [
28-
ClassMetadataInfo::INHERITANCE_TYPE_NONE,
29-
ClassMetadataInfo::INHERITANCE_TYPE_JOINED,
30-
ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE,
28+
ClassMetadata::INHERITANCE_TYPE_NONE,
29+
ClassMetadata::INHERITANCE_TYPE_JOINED,
30+
ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE,
3131
], true)) {
3232
throw new \Exception(\sprintf('Inheritance type "%s" is not yet supported', $metadata->inheritanceType));
3333
}
@@ -38,7 +38,7 @@ public function postGenerateSchemaTable(GenerateSchemaTableEventArgs $eventArgs)
3838
$audited = false;
3939
if (
4040
$metadata->rootEntityName === $metadata->name
41-
&& ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE === $metadata->inheritanceType
41+
&& ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE === $metadata->inheritanceType
4242
) {
4343
foreach ($metadata->subClasses as $subClass) {
4444
if ($this->provider->isAuditable($subClass)) {

tests/Provider/Doctrine/Event/DoctrineSubscriberTest.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public function process($transaction): void
6262
->willReturn($driver)
6363
;
6464

65-
$target = new DoctrineSubscriber($transactionManager);
65+
$target = new DoctrineSubscriber($transactionManager, $objectManager);
6666
$target->onFlush($args);
6767

6868
foreach ($dhDriver->getFlusherList() as $item) {
@@ -117,7 +117,7 @@ public function createDatabasePlatformForVersion($version): void {}
117117
->willReturn($driver)
118118
;
119119

120-
$target = new DoctrineSubscriber($transactionManager);
120+
$target = new DoctrineSubscriber($transactionManager, $objectManager);
121121
$target->onFlush($args);
122122

123123
foreach ($dhDriver->getFlusherList() as $item) {
@@ -171,7 +171,7 @@ public function createDatabasePlatformForVersion($version): void {}
171171
->willReturn($configuration = $this->createMock(Configuration::class))
172172
;
173173

174-
$target = new DoctrineSubscriber($transactionManager);
174+
$target = new DoctrineSubscriber($transactionManager, $objectManager);
175175
$target->onFlush($args);
176176

177177
$this->assertTrue(true);

tests/Provider/Doctrine/Traits/EntityManagerInterfaceTrait.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ private function createEntityManager(?array $paths = null, string $connectionNam
2525

2626
$connection = $this->getConnection($connectionName, $params);
2727

28-
$em = EntityManager::create($connection, $configuration);
28+
$em = new EntityManager($connection, $configuration);
2929
$evm = $em->getEventManager();
3030
$allListeners = method_exists($evm, 'getAllListeners') ? $evm->getAllListeners() : $evm->getListeners();
3131
foreach ($allListeners as $event => $listeners) {

0 commit comments

Comments
 (0)