From f84971bcd1a5a727e3d573a4f905dd1659677ed1 Mon Sep 17 00:00:00 2001 From: Urban Suppiger Date: Sat, 15 Jun 2024 10:34:04 +0200 Subject: [PATCH 1/2] chore: upgrade to doctrine/orm 3 --- api/composer.json | 7 +- api/composer.lock | 125 +++++++++--------- api/src/HttpCache/PurgeHttpCacheListener.php | 19 ++- api/src/Repository/UserRepository.php | 6 +- .../RelatedCollectionLinkNormalizer.php | 28 ++-- api/src/Util/IdGenerator.php | 4 +- .../Factory/UriTemplateFactoryTest.php | 2 +- .../RelatedCollectionLinkNormalizerTest.php | 83 ++++++++---- .../Util/CamelPascalNamingStrategyTest.php | 2 +- 9 files changed, 157 insertions(+), 119 deletions(-) diff --git a/api/composer.json b/api/composer.json index 088f750344..1bb55dd73e 100644 --- a/api/composer.json +++ b/api/composer.json @@ -10,10 +10,11 @@ "cweagans/composer-patches": "1.7.3", "doctrine/doctrine-bundle": "2.12.0", "doctrine/doctrine-migrations-bundle": "3.3.1", - "doctrine/orm": "2.19.5", + "doctrine/orm": "3.2.0", "exercise/htmlpurifier-bundle": "5.0", "friendsofsymfony/http-cache": "3.0.0", "friendsofsymfony/http-cache-bundle": "3.0.1", + "gedmo/doctrine-extensions": "dev-main as 3.16", "google/recaptcha": "1.3.0", "guzzlehttp/guzzle": "7.8.1", "knpuniversity/oauth2-client-bundle": "2.18.1", @@ -52,7 +53,7 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "3.58.1", - "hautelook/alice-bundle": "2.13.0", + "hautelook/alice-bundle": "2.14.0", "justinrainbow/json-schema": "5.2.13", "php-coveralls/php-coveralls": "2.7.0", "phpspec/prophecy-phpunit": "2.2", @@ -156,4 +157,4 @@ } } } -} \ No newline at end of file +} diff --git a/api/composer.lock b/api/composer.lock index 87c8479d59..c7a23fbff0 100644 --- a/api/composer.lock +++ b/api/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e94893941cb6c3cd2cc6b2de4c62b13d", + "content-hash": "40ac1826db60324bea3b277858502c9d", "packages": [ { "name": "api-platform/core", @@ -1565,61 +1565,48 @@ }, { "name": "doctrine/orm", - "version": "2.19.5", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "94986af28452da42a46a4489d1c958a2e5d710e5" + "reference": "37946d3a21ddf837c0d84f8156ee60a92102e332" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/94986af28452da42a46a4489d1c958a2e5d710e5", - "reference": "94986af28452da42a46a4489d1c958a2e5d710e5", + "url": "https://api.github.com/repos/doctrine/orm/zipball/37946d3a21ddf837c0d84f8156ee60a92102e332", + "reference": "37946d3a21ddf837c0d84f8156ee60a92102e332", "shasum": "" }, "require": { "composer-runtime-api": "^2", - "doctrine/cache": "^1.12.1 || ^2.1.1", - "doctrine/collections": "^1.5 || ^2.1", - "doctrine/common": "^3.0.3", - "doctrine/dbal": "^2.13.1 || ^3.2", + "doctrine/collections": "^2.2", + "doctrine/dbal": "^3.8.2 || ^4", "doctrine/deprecations": "^0.5.3 || ^1", "doctrine/event-manager": "^1.2 || ^2", "doctrine/inflector": "^1.4 || ^2.0", "doctrine/instantiator": "^1.3 || ^2", - "doctrine/lexer": "^2 || ^3", - "doctrine/persistence": "^2.4 || ^3", + "doctrine/lexer": "^3", + "doctrine/persistence": "^3.3.1", "ext-ctype": "*", - "php": "^7.1 || ^8.0", + "php": "^8.1", "psr/cache": "^1 || ^2 || ^3", - "symfony/console": "^4.2 || ^5.0 || ^6.0 || ^7.0", - "symfony/polyfill-php72": "^1.23", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "doctrine/annotations": "<1.13 || >= 3.0" + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/var-exporter": "^6.3.9 || ^7.0" }, "require-dev": { - "doctrine/annotations": "^1.13 || ^2", - "doctrine/coding-standard": "^9.0.2 || ^12.0", - "phpbench/phpbench": "^0.16.10 || ^1.0", - "phpstan/phpstan": "~1.4.10 || 1.10.59", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", + "doctrine/coding-standard": "^12.0", + "phpbench/phpbench": "^1.0", + "phpstan/phpstan": "1.11.1", + "phpunit/phpunit": "^10.4.0", "psr/log": "^1 || ^2 || ^3", "squizlabs/php_codesniffer": "3.7.2", - "symfony/cache": "^4.4 || ^5.4 || ^6.4 || ^7.0", - "symfony/var-exporter": "^4.4 || ^5.4 || ^6.2 || ^7.0", - "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0", - "vimeo/psalm": "4.30.0 || 5.22.2" + "symfony/cache": "^5.4 || ^6.2 || ^7.0", + "vimeo/psalm": "5.24.0" }, "suggest": { "ext-dom": "Provides support for XSD validation for XML mapping files", - "symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0", - "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" + "symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0" }, - "bin": [ - "bin/doctrine" - ], "type": "library", "autoload": { "psr-4": { @@ -1660,9 +1647,9 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/2.19.5" + "source": "https://github.com/doctrine/orm/tree/3.2.0" }, - "time": "2024-04-30T06:49:54+00:00" + "time": "2024-05-23T14:27:52+00:00" }, { "name": "doctrine/persistence", @@ -2190,16 +2177,16 @@ }, { "name": "gedmo/doctrine-extensions", - "version": "v3.15.0", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/doctrine-extensions/DoctrineExtensions.git", - "reference": "2a89103f4984d8970f3855284c8c04e6e6a63c0f" + "reference": "165fc33c70577028f62b6a8c46048161db20f559" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine-extensions/DoctrineExtensions/zipball/2a89103f4984d8970f3855284c8c04e6e6a63c0f", - "reference": "2a89103f4984d8970f3855284c8c04e6e6a63c0f", + "url": "https://api.github.com/repos/doctrine-extensions/DoctrineExtensions/zipball/165fc33c70577028f62b6a8c46048161db20f559", + "reference": "165fc33c70577028f62b6a8c46048161db20f559", "shasum": "" }, "require": { @@ -2218,7 +2205,7 @@ "doctrine/annotations": "<1.13 || >=3.0", "doctrine/dbal": "<3.2 || >=4.0", "doctrine/mongodb-odm": "<2.3 || >=3.0", - "doctrine/orm": "<2.14.0 || 2.16.0 || 2.16.1 || >=3.0" + "doctrine/orm": "<2.14.0 || 2.16.0 || 2.16.1 || >=4.0" }, "require-dev": { "doctrine/annotations": "^1.13 || ^2.0", @@ -2226,26 +2213,29 @@ "doctrine/dbal": "^3.2", "doctrine/doctrine-bundle": "^2.3", "doctrine/mongodb-odm": "^2.3", - "doctrine/orm": "^2.14.0", + "doctrine/orm": "^2.14.0 || ^3.0", "friendsofphp/php-cs-fixer": "^3.14.0", "nesbot/carbon": "^2.71 || ^3.0", - "phpstan/phpstan": "^1.10.2", - "phpstan/phpstan-doctrine": "^1.0", - "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-doctrine": "^1.4", + "phpstan/phpstan-phpunit": "^1.4", "phpunit/phpunit": "^9.6", - "rector/rector": "^0.19", + "rector/rector": "^1.1", "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/doctrine-bridge": "^5.4 || ^6.0 || ^7.0", "symfony/phpunit-bridge": "^6.0 || ^7.0", + "symfony/uid": "^5.4 || ^6.0 || ^7.0", "symfony/yaml": "^5.4 || ^6.0 || ^7.0" }, "suggest": { "doctrine/mongodb-odm": "to use the extensions with the MongoDB ODM", "doctrine/orm": "to use the extensions with the ORM" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.13-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -2293,7 +2283,7 @@ "support": { "email": "gediminas.morkevicius@gmail.com", "issues": "https://github.com/doctrine-extensions/DoctrineExtensions/issues", - "source": "https://github.com/doctrine-extensions/DoctrineExtensions/tree/v3.15.0", + "source": "https://github.com/doctrine-extensions/DoctrineExtensions/tree/main", "wiki": "https://github.com/Atlantic18/DoctrineExtensions/tree/main/doc" }, "funding": [ @@ -2314,7 +2304,7 @@ "type": "github" } ], - "time": "2024-02-12T15:17:22+00:00" + "time": "2024-06-10T12:19:13+00:00" }, { "name": "google/recaptcha", @@ -11183,35 +11173,35 @@ }, { "name": "hautelook/alice-bundle", - "version": "2.13.0", + "version": "2.14.0", "source": { "type": "git", "url": "https://github.com/theofidry/AliceBundle.git", - "reference": "7b8cf62973853ec406ecb27f3b90b91a1b525a05" + "reference": "9b648956ffe4c39318dc61cbe8c2a7e209e9ab58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/AliceBundle/zipball/7b8cf62973853ec406ecb27f3b90b91a1b525a05", - "reference": "7b8cf62973853ec406ecb27f3b90b91a1b525a05", + "url": "https://api.github.com/repos/theofidry/AliceBundle/zipball/9b648956ffe4c39318dc61cbe8c2a7e209e9ab58", + "reference": "9b648956ffe4c39318dc61cbe8c2a7e209e9ab58", "shasum": "" }, "require": { - "doctrine/data-fixtures": "^1.5", - "doctrine/doctrine-bundle": "^2.5", - "doctrine/orm": "^2.10.0", - "doctrine/persistence": "^2.2 || ^3.0", + "doctrine/data-fixtures": "^1.7", + "doctrine/doctrine-bundle": "^2.11.3", + "doctrine/orm": "^3.1", + "doctrine/persistence": "^3.3.1", "php": "^8.2", "psr/log": "^1.0 || ^2.0 || ^3.0", "symfony/finder": "^6.4 || ^7.0", "symfony/framework-bundle": "^6.4 || ^7.0", - "theofidry/alice-data-fixtures": "^1.5" + "theofidry/alice-data-fixtures": "^1.7" }, "require-dev": { "monolog/monolog": "^3.5", - "phpspec/prophecy": "^1.7", + "phpspec/prophecy": "^1.14.0", "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.5", - "symfony/phpunit-bridge": "^6.4 || ^7.0" + "phpunit/phpunit": "^9.6.17", + "symfony/phpunit-bridge": "^6.4.4 || ^7.0" }, "type": "symfony-bundle", "extra": { @@ -11251,9 +11241,9 @@ ], "support": { "issues": "https://github.com/theofidry/AliceBundle/issues", - "source": "https://github.com/theofidry/AliceBundle/tree/2.13.0" + "source": "https://github.com/theofidry/AliceBundle/tree/2.14.0" }, - "time": "2023-12-03T23:53:29+00:00" + "time": "2024-03-08T19:55:04+00:00" }, { "name": "justinrainbow/json-schema", @@ -14886,9 +14876,18 @@ "time": "2024-05-01T19:32:08+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "gedmo/doctrine-extensions", + "version": "dev-main", + "alias": "3.16", + "alias_normalized": "3.16.0.0" + } + ], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "gedmo/doctrine-extensions": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/api/src/HttpCache/PurgeHttpCacheListener.php b/api/src/HttpCache/PurgeHttpCacheListener.php index 8c5bbd59b3..2697e2ea49 100644 --- a/api/src/HttpCache/PurgeHttpCacheListener.php +++ b/api/src/HttpCache/PurgeHttpCacheListener.php @@ -50,7 +50,7 @@ public function __construct(private readonly IriConverterInterface|LegacyIriConv */ public function preUpdate(PreUpdateEventArgs $eventArgs): void { $changeSet = $eventArgs->getEntityChangeSet(); - $objectManager = method_exists($eventArgs, 'getObjectManager') ? $eventArgs->getObjectManager() : $eventArgs->getEntityManager(); + $objectManager = $eventArgs->getObjectManager(); $associationMappings = $objectManager->getClassMetadata(ClassUtils::getClass($eventArgs->getObject()))->getAssociationMappings(); foreach ($changeSet as $key => $value) { @@ -72,7 +72,12 @@ public function preUpdate(PreUpdateEventArgs $eventArgs): void { * Collects tags from inserted, updated and deleted entities, including relations. */ public function onFlush(OnFlushEventArgs $eventArgs): void { - $em = method_exists($eventArgs, 'getObjectManager') ? $eventArgs->getObjectManager() : $eventArgs->getEntityManager(); + $em = $eventArgs->getObjectManager(); + + if (!$em instanceof EntityManagerInterface) { + return; + } + $uow = $em->getUnitOfWork(); foreach ($uow->getScheduledEntityInsertions() as $entity) { @@ -179,15 +184,7 @@ private function gatherRelationTags(EntityManagerInterface $em, object $entity): $associationMappings = $em->getClassMetadata(ClassUtils::getClass($entity))->getAssociationMappings(); foreach ($associationMappings as $property => $associationMapping) { - // @phpstan-ignore-next-line - if (class_exists(AssociationMapping::class) && $associationMapping instanceof AssociationMapping && ($associationMapping->targetEntity ?? null) && !$this->resourceClassResolver->isResourceClass($associationMapping->targetEntity)) { - return; - } - - // @phpstan-ignore-next-line - if (\is_array($associationMapping) - && \array_key_exists('targetEntity', $associationMapping) - && !$this->resourceClassResolver->isResourceClass($associationMapping['targetEntity'])) { + if ($associationMapping instanceof AssociationMapping && ($associationMapping->targetEntity ?? null) && !$this->resourceClassResolver->isResourceClass($associationMapping->targetEntity)) { return; } diff --git a/api/src/Repository/UserRepository.php b/api/src/Repository/UserRepository.php index 6be84d88e1..0829c2a014 100644 --- a/api/src/Repository/UserRepository.php +++ b/api/src/Repository/UserRepository.php @@ -32,8 +32,8 @@ public function upgradePassword(PasswordAuthenticatedUserInterface $user, string } $user->password = $newHashedPassword; - $this->_em->persist($user); - $this->_em->flush(); + $this->getEntityManager()->persist($user); + $this->getEntityManager()->flush(); } /** @@ -41,7 +41,7 @@ public function upgradePassword(PasswordAuthenticatedUserInterface $user, string * @throws NoResultException */ public function loadUserByIdentifier(string $identifier): ?User { - $queryBuilder = $this->_em->createQueryBuilder(); + $queryBuilder = $this->getEntityManager()->createQueryBuilder(); $queryBuilder->select('user'); $queryBuilder->from(User::class, 'user'); $queryBuilder->join('user.profile', 'profile'); diff --git a/api/src/Serializer/Normalizer/RelatedCollectionLinkNormalizer.php b/api/src/Serializer/Normalizer/RelatedCollectionLinkNormalizer.php index 4462d48ad0..c3c2ad6c80 100644 --- a/api/src/Serializer/Normalizer/RelatedCollectionLinkNormalizer.php +++ b/api/src/Serializer/Normalizer/RelatedCollectionLinkNormalizer.php @@ -15,9 +15,11 @@ use App\Metadata\Resource\OperationHelper; use App\Util\ClassInfoTrait; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Mapping\AssociationMapping; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\InverseSideMapping; use Doctrine\ORM\Mapping\MappingException; -use Doctrine\Persistence\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\OwningSideMapping; use Rize\UriTemplate; use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; @@ -155,8 +157,8 @@ public function getRelatedCollectionHref($object, $rel, array $context = []): st try { $classMetadata = $this->getClassMetadata($resourceClass); - if (!$classMetadata instanceof ClassMetadataInfo) { - throw new \RuntimeException("The class metadata for {$resourceClass} must be an instance of ClassMetadataInfo."); + if (!$classMetadata instanceof ClassMetadata) { + throw new \RuntimeException("The class metadata for {$resourceClass} must be an instance of ClassMetadata."); } $relationMetadata = $classMetadata->getAssociationMapping($rel); @@ -164,10 +166,8 @@ public function getRelatedCollectionHref($object, $rel, array $context = []): st throw new UnsupportedRelationException($resourceClass.'#'.$rel.' is not a Doctrine association. Embedding non-Doctrine collections is currently not implemented.'); } - $relatedResourceClass = $relationMetadata['targetEntity']; - - $relatedFilterName = $relationMetadata['mappedBy']; - $relatedFilterName ??= $relationMetadata['inversedBy']; + $relatedResourceClass = $relationMetadata->targetEntity; + $relatedFilterName = $this->getRelatedProperty($relationMetadata); if (empty($relatedResourceClass) || empty($relatedFilterName)) { throw new UnsupportedRelationException('The '.$resourceClass.'#'.$rel.' relation does not have both a targetEntity and a mappedBy or inversedBy property'); @@ -251,4 +251,16 @@ private function exactSearchFilterExists(string $resourceClass, mixed $propertyN && 'exact' === $filterDescription[$propertyName]['strategy']; })); } + + private function getRelatedProperty(AssociationMapping $mapping): ?string { + if ($mapping instanceof InverseSideMapping) { + return $mapping->mappedBy ?? null; + } + + if ($mapping instanceof OwningSideMapping) { + return $mapping->inversedBy ?? null; + } + + return null; + } } diff --git a/api/src/Util/IdGenerator.php b/api/src/Util/IdGenerator.php index a45db9d0e2..8ae71587ed 100644 --- a/api/src/Util/IdGenerator.php +++ b/api/src/Util/IdGenerator.php @@ -2,7 +2,7 @@ namespace App\Util; -use Doctrine\ORM\EntityManager; +use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Id\AbstractIdGenerator; class IdGenerator extends AbstractIdGenerator { @@ -10,7 +10,7 @@ public static function generateRandomHexString(int $length): string { return bin2hex(random_bytes($length / 2)); } - public function generate(EntityManager $em, $entity): string { + public function generateId(EntityManagerInterface $em, ?object $entity): mixed { return IdGenerator::generateRandomHexString(12); } } diff --git a/api/tests/Metadata/Resource/Factory/UriTemplateFactoryTest.php b/api/tests/Metadata/Resource/Factory/UriTemplateFactoryTest.php index b250fef235..9379bd627f 100644 --- a/api/tests/Metadata/Resource/Factory/UriTemplateFactoryTest.php +++ b/api/tests/Metadata/Resource/Factory/UriTemplateFactoryTest.php @@ -2,8 +2,8 @@ namespace App\Tests\Metadata\Resource\Factory; -use ApiPlatform\Api\FilterInterface; use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\FilterInterface; use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\IriConverterInterface; diff --git a/api/tests/Serializer/Normalizer/RelatedCollectionLinkNormalizerTest.php b/api/tests/Serializer/Normalizer/RelatedCollectionLinkNormalizerTest.php index 803884aeb1..7e52cd15d0 100644 --- a/api/tests/Serializer/Normalizer/RelatedCollectionLinkNormalizerTest.php +++ b/api/tests/Serializer/Normalizer/RelatedCollectionLinkNormalizerTest.php @@ -2,10 +2,10 @@ namespace App\Tests\Serializer\Normalizer; -use ApiPlatform\Api\FilterInterface; use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\FilterInterface; use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\IriConverterInterface; @@ -18,6 +18,7 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping as ORM; +use Doctrine\ORM\Mapping\OneToManyAssociationMapping; use Doctrine\Persistence\ManagerRegistry; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -160,7 +161,13 @@ public function testNormalizeReplacesLinkArrayWithSingleFilteredCollectionLink() $resource = new ParentEntity(); $this->mockDecoratedNormalizer(); $this->mockNameConverter(); - $this->mockAssociationMetadata(['targetEntity' => Child::class, 'mappedBy' => 'parent']); + $this->mockAssociationMetadata(OneToManyAssociationMapping::fromMappingArray([ + 'targetEntity' => Child::class, + 'mappedBy' => 'parent', + 'fieldName' => 'children', + 'sourceEntity' => ParentEntity::class, + ])); + $this->mockRelatedResourceMetadata(['filters' => ['attribute_filter_something_something']]); $this->mockRelatedFilterDescription(['parent' => ['strategy' => 'exact']]); $this->mockGeneratedRoute(); @@ -232,7 +239,12 @@ public function testNormalizeReplacesSerializedNameLinkArray() { $this->mockRelatedResourceMetadata(['filters' => ['attribute_filter_something_something']]); $this->mockRelatedFilterDescription(['parent' => ['strategy' => 'exact']]); - $this->mockAssociationMetadata(['targetEntity' => Child::class, 'mappedBy' => 'parent']); + $this->mockAssociationMetadata(OneToManyAssociationMapping::fromMappingArray([ + 'targetEntity' => Child::class, + 'mappedBy' => 'parent', + 'fieldName' => 'children', + 'sourceEntity' => ParentEntity::class, + ])); $this->mockGeneratedRoute(); // when @@ -253,7 +265,12 @@ public function testNormalizeDoesntReplaceWhenFilterDoesntApplyToMappedProperty( $resource = new ParentEntity(); $this->mockDecoratedNormalizer(); $this->mockNameConverter(); - $this->mockAssociationMetadata(['targetEntity' => Child::class, 'mappedBy' => 'parent']); + $this->mockAssociationMetadata(OneToManyAssociationMapping::fromMappingArray([ + 'targetEntity' => Child::class, + 'mappedBy' => 'parent', + 'fieldName' => 'children', + 'sourceEntity' => ParentEntity::class, + ])); $this->mockRelatedResourceMetadata(['filters' => ['attribute_filter_something_something']]); $this->mockRelatedFilterDescription(['some_other_property' => ['strategy' => 'exact']]); $this->mockGeneratedRoute(); @@ -270,7 +287,12 @@ public function testNormalizeDoesntReplaceWhenEmptyFiltersArray() { $resource = new ParentEntity(); $this->mockDecoratedNormalizer(); $this->mockNameConverter(); - $this->mockAssociationMetadata(['targetEntity' => Child::class, 'mappedBy' => 'parent']); + $this->mockAssociationMetadata(OneToManyAssociationMapping::fromMappingArray([ + 'targetEntity' => Child::class, + 'mappedBy' => 'parent', + 'fieldName' => 'children', + 'sourceEntity' => ParentEntity::class, + ])); $this->mockRelatedResourceMetadata(['filters' => []]); $this->mockRelatedFilterDescription(['parent' => ['strategy' => 'exact']]); $this->mockGeneratedRoute(); @@ -287,7 +309,12 @@ public function testNormalizeDoesntReplaceWhenNoFilters() { $resource = new ParentEntity(); $this->mockDecoratedNormalizer(); $this->mockNameConverter(); - $this->mockAssociationMetadata(['targetEntity' => Child::class, 'mappedBy' => 'parent']); + $this->mockAssociationMetadata(OneToManyAssociationMapping::fromMappingArray([ + 'targetEntity' => Child::class, + 'mappedBy' => 'parent', + 'fieldName' => 'children', + 'sourceEntity' => ParentEntity::class, + ])); $this->mockRelatedResourceMetadata([]); $this->mockRelatedFilterDescription(['parent' => ['strategy' => 'exact']]); $this->mockGeneratedRoute(); @@ -299,23 +326,6 @@ public function testNormalizeDoesntReplaceWhenNoFilters() { $this->shouldNotReplaceChildren($result); } - public function testNormalizeDoesntReplaceWhenTargetEntityIsMissing() { - // given - $resource = new ParentEntity(); - $this->mockDecoratedNormalizer(); - $this->mockNameConverter(); - $this->mockAssociationMetadata(['targetEntity' => null, 'mappedBy' => 'parent']); - $this->mockRelatedResourceMetadata(['filters' => ['attribute_filter_something_something']]); - $this->mockRelatedFilterDescription(['parent' => ['strategy' => 'exact']]); - $this->mockGeneratedRoute(); - - // when - $result = $this->normalizer->normalize($resource, null, ['resource_class' => ParentEntity::class]); - - // then - $this->shouldNotReplaceChildren($result); - } - public function testNormalizeDoesntReplaceWhenNotADoctrineAssociation() { // given $resource = new ParentEntity(); @@ -343,7 +353,11 @@ public function testNormalizeDoesntReplaceWhenMappedByIsMissing() { $resource = new ParentEntity(); $this->mockDecoratedNormalizer(); $this->mockNameConverter(); - $this->mockAssociationMetadata(['targetEntity' => Child::class, 'mappedBy' => null, 'inversedBy' => null]); + $this->mockAssociationMetadata(OneToManyAssociationMapping::fromMappingArray([ + 'targetEntity' => Child::class, + 'fieldName' => 'children', + 'sourceEntity' => ParentEntity::class, + ])); $this->mockRelatedResourceMetadata(['filters' => ['attribute_filter_something_something']]); $this->mockRelatedFilterDescription(['parent' => ['strategy' => 'exact']]); $this->mockGeneratedRoute(); @@ -360,7 +374,12 @@ public function testNormalizeDoesntReplaceWhenFilterDoesntExistInContainer() { $resource = new ParentEntity(); $this->mockDecoratedNormalizer(); $this->mockNameConverter(); - $this->mockAssociationMetadata(['targetEntity' => Child::class, 'mappedBy' => 'parent']); + $this->mockAssociationMetadata(OneToManyAssociationMapping::fromMappingArray([ + 'targetEntity' => Child::class, + 'mappedBy' => 'parent', + 'fieldName' => 'children', + 'sourceEntity' => ParentEntity::class, + ])); $this->mockRelatedResourceMetadata(['filters' => ['attribute_filter_something_something']]); $this->filterInstance = null; $this->mockGeneratedRoute(); @@ -377,7 +396,12 @@ public function testNormalizeDoesntReplaceWhenFilterIsNotSearchFilter() { $resource = new ParentEntity(); $this->mockDecoratedNormalizer(); $this->mockNameConverter(); - $this->mockAssociationMetadata(['targetEntity' => Child::class, 'mappedBy' => 'parent']); + $this->mockAssociationMetadata(OneToManyAssociationMapping::fromMappingArray([ + 'targetEntity' => Child::class, + 'mappedBy' => 'parent', + 'fieldName' => 'children', + 'sourceEntity' => ParentEntity::class, + ])); $this->mockRelatedResourceMetadata(['filters' => ['attribute_filter_something_something']]); $this->filterInstance = new DateFilter($this->managerRegistryMock, null, ['filters' => ['attribute_filter_something_something']]); $this->mockGeneratedRoute(); @@ -394,7 +418,12 @@ public function testNormalizeDoesntReplaceWhenMissingGetCollectionOperation() { $resource = new ParentEntity(); $this->mockDecoratedNormalizer(); $this->mockNameConverter(); - $this->mockAssociationMetadata(['targetEntity' => Child::class, 'mappedBy' => 'parent']); + $this->mockAssociationMetadata(OneToManyAssociationMapping::fromMappingArray([ + 'targetEntity' => Child::class, + 'mappedBy' => 'parent', + 'fieldName' => 'children', + 'sourceEntity' => ParentEntity::class, + ])); $metadataCollection = new ResourceMetadataCollection('Dummy'); $metadataCollection->append((new ApiResource())->withOperations(new Operations([new Get()]))); diff --git a/api/tests/Util/CamelPascalNamingStrategyTest.php b/api/tests/Util/CamelPascalNamingStrategyTest.php index 84cb01a92c..f5b58c8ab1 100644 --- a/api/tests/Util/CamelPascalNamingStrategyTest.php +++ b/api/tests/Util/CamelPascalNamingStrategyTest.php @@ -41,7 +41,7 @@ public function testPropertyToColumnName(string $input, string $output) { $strategy = new CamelPascalNamingStrategy(); // when - $result = $strategy->propertyToColumnName($input); + $result = $strategy->propertyToColumnName($input, ''); // then $this->assertEquals($output, $result); From 74eabbab746351a1c4f4394ae91618aa8592ec9e Mon Sep 17 00:00:00 2001 From: Urban Suppiger Date: Tue, 9 Jul 2024 21:09:40 +0200 Subject: [PATCH 2/2] fix ClassMetadataInfo --- api/src/HttpCache/PurgeHttpCacheListener.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/HttpCache/PurgeHttpCacheListener.php b/api/src/HttpCache/PurgeHttpCacheListener.php index 2e0f1ba761..b803c1b52b 100644 --- a/api/src/HttpCache/PurgeHttpCacheListener.php +++ b/api/src/HttpCache/PurgeHttpCacheListener.php @@ -31,7 +31,7 @@ use Doctrine\ORM\Event\OnFlushEventArgs; use Doctrine\ORM\Event\PreUpdateEventArgs; use Doctrine\ORM\Mapping\AssociationMapping; -use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\PersistentCollection; use FOS\HttpCacheBundle\CacheManager; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; @@ -121,7 +121,7 @@ public function postFlush(): void { private function addTagsForManyToManyRelations($collection, $entities) { $associationMapping = $collection->getMapping(); - if (ClassMetadataInfo::MANY_TO_MANY !== $associationMapping['type']) { + if (ClassMetadata::MANY_TO_MANY !== $associationMapping['type']) { return; }