Skip to content

Commit d8e2d0c

Browse files
soyukaddeboer
andauthored
fix(doctrine): doctrine/orm:^3.0 support (#6193)
* feat: add compatibility with doctrine/orm 3.0 * attempt to fix lowest --------- Co-authored-by: David de Boer <[email protected]>
1 parent f891f16 commit d8e2d0c

23 files changed

+450
-302
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"doctrine/doctrine-bundle": "^1.12 || ^2.0",
4747
"doctrine/mongodb-odm": "^2.2",
4848
"doctrine/mongodb-odm-bundle": "^4.0 || ^5.0",
49-
"doctrine/orm": "^2.14",
49+
"doctrine/orm": "^2.14 || ^3.0",
5050
"elasticsearch/elasticsearch": "^7.11 || ^8.4",
5151
"friends-of-behat/mink-browserkit-driver": "^1.3.1",
5252
"friends-of-behat/mink-extension": "^2.2",

src/Doctrine/Common/Tests/State/PersistProcessorTest.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
use ApiPlatform\Metadata\Get;
1919
use ApiPlatform\State\ProcessorInterface;
2020
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
21-
use Doctrine\ORM\Mapping\ClassMetadataInfo;
21+
use Doctrine\ORM\Mapping\ClassMetadata as ORMClassMetadata;
2222
use Doctrine\Persistence\ManagerRegistry;
2323
use Doctrine\Persistence\ObjectManager;
2424
use PHPUnit\Framework\TestCase;
@@ -84,8 +84,8 @@ public function testPersistWithNullManager(): void
8484
public static function getTrackingPolicyParameters(): array
8585
{
8686
return [
87-
'deferred explicit ORM' => [ClassMetadataInfo::class, true, true],
88-
'deferred implicit ORM' => [ClassMetadataInfo::class, false, false],
87+
'deferred explicit ORM' => [ORMClassMetadata::class, true, true],
88+
'deferred implicit ORM' => [ORMClassMetadata::class, false, false],
8989
'deferred explicit ODM' => [ClassMetadata::class, true, true],
9090
'deferred implicit ODM' => [ClassMetadata::class, false, false],
9191
];
@@ -98,15 +98,15 @@ public function testTrackingPolicy(string $metadataClass, bool $deferredExplicit
9898
{
9999
$dummy = new Dummy();
100100

101-
$classMetadataInfo = $this->prophesize($metadataClass);
101+
$classMetadata = $this->prophesize($metadataClass);
102102
if (method_exists($metadataClass, 'isChangeTrackingDeferredExplicit')) {
103-
$classMetadataInfo->isChangeTrackingDeferredExplicit()->willReturn($deferredExplicit)->shouldBeCalled();
103+
$classMetadata->isChangeTrackingDeferredExplicit()->willReturn($deferredExplicit)->shouldBeCalled();
104104
} else {
105105
$persisted = false;
106106
}
107107

108108
$objectManagerProphecy = $this->prophesize(ObjectManager::class);
109-
$objectManagerProphecy->getClassMetadata(Dummy::class)->willReturn($classMetadataInfo)->shouldBeCalled();
109+
$objectManagerProphecy->getClassMetadata(Dummy::class)->willReturn($classMetadata)->shouldBeCalled();
110110
$objectManagerProphecy->contains($dummy)->willReturn(true);
111111
$objectManagerProphecy->persist($dummy)->should($persisted ? new CallPrediction() : new NoCallsPrediction());
112112
$objectManagerProphecy->flush()->shouldBeCalled();

src/Doctrine/EventListener/PublishMercureUpdatesListener.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ public function __construct(LegacyResourceClassResolverInterface|ResourceClassRe
9999
public function onFlush(EventArgs $eventArgs): void
100100
{
101101
if ($eventArgs instanceof OrmOnFlushEventArgs) {
102+
// @phpstan-ignore-next-line
102103
$uow = method_exists($eventArgs, 'getObjectManager') ? $eventArgs->getObjectManager()->getUnitOfWork() : $eventArgs->getEntityManager()->getUnitOfWork();
103104
} elseif ($eventArgs instanceof MongoDbOdmOnFlushEventArgs) {
104105
$uow = $eventArgs->getDocumentManager()->getUnitOfWork();

src/Doctrine/EventListener/PurgeHttpCacheListener.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
use Doctrine\ORM\EntityManagerInterface;
2929
use Doctrine\ORM\Event\OnFlushEventArgs;
3030
use Doctrine\ORM\Event\PreUpdateEventArgs;
31+
use Doctrine\ORM\Mapping\AssociationMapping;
3132
use Doctrine\ORM\PersistentCollection;
3233
use Symfony\Component\PropertyAccess\PropertyAccess;
3334
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
@@ -57,6 +58,7 @@ public function preUpdate(PreUpdateEventArgs $eventArgs): void
5758
$this->gatherResourceAndItemTags($object, true);
5859

5960
$changeSet = $eventArgs->getEntityChangeSet();
61+
// @phpstan-ignore-next-line
6062
$objectManager = method_exists($eventArgs, 'getObjectManager') ? $eventArgs->getObjectManager() : $eventArgs->getEntityManager();
6163
$associationMappings = $objectManager->getClassMetadata(ClassUtils::getClass($eventArgs->getObject()))->getAssociationMappings();
6264

@@ -75,6 +77,7 @@ public function preUpdate(PreUpdateEventArgs $eventArgs): void
7577
*/
7678
public function onFlush(OnFlushEventArgs $eventArgs): void
7779
{
80+
// @phpstan-ignore-next-line
7881
$em = method_exists($eventArgs, 'getObjectManager') ? $eventArgs->getObjectManager() : $eventArgs->getEntityManager();
7982
$uow = $em->getUnitOfWork();
8083

@@ -125,12 +128,19 @@ private function gatherResourceAndItemTags(object $entity, bool $purgeItem): voi
125128
private function gatherRelationTags(EntityManagerInterface $em, object $entity): void
126129
{
127130
$associationMappings = $em->getClassMetadata(ClassUtils::getClass($entity))->getAssociationMappings();
128-
foreach (array_keys($associationMappings) as $property) {
131+
/** @var array|AssociationMapping $associationMapping according to the version of doctrine orm */
132+
foreach ($associationMappings as $property => $associationMapping) {
133+
if ($associationMapping instanceof AssociationMapping && ($associationMapping->targetEntity ?? null) && !$this->resourceClassResolver->isResourceClass($associationMapping->targetEntity)) {
134+
return;
135+
}
136+
129137
if (
130-
\array_key_exists('targetEntity', $associationMappings[$property])
131-
&& !$this->resourceClassResolver->isResourceClass($associationMappings[$property]['targetEntity'])) {
138+
\is_array($associationMapping)
139+
&& \array_key_exists('targetEntity', $associationMapping)
140+
&& !$this->resourceClassResolver->isResourceClass($associationMapping['targetEntity'])) {
132141
return;
133142
}
143+
134144
if ($this->propertyAccessor->isReadable($entity, $property)) {
135145
$this->addTagsFor($this->propertyAccessor->getValue($entity, $property));
136146
}

src/Doctrine/Orm/AbstractPaginator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public function __construct(DoctrinePaginator $paginator)
3232
{
3333
$query = $paginator->getQuery();
3434

35-
if (null === ($firstResult = $query->getFirstResult()) || null === $maxResults = $query->getMaxResults()) { // @phpstan-ignore-line
35+
if (null === ($firstResult = $query->getFirstResult()) || $firstResult < 0 || null === $maxResults = $query->getMaxResults()) { // @phpstan-ignore-line
3636
throw new InvalidArgumentException(sprintf('"%1$s::setFirstResult()" or/and "%1$s::setMaxResults()" was/were not applied to the query.', Query::class));
3737
}
3838

src/Doctrine/Orm/Extension/EagerLoadingExtension.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
2424
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
2525
use Doctrine\ORM\Mapping\ClassMetadata;
26-
use Doctrine\ORM\Mapping\ClassMetadataInfo;
2726
use Doctrine\ORM\Query\Expr\Join;
2827
use Doctrine\ORM\Query\Expr\Select;
2928
use Doctrine\ORM\QueryBuilder;
@@ -137,9 +136,9 @@ private function joinRelations(QueryBuilder $queryBuilder, QueryNameGeneratorInt
137136

138137
if (
139138
// Always skip extra lazy associations
140-
ClassMetadataInfo::FETCH_EXTRA_LAZY === $mapping['fetch']
139+
ClassMetadata::FETCH_EXTRA_LAZY === $mapping['fetch']
141140
// We don't want to interfere with doctrine on this association
142-
|| (false === $forceEager && ClassMetadataInfo::FETCH_EAGER !== $mapping['fetch'])
141+
|| (false === $forceEager && ClassMetadata::FETCH_EAGER !== $mapping['fetch'])
143142
) {
144143
continue;
145144
}

src/Doctrine/Orm/Extension/FilterEagerLoadingExtension.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
use ApiPlatform\Metadata\Operation;
2020
use ApiPlatform\Metadata\ResourceClassResolverInterface;
2121
use Doctrine\ORM\EntityManagerInterface;
22-
use Doctrine\ORM\Mapping\ClassMetadataInfo;
22+
use Doctrine\ORM\Mapping\ClassMetadata;
2323
use Doctrine\ORM\Query\Expr\Join;
2424
use Doctrine\ORM\QueryBuilder;
2525

@@ -111,12 +111,12 @@ public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGenerator
111111
*
112112
* @param array $checked array cache of tested metadata classes
113113
*/
114-
private function hasFetchEagerAssociation(EntityManagerInterface $em, ClassMetadataInfo $classMetadata, array &$checked = []): bool
114+
private function hasFetchEagerAssociation(EntityManagerInterface $em, ClassMetadata $classMetadata, array &$checked = []): bool
115115
{
116116
$checked[] = $classMetadata->name;
117117

118118
foreach ($classMetadata->getAssociationMappings() as $mapping) {
119-
if (ClassMetadataInfo::FETCH_EAGER === $mapping['fetch']) {
119+
if (ClassMetadata::FETCH_EAGER === $mapping['fetch']) {
120120
return true;
121121
}
122122

src/Doctrine/Orm/Filter/ExistsFilter.php

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
use ApiPlatform\Doctrine\Orm\Util\QueryBuilderHelper;
1919
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
2020
use ApiPlatform\Metadata\Operation;
21-
use Doctrine\ORM\Mapping\ClassMetadataInfo;
21+
use Doctrine\ORM\Mapping\AssociationMapping;
22+
use Doctrine\ORM\Mapping\ClassMetadata;
23+
use Doctrine\ORM\Mapping\ManyToManyOwningSideMapping;
24+
use Doctrine\ORM\Mapping\ToOneOwningSideMapping;
2225
use Doctrine\ORM\Query\Expr\Join;
2326
use Doctrine\ORM\QueryBuilder;
2427
use Doctrine\Persistence\ManagerRegistry;
@@ -199,7 +202,7 @@ protected function isNullableField(string $property, string $resourceClass): boo
199202

200203
if ($metadata->hasAssociation($field)) {
201204
if ($metadata->isSingleValuedAssociation($field)) {
202-
if (!($metadata instanceof ClassMetadataInfo)) {
205+
if (!($metadata instanceof ClassMetadata)) {
203206
return false;
204207
}
205208

@@ -211,7 +214,7 @@ protected function isNullableField(string $property, string $resourceClass): boo
211214
return true;
212215
}
213216

214-
if ($metadata instanceof ClassMetadataInfo && $metadata->hasField($field)) {
217+
if ($metadata instanceof ClassMetadata && $metadata->hasField($field)) {
215218
return $metadata->isNullable($field);
216219
}
217220

@@ -223,8 +226,26 @@ protected function isNullableField(string $property, string $resourceClass): boo
223226
*
224227
* @see https://github.com/doctrine/doctrine2/blob/v2.5.4/lib/Doctrine/ORM/Tools/EntityGenerator.php#L1221-L1246
225228
*/
226-
private function isAssociationNullable(array $associationMapping): bool
229+
private function isAssociationNullable(AssociationMapping|array $associationMapping): bool
227230
{
231+
if ($associationMapping instanceof AssociationMapping) {
232+
if (!empty($associationMapping->id)) {
233+
return false;
234+
}
235+
236+
if ($associationMapping instanceof ToOneOwningSideMapping || $associationMapping instanceof ManyToManyOwningSideMapping) {
237+
foreach ($associationMapping->joinColumns as $joinColumn) {
238+
if (false === $joinColumn->nullable) {
239+
return false;
240+
}
241+
}
242+
243+
return true;
244+
}
245+
246+
return true;
247+
}
248+
228249
if (!empty($associationMapping['id'])) {
229250
return false;
230251
}

src/Doctrine/Orm/Metadata/Property/DoctrineOrmPropertyMetadataFactory.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515

1616
use ApiPlatform\Metadata\ApiProperty;
1717
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
18-
use Doctrine\ORM\Mapping\ClassMetadataInfo;
18+
use Doctrine\ORM\Mapping\ClassMetadata;
19+
use Doctrine\ORM\Mapping\FieldMapping;
1920
use Doctrine\Persistence\ManagerRegistry;
2021

2122
/**
@@ -55,7 +56,7 @@ public function create(string $resourceClass, string $property, array $options =
5556
break;
5657
}
5758

58-
if ($doctrineClassMetadata instanceof ClassMetadataInfo) {
59+
if ($doctrineClassMetadata instanceof ClassMetadata) {
5960
$writable = $doctrineClassMetadata->isIdentifierNatural();
6061
} else {
6162
$writable = false;
@@ -67,10 +68,13 @@ public function create(string $resourceClass, string $property, array $options =
6768
}
6869
}
6970

70-
if ($doctrineClassMetadata instanceof ClassMetadataInfo && \in_array($property, $doctrineClassMetadata->getFieldNames(), true)) {
71-
/** @var mixed[] */
71+
if ($doctrineClassMetadata instanceof ClassMetadata && \in_array($property, $doctrineClassMetadata->getFieldNames(), true)) {
7272
$fieldMapping = $doctrineClassMetadata->getFieldMapping($property);
73-
$propertyMetadata = $propertyMetadata->withDefault($fieldMapping['options']['default'] ?? $propertyMetadata->getDefault());
73+
if (class_exists(FieldMapping::class) && $fieldMapping instanceof FieldMapping) {
74+
$propertyMetadata = $propertyMetadata->withDefault($fieldMapping->default ?? $propertyMetadata->getDefault());
75+
} else {
76+
$propertyMetadata = $propertyMetadata->withDefault($fieldMapping['options']['default'] ?? $propertyMetadata->getDefault());
77+
}
7478
}
7579

7680
return $propertyMetadata;

src/Doctrine/Orm/State/LinksHandlerTrait.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use ApiPlatform\Doctrine\Orm\Util\QueryNameGenerator;
1818
use ApiPlatform\Metadata\Link;
1919
use ApiPlatform\Metadata\Operation;
20-
use Doctrine\ORM\Mapping\ClassMetadataInfo;
20+
use Doctrine\ORM\Mapping\ClassMetadata;
2121
use Doctrine\ORM\QueryBuilder;
2222

2323
/**
@@ -83,7 +83,7 @@ private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, Que
8383
$associationMapping = $fromClassMetadata->getAssociationMapping($link->getFromProperty()); // @phpstan-ignore-line
8484
$relationType = $associationMapping['type'];
8585

86-
if ($relationType & ClassMetadataInfo::TO_MANY) {
86+
if ($relationType & ClassMetadata::TO_MANY) {
8787
$nextAlias = $queryNameGenerator->generateJoinAlias($alias);
8888
$whereClause = [];
8989
foreach ($identifierProperties as $identifierProperty) {
@@ -100,7 +100,7 @@ private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, Que
100100
}
101101

102102
// A single-valued association path expression to an inverse side is not supported in DQL queries.
103-
if ($relationType & ClassMetadataInfo::TO_ONE && !($associationMapping['isOwningSide'] ?? true)) {
103+
if ($relationType & ClassMetadata::TO_ONE && !($associationMapping['isOwningSide'] ?? true)) {
104104
$queryBuilder->innerJoin("$previousAlias.".$associationMapping['mappedBy'], $joinAlias);
105105
} else {
106106
$queryBuilder->join(

0 commit comments

Comments
 (0)