diff --git a/src/Bridge/Doctrine/Orm/ItemDataProvider.php b/src/Bridge/Doctrine/Orm/ItemDataProvider.php index 0d820f9ac98..e51913ed15f 100644 --- a/src/Bridge/Doctrine/Orm/ItemDataProvider.php +++ b/src/Bridge/Doctrine/Orm/ItemDataProvider.php @@ -13,15 +13,14 @@ use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface; use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryResultItemExtensionInterface; +use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\IdentifierManagerTrait; use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGenerator; use ApiPlatform\Core\DataProvider\ItemDataProviderInterface; -use ApiPlatform\Core\Exception\PropertyNotFoundException; use ApiPlatform\Core\Exception\ResourceClassNotSupportedException; use ApiPlatform\Core\Exception\RuntimeException; use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface; use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface; use Doctrine\Common\Persistence\ManagerRegistry; -use Doctrine\Common\Persistence\ObjectManager; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\QueryBuilder; @@ -38,11 +37,12 @@ class ItemDataProvider implements ItemDataProviderInterface private $propertyMetadataFactory; private $itemExtensions; + use IdentifierManagerTrait; + /** - * @param ManagerRegistry $managerRegistry - * @param PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory - * @param PropertyMetadataFactoryInterface $propertyMetadataFactory - * @param QueryItemExtensionInterface[] $itemExtensions + * @param ManagerRegistry $managerRegistry + * @param IdentifierManagerInterface $identifierManager + * @param QueryItemExtensionInterface[] $itemExtensions */ public function __construct(ManagerRegistry $managerRegistry, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, array $itemExtensions = []) { @@ -114,55 +114,4 @@ private function addWhereForIdentifiers(array $identifiers, QueryBuilder $queryB $queryBuilder->setParameter($placeholder, $value); } } - - /** - * Transform and check the identifier, composite or not. - * - * @param int|string $id - * @param ObjectManager $manager - * @param string $resourceClass - * - * @throws PropertyNotFoundException - * - * @return array - */ - private function normalizeIdentifiers($id, ObjectManager $manager, string $resourceClass): array - { - $identifierValues = [$id]; - $doctrineMetadataIdentifier = $manager->getClassMetadata($resourceClass)->getIdentifier(); - - if (count($doctrineMetadataIdentifier) >= 2) { - $identifiers = explode(';', $id); - $identifiersMap = []; - - // first transform identifiers to a proper key/value array - foreach ($identifiers as $identifier) { - $keyValue = explode('=', $identifier); - $identifiersMap[$keyValue[0]] = $keyValue[1]; - } - } - - $identifiers = []; - $i = 0; - - foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $propertyName) { - $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName); - - $identifier = $propertyMetadata->isIdentifier(); - if (null === $identifier || false === $identifier) { - continue; - } - - $identifier = !isset($identifiersMap) ? $identifierValues[$i] ?? null : $identifiersMap[$propertyName] ?? null; - - if (null === $identifier) { - throw new PropertyNotFoundException(sprintf('Invalid identifier "%s", "%s" has not been found.', $id, $propertyName)); - } - - $identifiers[$propertyName] = $identifier; - ++$i; - } - - return $identifiers; - } } diff --git a/src/Bridge/Doctrine/Orm/Util/IdentifierManagerTrait.php b/src/Bridge/Doctrine/Orm/Util/IdentifierManagerTrait.php new file mode 100644 index 00000000000..9e741ee4ad2 --- /dev/null +++ b/src/Bridge/Doctrine/Orm/Util/IdentifierManagerTrait.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ApiPlatform\Core\Bridge\Doctrine\Orm\Util; + +use ApiPlatform\Core\Exception\PropertyNotFoundException; +use Doctrine\Common\Persistence\ObjectManager; + +/** + * @internal + */ +trait IdentifierManagerTrait +{ + /** + * Transform and check the identifier, composite or not. + * + * @param int|string $id + * @param ObjectManager $manager + * @param string $resourceClass + * + * @throws PropertyNotFoundException + * + * @return array + */ + public function normalizeIdentifiers($id, ObjectManager $manager, string $resourceClass): array + { + $identifierValues = [$id]; + $doctrineMetadataIdentifier = $manager->getClassMetadata($resourceClass)->getIdentifier(); + + if (2 <= count($doctrineMetadataIdentifier)) { + $identifiers = explode(';', $id); + $identifiersMap = []; + + // first transform identifiers to a proper key/value array + foreach ($identifiers as $identifier) { + $keyValue = explode('=', $identifier); + $identifiersMap[$keyValue[0]] = $keyValue[1]; + } + } + + $identifiers = []; + $i = 0; + + foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $propertyName) { + $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName); + + $identifier = $propertyMetadata->isIdentifier(); + if (null === $identifier || false === $identifier) { + continue; + } + + $identifier = !isset($identifiersMap) ? $identifierValues[$i] ?? null : $identifiersMap[$propertyName] ?? null; + if (null === $identifier) { + throw new PropertyNotFoundException(sprintf('Invalid identifier "%s", "%s" has not been found.', $id, $propertyName)); + } + + $identifiers[$propertyName] = $identifier; + ++$i; + } + + return $identifiers; + } +} diff --git a/tests/Bridge/Doctrine/Orm/ItemDataProviderTest.php b/tests/Bridge/Doctrine/Orm/ItemDataProviderTest.php index f008f20de51..7ee30e33b2a 100644 --- a/tests/Bridge/Doctrine/Orm/ItemDataProviderTest.php +++ b/tests/Bridge/Doctrine/Orm/ItemDataProviderTest.php @@ -36,6 +36,47 @@ */ class ItemDataProviderTest extends \PHPUnit_Framework_TestCase { + private function getManagerRegistryProphecy(QueryBuilder $queryBuilder, array $identifiers, string $resourceClass) + { + $classMetadataProphecy = $this->prophesize(ClassMetadata::class); + $classMetadataProphecy->getIdentifier()->willReturn($identifiers)->shouldBeCalled(); + + $repositoryProphecy = $this->prophesize(EntityRepository::class); + $repositoryProphecy->createQueryBuilder('o')->willReturn($queryBuilder)->shouldBeCalled(); + + $managerProphecy = $this->prophesize(ObjectManager::class); + $managerProphecy->getClassMetadata($resourceClass)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal()); + $managerProphecy->getRepository($resourceClass)->willReturn($repositoryProphecy->reveal())->shouldBeCalled(); + + $managerRegistryProphecy = $this->prophesize(ManagerRegistry::class); + $managerRegistryProphecy->getManagerForClass(Dummy::class)->willReturn($managerProphecy->reveal())->shouldBeCalled(); + + return $managerRegistryProphecy->reveal(); + } + + private function getMetadataProphecies(array $identifiers, string $resourceClass) + { + $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class); + $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class); + + $nameCollection = ['foobar']; + + foreach ($identifiers as $identifier) { + $metadata = new PropertyMetadata(); + $metadata = $metadata->withIdentifier(true); + $propertyMetadataFactoryProphecy->create($resourceClass, $identifier)->willReturn($metadata); + + $nameCollection[] = $identifier; + } + + //random property to prevent the use of non-identifiers metadata while looping + $propertyMetadataFactoryProphecy->create($resourceClass, 'foobar')->willReturn(new PropertyMetadata()); + + $propertyNameCollectionFactoryProphecy->create($resourceClass)->willReturn(new PropertyNameCollection($nameCollection)); + + return [$propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal()]; + } + public function testGetItemSingleIdentifier() { $context = ['foo' => 'bar', 'fetch_data' => true]; @@ -56,29 +97,14 @@ public function testGetItemSingleIdentifier() $queryBuilder = $queryBuilderProphecy->reveal(); - $repositoryProphecy = $this->prophesize(EntityRepository::class); - $repositoryProphecy->createQueryBuilder('o')->willReturn($queryBuilder)->shouldBeCalled(); - - $managerProphecy = $this->prophesize(ObjectManager::class); - $managerProphecy->getRepository(Dummy::class)->willReturn($repositoryProphecy->reveal())->shouldBeCalled(); - $classMetadataProphecy = $this->prophesize(ClassMetadata::class); - $classMetadataProphecy->getIdentifier()->willReturn(['id'])->shouldBeCalled(); - $managerProphecy->getClassMetadata(Dummy::class)->willReturn($classMetadataProphecy->reveal())->shouldBeCalled(); - - $managerRegistryProphecy = $this->prophesize(ManagerRegistry::class); - $managerRegistryProphecy->getManagerForClass(Dummy::class)->willReturn($managerProphecy->reveal())->shouldBeCalled(); + $identifiers = ['id']; + list($propertyNameCollectionFactory, $propertyMetadataFactory) = $this->getMetadataProphecies($identifiers, Dummy::class); + $managerRegistry = $this->getManagerRegistryProphecy($queryBuilder, $identifiers, Dummy::class); $extensionProphecy = $this->prophesize(QueryItemExtensionInterface::class); $extensionProphecy->applyToItem($queryBuilder, Argument::type(QueryNameGeneratorInterface::class), Dummy::class, ['id' => 1], 'foo', $context)->shouldBeCalled(); - $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class); - $propertyNameCollectionFactoryProphecy->create(Dummy::class)->willReturn(new PropertyNameCollection(['id']))->shouldBeCalled(); - $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class); - $metadata = new PropertyMetadata(); - $metadata = $metadata->withIdentifier(true); - $propertyMetadataFactoryProphecy->create(Dummy::class, 'id')->willReturn($metadata)->shouldBeCalled(); - - $dataProvider = new ItemDataProvider($managerRegistryProphecy->reveal(), $propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), [$extensionProphecy->reveal()]); + $dataProvider = new ItemDataProvider($managerRegistry, $propertyNameCollectionFactory, $propertyMetadataFactory, [$extensionProphecy->reveal()]); $this->assertEquals([], $dataProvider->getItem(Dummy::class, 1, 'foo', $context)); } @@ -105,61 +131,18 @@ public function testGetItemDoubleIdentifier() $queryBuilder = $queryBuilderProphecy->reveal(); - $repositoryProphecy = $this->prophesize(EntityRepository::class); - $repositoryProphecy->createQueryBuilder('o')->willReturn($queryBuilder)->shouldBeCalled(); - - $managerProphecy = $this->prophesize(ObjectManager::class); - $managerProphecy->getRepository(Dummy::class)->willReturn($repositoryProphecy->reveal())->shouldBeCalled(); - $classMetadataProphecy = $this->prophesize(ClassMetadata::class); - $classMetadataProphecy->getIdentifier()->willReturn(['ida', 'idb'])->shouldBeCalled(); - $managerProphecy->getClassMetadata(Dummy::class)->willReturn($classMetadataProphecy->reveal())->shouldBeCalled(); - - $managerRegistryProphecy = $this->prophesize(ManagerRegistry::class); - $managerRegistryProphecy->getManagerForClass(Dummy::class)->willReturn($managerProphecy->reveal())->shouldBeCalled(); + $identifiers = ['ida', 'idb']; + list($propertyNameCollectionFactory, $propertyMetadataFactory) = $this->getMetadataProphecies($identifiers, Dummy::class); + $managerRegistry = $this->getManagerRegistryProphecy($queryBuilder, $identifiers, Dummy::class); $extensionProphecy = $this->prophesize(QueryItemExtensionInterface::class); $extensionProphecy->applyToItem($queryBuilder, Argument::type(QueryNameGeneratorInterface::class), Dummy::class, ['ida' => 1, 'idb' => 2], 'foo', [])->shouldBeCalled(); - $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class); - $propertyNameCollectionFactoryProphecy->create(Dummy::class)->willReturn(new PropertyNameCollection(['nonid', 'ida', 'idb']))->shouldBeCalled(); - $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class); - $metadata = new PropertyMetadata(); - $metadata = $metadata->withIdentifier(true); - $propertyMetadataFactoryProphecy->create(Dummy::class, 'nonid')->willReturn(new PropertyMetadata())->shouldBeCalled(); - $propertyMetadataFactoryProphecy->create(Dummy::class, 'ida')->willReturn($metadata)->shouldBeCalled(); - $propertyMetadataFactoryProphecy->create(Dummy::class, 'idb')->willReturn($metadata)->shouldBeCalled(); - - $dataProvider = new ItemDataProvider($managerRegistryProphecy->reveal(), $propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), [$extensionProphecy->reveal()]); + $dataProvider = new ItemDataProvider($managerRegistry, $propertyNameCollectionFactory, $propertyMetadataFactory, [$extensionProphecy->reveal()]); $this->assertEquals([], $dataProvider->getItem(Dummy::class, 'ida=1;idb=2', 'foo')); } - /** - * @expectedException \ApiPlatform\Core\Exception\PropertyNotFoundException - * @expectedExceptionMessage Invalid identifier "idbad=1;idb=2", "ida" has not been found. - */ - public function testInvalidIdentifier() - { - $managerProphecy = $this->prophesize(ObjectManager::class); - $classMetadataProphecy = $this->prophesize(ClassMetadata::class); - $classMetadataProphecy->getIdentifier()->willReturn(['ida', 'idb'])->shouldBeCalled(); - $managerProphecy->getClassMetadata(Dummy::class)->willReturn($classMetadataProphecy->reveal())->shouldBeCalled(); - - $managerRegistryProphecy = $this->prophesize(ManagerRegistry::class); - $managerRegistryProphecy->getManagerForClass(Dummy::class)->willReturn($managerProphecy->reveal())->shouldBeCalled(); - - $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class); - $propertyNameCollectionFactoryProphecy->create(Dummy::class)->willReturn(new PropertyNameCollection(['ida', 'idb']))->shouldBeCalled(); - $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class); - $metadata = new PropertyMetadata(); - $metadata = $metadata->withIdentifier(true); - $propertyMetadataFactoryProphecy->create(Dummy::class, 'ida')->willReturn($metadata)->shouldBeCalled(); - - $dataProvider = new ItemDataProvider($managerRegistryProphecy->reveal(), $propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), []); - - $this->assertEquals([], $dataProvider->getItem(Dummy::class, 'idbad=1;idb=2', 'foo')); - } - public function testQueryResultExtension() { $comparisonProphecy = $this->prophesize(Comparison::class); @@ -175,32 +158,16 @@ public function testQueryResultExtension() $queryBuilder = $queryBuilderProphecy->reveal(); - $repositoryProphecy = $this->prophesize(EntityRepository::class); - $repositoryProphecy->createQueryBuilder('o')->willReturn($queryBuilder)->shouldBeCalled(); - - $managerProphecy = $this->prophesize(ObjectManager::class); - $managerProphecy->getRepository(Dummy::class)->willReturn($repositoryProphecy->reveal())->shouldBeCalled(); - $classMetadataProphecy = $this->prophesize(ClassMetadata::class); - $classMetadataProphecy->getIdentifier()->willReturn(['id'])->shouldBeCalled(); - $managerProphecy->getClassMetadata(Dummy::class)->willReturn($classMetadataProphecy->reveal())->shouldBeCalled(); - - $managerRegistryProphecy = $this->prophesize(ManagerRegistry::class); - $managerRegistryProphecy->getManagerForClass(Dummy::class)->willReturn($managerProphecy->reveal())->shouldBeCalled(); + $identifiers = ['id']; + list($propertyNameCollectionFactory, $propertyMetadataFactory) = $this->getMetadataProphecies($identifiers, Dummy::class); + $managerRegistry = $this->getManagerRegistryProphecy($queryBuilder, $identifiers, Dummy::class); $extensionProphecy = $this->prophesize(QueryResultItemExtensionInterface::class); $extensionProphecy->applyToItem($queryBuilder, Argument::type(QueryNameGeneratorInterface::class), Dummy::class, ['id' => 1], 'foo', [])->shouldBeCalled(); $extensionProphecy->supportsResult(Dummy::class, 'foo')->willReturn(true)->shouldBeCalled(); $extensionProphecy->getResult($queryBuilder)->willReturn([])->shouldBeCalled(); - $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class); - $propertyNameCollectionFactoryProphecy->create(Dummy::class)->willReturn(new PropertyNameCollection(['id']))->shouldBeCalled(); - $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class); - - $metadata = new PropertyMetadata(); - $metadata = $metadata->withIdentifier(true); - $propertyMetadataFactoryProphecy->create(Dummy::class, 'id')->willReturn($metadata)->shouldBeCalled(); - - $dataProvider = new ItemDataProvider($managerRegistryProphecy->reveal(), $propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), [$extensionProphecy->reveal()]); + $dataProvider = new ItemDataProvider($managerRegistry, $propertyNameCollectionFactory, $propertyMetadataFactory, [$extensionProphecy->reveal()]); $this->assertEquals([], $dataProvider->getItem(Dummy::class, 1, 'foo')); } @@ -213,12 +180,12 @@ public function testThrowResourceClassNotSupportedException() $managerRegistryProphecy = $this->prophesize(ManagerRegistry::class); $managerRegistryProphecy->getManagerForClass(Dummy::class)->willReturn(null)->shouldBeCalled(); - $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class); - $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class); - $extensionProphecy = $this->prophesize(QueryItemExtensionInterface::class); - $dataProvider = new ItemDataProvider($managerRegistryProphecy->reveal(), $propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), [$extensionProphecy->reveal()]); + $identifiers = ['id']; + list($propertyNameCollectionFactory, $propertyMetadataFactory) = $this->getMetadataProphecies($identifiers, Dummy::class); + + $dataProvider = new ItemDataProvider($managerRegistryProphecy->reveal(), $propertyNameCollectionFactory, $propertyMetadataFactory, [$extensionProphecy->reveal()]); $dataProvider->getItem(Dummy::class, 'foo'); } @@ -228,26 +195,22 @@ public function testThrowResourceClassNotSupportedException() */ public function testCannotCreateQueryBuilder() { - $repositoryProphecy = $this->prophesize(ObjectRepository::class); + $identifiers = ['id']; + $repositoryProphecy = $this->prophesize(ObjectRepository::class); $classMetadataProphecy = $this->prophesize(ClassMetadata::class); - $classMetadataProphecy->getIdentifier()->willReturn(['id'])->shouldBeCalled(); - + $classMetadataProphecy->getIdentifier()->willReturn($identifiers)->shouldBeCalled(); $managerProphecy = $this->prophesize(ObjectManager::class); + $managerProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal()); $managerProphecy->getRepository(Dummy::class)->willReturn($repositoryProphecy->reveal())->shouldBeCalled(); - $managerProphecy->getClassMetadata(Dummy::class)->willReturn($classMetadataProphecy->reveal())->shouldBeCalled(); $managerRegistryProphecy = $this->prophesize(ManagerRegistry::class); $managerRegistryProphecy->getManagerForClass(Dummy::class)->willReturn($managerProphecy->reveal())->shouldBeCalled(); - $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class); - $propertyNameCollectionFactoryProphecy->create(Dummy::class)->willReturn(new PropertyNameCollection(['id']))->shouldBeCalled(); - - $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class); - $propertyMetadataFactoryProphecy->create(Dummy::class, 'id')->willReturn((new PropertyMetadata())->withIdentifier(true))->shouldBeCalled(); - $extensionProphecy = $this->prophesize(QueryItemExtensionInterface::class); - (new ItemDataProvider($managerRegistryProphecy->reveal(), $propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), [$extensionProphecy->reveal()]))->getItem(Dummy::class, 'foo'); + list($propertyNameCollectionFactory, $propertyMetadataFactory) = $this->getMetadataProphecies($identifiers, Dummy::class); + + (new ItemDataProvider($managerRegistryProphecy->reveal(), $propertyNameCollectionFactory, $propertyMetadataFactory, [$extensionProphecy->reveal()]))->getItem(Dummy::class, 'foo'); } } diff --git a/tests/Bridge/Doctrine/Util/IdentifierManagerTraitTest.php b/tests/Bridge/Doctrine/Util/IdentifierManagerTraitTest.php new file mode 100644 index 00000000000..b6116c148c0 --- /dev/null +++ b/tests/Bridge/Doctrine/Util/IdentifierManagerTraitTest.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ApiPlatform\Core\Tests\Doctrine\Util; + +use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\IdentifierManagerTrait; +use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface; +use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface; +use ApiPlatform\Core\Metadata\Property\PropertyMetadata; +use ApiPlatform\Core\Metadata\Property\PropertyNameCollection; +use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy; +use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\ORM\Mapping\ClassMetadata; + +class IdentifierManagerTraitImpl +{ + use IdentifierManagerTrait; + private $propertyNameCollectionFactory; + private $propertyMetadataFactory; + + public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory) + { + $this->propertyNameCollectionFactory = $propertyNameCollectionFactory; + $this->propertyMetadataFactory = $propertyMetadataFactory; + } +} + +class IdentifierManagerTraitTest extends \PHPUnit_Framework_TestCase +{ + private function getMetadataProphecies(array $identifiers, string $resourceClass) + { + $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class); + $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class); + + $nameCollection = ['foobar']; + + foreach ($identifiers as $identifier) { + $metadata = new PropertyMetadata(); + $metadata = $metadata->withIdentifier(true); + $propertyMetadataFactoryProphecy->create($resourceClass, $identifier)->willReturn($metadata); + + $nameCollection[] = $identifier; + } + + //random property to prevent the use of non-identifiers metadata while looping + $propertyMetadataFactoryProphecy->create($resourceClass, 'foobar')->willReturn(new PropertyMetadata()); + + $propertyNameCollectionFactoryProphecy->create($resourceClass)->willReturn(new PropertyNameCollection($nameCollection))->shouldBeCalled(); + + return [$propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal()]; + } + + private function getObjectManagerProphecy(array $output, string $resourceClass) + { + $classMetadataProphecy = $this->prophesize(ClassMetadata::class); + $classMetadataProphecy->getIdentifier()->willReturn($output)->shouldBeCalled(); + $managerProphecy = $this->prophesize(ObjectManager::class); + $managerProphecy->getClassMetadata($resourceClass)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal()); + + return $managerProphecy->reveal(); + } + + public function testSingleIdentifier() + { + $identifiers = ['id']; + list($propertyNameCollectionFactory, $propertyMetadataFactory) = $this->getMetadataProphecies($identifiers, Dummy::class); + $objectManager = $this->getObjectManagerProphecy($identifiers, Dummy::class); + + $identifierManager = new IdentifierManagerTraitImpl($propertyNameCollectionFactory, $propertyMetadataFactory); + + $this->assertEquals($identifierManager->normalizeIdentifiers(1, $objectManager, Dummy::class), ['id' => 1]); + } + + public function testCompositeIdentifier() + { + $identifiers = ['ida', 'idb']; + list($propertyNameCollectionFactory, $propertyMetadataFactory) = $this->getMetadataProphecies($identifiers, Dummy::class); + $objectManager = $this->getObjectManagerProphecy($identifiers, Dummy::class); + + $identifierManager = new IdentifierManagerTraitImpl($propertyNameCollectionFactory, $propertyMetadataFactory); + + $this->assertEquals($identifierManager->normalizeIdentifiers('ida=1;idb=2', $objectManager, Dummy::class), ['ida' => 1, 'idb' => 2]); + } + + /** + * @expectedException \ApiPlatform\Core\Exception\PropertyNotFoundException + * @expectedExceptionMessage Invalid identifier "idbad=1;idb=2", "ida" has not been found. + */ + public function testInvalidIdentifier() + { + $identifiers = ['ida', 'idb']; + list($propertyNameCollectionFactory, $propertyMetadataFactory) = $this->getMetadataProphecies($identifiers, Dummy::class); + $objectManager = $this->getObjectManagerProphecy($identifiers, Dummy::class); + + $identifierManager = new IdentifierManagerTraitImpl($propertyNameCollectionFactory, $propertyMetadataFactory); + + $identifierManager->normalizeIdentifiers('idbad=1;idb=2', $objectManager, Dummy::class); + } +} diff --git a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php index 737ccaaad8c..2450a5f695e 100644 --- a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php +++ b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php @@ -242,7 +242,6 @@ private function getContainerBuilderProphecy() 'api_platform.doctrine.orm.date_filter', 'api_platform.doctrine.orm.default.collection_data_provider', 'api_platform.doctrine.orm.default.item_data_provider', - 'api_platform.doctrine.orm.default.item_data_provider', 'api_platform.doctrine.orm.item_data_provider', 'api_platform.doctrine.orm.metadata.property.metadata_factory', 'api_platform.doctrine.orm.numeric_filter',