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

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.      * @param mixed $queryNameGenerator
  112.      */
  113.     private function addWhereForIdentifiers(array $identifiers, QueryBuilder $queryBuilder, ClassMetadata $classMetadata, $queryNameGenerator)
  114.     {
  115.         $alias = $queryBuilder->getRootAliases()[0];
  116.         foreach ($identifiers as $identifier => $value) {
  117.             $placeholder = $queryNameGenerator->generateParameterName($identifier);
  118.             $expression = $queryBuilder->expr()->eq(
  119.                 "{$alias}.{$identifier}",
  120.                 ':'.$placeholder
  121.             );
  122.             $queryBuilder->andWhere($expression);
  123.             $queryBuilder->setParameter($placeholder, $value, $classMetadata->getTypeOfField($identifier));
  124.         }
  125.     }
  126. }