vendor/api-platform/core/src/Core/Bridge/Doctrine/Orm/ItemDataProvider.php line 80

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of the API Platform project.
  4. *
  5. * (c) Kévin Dunglas <dunglas@gmail.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. declare(strict_types=1);
  11. namespace ApiPlatform\Core\Bridge\Doctrine\Orm;
  12. use ApiPlatform\Core\Bridge\Doctrine\Common\Util\IdentifierManagerTrait;
  13. use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface as LegacyQueryItemExtensionInterface;
  14. use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryResultItemExtensionInterface as LegacyQueryResultItemExtensionInterface;
  15. use ApiPlatform\Core\DataProvider\DenormalizedIdentifiersAwareItemDataProviderInterface;
  16. use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
  17. use ApiPlatform\Core\Identifier\IdentifierConverterInterface;
  18. use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
  19. use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
  20. use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
  21. use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
  22. use ApiPlatform\Doctrine\Orm\Extension\QueryResultItemExtensionInterface;
  23. use ApiPlatform\Doctrine\Orm\Util\QueryNameGenerator;
  24. use ApiPlatform\Exception\RuntimeException;
  25. use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
  26. use Doctrine\ORM\EntityManagerInterface;
  27. use Doctrine\ORM\QueryBuilder;
  28. use Doctrine\Persistence\ManagerRegistry;
  29. use Doctrine\Persistence\Mapping\ClassMetadata;
  30. /**
  31. * Item data provider for the Doctrine ORM.
  32. *
  33. * @author Kévin Dunglas <dunglas@gmail.com>
  34. * @author Samuel ROZE <samuel.roze@gmail.com>
  35. *
  36. * @final
  37. */
  38. class ItemDataProvider implements DenormalizedIdentifiersAwareItemDataProviderInterface, RestrictedDataProviderInterface
  39. {
  40. use IdentifierManagerTrait;
  41. private $managerRegistry;
  42. private $itemExtensions;
  43. /**
  44. * @param LegacyQueryItemExtensionInterface[]|QueryItemExtensionInterface[] $itemExtensions
  45. * @param ResourceMetadataCollectionFactoryInterface|null $resourceMetadataFactory
  46. */
  47. public function __construct(ManagerRegistry $managerRegistry, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, iterable $itemExtensions = [], $resourceMetadataFactory = null)
  48. {
  49. $this->managerRegistry = $managerRegistry;
  50. $this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
  51. $this->propertyMetadataFactory = $propertyMetadataFactory;
  52. if (!$resourceMetadataFactory instanceof ResourceMetadataCollectionFactoryInterface) {
  53. trigger_deprecation('api-platform/core', '2.7', sprintf('Use "%s" instead of "%s".', ResourceMetadataCollectionFactoryInterface::class, ResourceMetadataFactoryInterface::class));
  54. }
  55. $this->resourceMetadataFactory = $resourceMetadataFactory;
  56. $this->itemExtensions = $itemExtensions;
  57. }
  58. public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
  59. {
  60. return $this->managerRegistry->getManagerForClass($resourceClass) instanceof EntityManagerInterface;
  61. }
  62. /**
  63. * {@inheritdoc}
  64. *
  65. * The context may contain a `fetch_data` key representing whether the value should be fetched by Doctrine or if we should return a reference.
  66. *
  67. * @throws RuntimeException
  68. */
  69. public function getItem(string $resourceClass, $id, string $operationName = null, array $context = [])
  70. {
  71. /** @var EntityManagerInterface $manager */
  72. $manager = $this->managerRegistry->getManagerForClass($resourceClass);
  73. if ((\is_int($id) || \is_string($id)) && !($context[IdentifierConverterInterface::HAS_IDENTIFIER_CONVERTER] ?? false)) {
  74. $id = $this->normalizeIdentifiers($id, $manager, $resourceClass);
  75. }
  76. if (!\is_array($id)) {
  77. throw new \InvalidArgumentException(sprintf('$id must be array when "%s" key is set to true in the $context', IdentifierConverterInterface::HAS_IDENTIFIER_CONVERTER));
  78. }
  79. $identifiers = $id;
  80. $fetchData = $context['fetch_data'] ?? true;
  81. if (!$fetchData) {
  82. return $manager->getReference($resourceClass, $identifiers);
  83. }
  84. $repository = $manager->getRepository($resourceClass);
  85. if (!method_exists($repository, 'createQueryBuilder')) {
  86. throw new RuntimeException('The repository class must have a "createQueryBuilder" method.');
  87. }
  88. $queryBuilder = $repository->createQueryBuilder('o');
  89. $queryNameGenerator = new QueryNameGenerator();
  90. $doctrineClassMetadata = $manager->getClassMetadata($resourceClass);
  91. $this->addWhereForIdentifiers($identifiers, $queryBuilder, $doctrineClassMetadata, $queryNameGenerator);
  92. foreach ($this->itemExtensions as $extension) {
  93. if ($extension instanceof LegacyQueryItemExtensionInterface) {
  94. $extension->applyToItem($queryBuilder, $queryNameGenerator, $resourceClass, $identifiers, $operationName, $context);
  95. }
  96. if ($extension instanceof QueryItemExtensionInterface) {
  97. $extension->applyToItem($queryBuilder, $queryNameGenerator, $resourceClass, $identifiers, $context['operation'] ?? null, $context);
  98. }
  99. if ($extension instanceof LegacyQueryResultItemExtensionInterface && $extension->supportsResult($resourceClass, $operationName, $context)) { // @phpstan-ignore-line because of context
  100. return $extension->getResult($queryBuilder, $resourceClass, $operationName, $context); // @phpstan-ignore-line because of context
  101. }
  102. if ($extension instanceof QueryResultItemExtensionInterface && $extension->supportsResult($resourceClass, $context['operation'] ?? null, $context)) {
  103. return $extension->getResult($queryBuilder, $resourceClass, $context['operation'] ?? null, $context);
  104. }
  105. }
  106. return $queryBuilder->getQuery()->getOneOrNullResult();
  107. }
  108. /**
  109. * Add WHERE conditions to the query for one or more identifiers (simple or composite).
  110. */
  111. private function addWhereForIdentifiers(array $identifiers, QueryBuilder $queryBuilder, ClassMetadata $classMetadata, $queryNameGenerator)
  112. {
  113. $alias = $queryBuilder->getRootAliases()[0];
  114. foreach ($identifiers as $identifier => $value) {
  115. $placeholder = $queryNameGenerator->generateParameterName($identifier);
  116. $expression = $queryBuilder->expr()->eq(
  117. "{$alias}.{$identifier}",
  118. ':'.$placeholder
  119. );
  120. $queryBuilder->andWhere($expression);
  121. $queryBuilder->setParameter($placeholder, $value, $classMetadata->getTypeOfField($identifier));
  122. }
  123. }
  124. }