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

  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 DenormalizedIdentifiersAwareItemDataProviderInterfaceRestrictedDataProviderInterface
  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 $managerRegistryPropertyNameCollectionFactoryInterface $propertyNameCollectionFactoryPropertyMetadataFactoryInterface $propertyMetadataFactoryiterable $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 $resourceClassstring $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$idstring $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 $identifiersQueryBuilder $queryBuilderClassMetadata $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. }