1 | <?php |
||||
2 | |||||
3 | /* |
||||
4 | * This file is part of the API Platform project. |
||||
5 | * |
||||
6 | * (c) Kévin Dunglas <[email protected]> |
||||
7 | * |
||||
8 | * For the full copyright and license information, please view the LICENSE |
||||
9 | * file that was distributed with this source code. |
||||
10 | */ |
||||
11 | |||||
12 | declare(strict_types=1); |
||||
13 | |||||
14 | namespace ApiPlatform\Core\Bridge\Doctrine\Orm; |
||||
15 | |||||
16 | use ApiPlatform\Core\Bridge\Doctrine\Common\Util\IdentifierManagerTrait; |
||||
17 | use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface; |
||||
18 | use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryResultItemExtensionInterface; |
||||
19 | use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGenerator; |
||||
20 | use ApiPlatform\Core\DataProvider\DenormalizedIdentifiersAwareItemDataProviderInterface; |
||||
21 | use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface; |
||||
22 | use ApiPlatform\Core\Exception\RuntimeException; |
||||
23 | use ApiPlatform\Core\Identifier\IdentifierConverterInterface; |
||||
24 | use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface; |
||||
25 | use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface; |
||||
26 | use Doctrine\Common\Persistence\ManagerRegistry; |
||||
27 | use Doctrine\Common\Persistence\Mapping\ClassMetadata; |
||||
28 | use Doctrine\ORM\EntityManagerInterface; |
||||
29 | use Doctrine\ORM\QueryBuilder; |
||||
30 | |||||
31 | /** |
||||
32 | * Item data provider for the Doctrine ORM. |
||||
33 | * |
||||
34 | * @author Kévin Dunglas <[email protected]> |
||||
35 | * @author Samuel ROZE <[email protected]> |
||||
36 | * @final |
||||
37 | */ |
||||
38 | class ItemDataProvider implements DenormalizedIdentifiersAwareItemDataProviderInterface, RestrictedDataProviderInterface |
||||
39 | { |
||||
40 | use IdentifierManagerTrait; |
||||
41 | |||||
42 | private $managerRegistry; |
||||
43 | private $itemExtensions; |
||||
44 | |||||
45 | /** |
||||
46 | * @param QueryItemExtensionInterface[] $itemExtensions |
||||
47 | */ |
||||
48 | public function __construct(ManagerRegistry $managerRegistry, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, iterable $itemExtensions = []) |
||||
49 | { |
||||
50 | $this->managerRegistry = $managerRegistry; |
||||
51 | $this->propertyNameCollectionFactory = $propertyNameCollectionFactory; |
||||
52 | $this->propertyMetadataFactory = $propertyMetadataFactory; |
||||
53 | $this->itemExtensions = $itemExtensions; |
||||
54 | } |
||||
55 | |||||
56 | public function supports(string $resourceClass, string $operationName = null, array $context = []): bool |
||||
57 | { |
||||
58 | return $this->managerRegistry->getManagerForClass($resourceClass) instanceof EntityManagerInterface; |
||||
59 | } |
||||
60 | |||||
61 | /** |
||||
62 | * {@inheritdoc} |
||||
63 | * |
||||
64 | * The context may contain a `fetch_data` key representing whether the value should be fetched by Doctrine or if we should return a reference. |
||||
65 | * |
||||
66 | * @throws RuntimeException |
||||
67 | */ |
||||
68 | public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []) |
||||
69 | { |
||||
70 | /** @var EntityManagerInterface $manager */ |
||||
71 | $manager = $this->managerRegistry->getManagerForClass($resourceClass); |
||||
72 | |||||
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( |
||||
78 | '$id must be array when "%s" key is set to true in the $context', |
||||
79 | IdentifierConverterInterface::HAS_IDENTIFIER_CONVERTER |
||||
80 | )); |
||||
81 | } |
||||
82 | $identifiers = $id; |
||||
83 | |||||
84 | $fetchData = $context['fetch_data'] ?? true; |
||||
85 | if (!$fetchData) { |
||||
86 | return $manager->getReference($resourceClass, $identifiers); |
||||
87 | } |
||||
88 | |||||
89 | $repository = $manager->getRepository($resourceClass); |
||||
90 | if (!method_exists($repository, 'createQueryBuilder')) { |
||||
91 | throw new RuntimeException('The repository class must have a "createQueryBuilder" method.'); |
||||
92 | } |
||||
93 | |||||
94 | $queryBuilder = $repository->createQueryBuilder('o'); |
||||
95 | $queryNameGenerator = new QueryNameGenerator(); |
||||
96 | $doctrineClassMetadata = $manager->getClassMetadata($resourceClass); |
||||
97 | |||||
98 | $this->addWhereForIdentifiers($identifiers, $queryBuilder, $doctrineClassMetadata); |
||||
99 | |||||
100 | foreach ($this->itemExtensions as $extension) { |
||||
101 | $extension->applyToItem($queryBuilder, $queryNameGenerator, $resourceClass, $identifiers, $operationName, $context); |
||||
102 | |||||
103 | if ($extension instanceof QueryResultItemExtensionInterface && $extension->supportsResult($resourceClass, $operationName, $context)) { |
||||
0 ignored issues
–
show
|
|||||
104 | return $extension->getResult($queryBuilder, $resourceClass, $operationName, $context); |
||||
0 ignored issues
–
show
The call to
ApiPlatform\Core\Bridge\...nInterface::getResult() has too many arguments starting with $resourceClass .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.
Loading history...
|
|||||
105 | } |
||||
106 | } |
||||
107 | |||||
108 | return $queryBuilder->getQuery()->getOneOrNullResult(); |
||||
109 | } |
||||
110 | |||||
111 | /** |
||||
112 | * Add WHERE conditions to the query for one or more identifiers (simple or composite). |
||||
113 | */ |
||||
114 | private function addWhereForIdentifiers(array $identifiers, QueryBuilder $queryBuilder, ClassMetadata $classMetadata) |
||||
115 | { |
||||
116 | $alias = $queryBuilder->getRootAliases()[0]; |
||||
117 | foreach ($identifiers as $identifier => $value) { |
||||
118 | $placeholder = ':id_'.$identifier; |
||||
119 | $expression = $queryBuilder->expr()->eq( |
||||
120 | "{$alias}.{$identifier}", |
||||
121 | $placeholder |
||||
122 | ); |
||||
123 | |||||
124 | $queryBuilder->andWhere($expression); |
||||
125 | |||||
126 | $queryBuilder->setParameter($placeholder, $value, $classMetadata->getTypeOfField($identifier)); |
||||
127 | } |
||||
128 | } |
||||
129 | } |
||||
130 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.