Completed
Pull Request — master (#578)
by Kévin
07:09 queued 03:03
created

ItemDataProvider::getItem()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 24
rs 8.5125
cc 5
eloc 13
nc 4
nop 4
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
namespace ApiPlatform\Core\Bridge\Doctrine\Orm;
13
14
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
15
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
16
use ApiPlatform\Core\Exception\InvalidArgumentException;
17
use ApiPlatform\Core\Exception\ResourceClassNotSupportedException;
18
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
19
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
20
use Doctrine\Common\Persistence\ManagerRegistry;
21
use Doctrine\ORM\EntityManagerInterface;
22
use Doctrine\ORM\QueryBuilder;
23
24
/**
25
 * Item data provider for the Doctrine ORM.
26
 *
27
 * @author Kévin Dunglas <[email protected]>
28
 * @author Samuel ROZE <[email protected]>
29
 */
30
class ItemDataProvider implements ItemDataProviderInterface
31
{
32
    private $managerRegistry;
33
    private $propertyNameCollectionFactory;
34
    private $propertyMetadataFactory;
35
    private $itemExtensions;
36
37
    /**
38
     * @param ManagerRegistry                        $managerRegistry
39
     * @param PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory
40
     * @param PropertyMetadataFactoryInterface       $propertyMetadataFactory
41
     * @param QueryItemExtensionInterface[]          $itemExtensions
42
     */
43
    public function __construct(ManagerRegistry $managerRegistry, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, array $itemExtensions = [])
44
    {
45
        $this->managerRegistry = $managerRegistry;
46
        $this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
47
        $this->propertyMetadataFactory = $propertyMetadataFactory;
48
        $this->itemExtensions = $itemExtensions;
49
    }
50
51
    /**
52
     * {@inheritdoc}
53
     */
54
    public function getItem(string $resourceClass, $id, string $operationName = null, bool $fetchData = false)
55
    {
56
        $manager = $this->managerRegistry->getManagerForClass($resourceClass);
57
        if (null === $manager) {
58
            throw new ResourceClassNotSupportedException();
59
        }
60
61
        $identifiers = $this->normalizeIdentifiers($id, $manager, $resourceClass);
62
63
        if (!$fetchData && $manager instanceof EntityManagerInterface) {
64
            return $manager->getReference($resourceClass, $identifiers);
65
        }
66
67
        $repository = $manager->getRepository($resourceClass);
68
        $queryBuilder = $repository->createQueryBuilder('o');
69
70
        $this->addWhereForIdentifiers($identifiers, $queryBuilder);
71
72
        foreach ($this->itemExtensions as $extension) {
73
            $extension->applyToItem($queryBuilder, $resourceClass, $identifiers, $operationName);
74
        }
75
76
        return $queryBuilder->getQuery()->getOneOrNullResult();
77
    }
78
79
    /**
80
     * Add WHERE conditions to the query for one or more identifiers (simple or composite).
81
     *
82
     * @param array        $identifiers
83
     * @param QueryBuilder $queryBuilder
84
     */
85
    private function addWhereForIdentifiers(array $identifiers, QueryBuilder $queryBuilder)
86
    {
87
        if (empty($identifiers)) {
88
            return;
89
        }
90
91
        foreach ($identifiers as $identifier => $value) {
92
            $placeholder = ':id_'.$identifier;
93
            $expression = $queryBuilder->expr()->eq(
94
                'o.'.$identifier,
95
                $placeholder
96
            );
97
98
            $queryBuilder->andWhere($expression);
99
100
            $queryBuilder->setParameter($placeholder, $value);
101
        }
102
    }
103
104
    /**
105
     * Transform and check the identifier, composite or not.
106
     *
107
     * @param $id
108
     * @param $manager
109
     * @param $resourceClass
110
     *
111
     * @return array
112
     */
113
    private function normalizeIdentifiers($id, $manager, $resourceClass) : array
114
    {
115
        $doctrineMetadataIdentifier = $manager->getClassMetadata($resourceClass)->getIdentifier();
116
        $identifierValues = [$id];
117
118
        if (count($doctrineMetadataIdentifier) >= 2) {
119
            $identifiers = explode(';', $id);
120
            $identifiersMap = [];
121
122
            // first transform identifiers to a proper key/value array
123
            foreach ($identifiers as $identifier) {
124
                $keyValue = explode('=', $identifier);
125
                $identifiersMap[$keyValue[0]] = $keyValue[1];
126
            }
127
128
            $identifierValuesArray = [];
129
130
            // then, loop through doctrine metadata identifiers to keep the same identifiers order
131
            foreach ($doctrineMetadataIdentifier as $metadataIdentifierKey) {
132
                $identifierValuesArray[] = $identifiersMap[$metadataIdentifierKey];
133
            }
134
135
            $identifierValues = $identifierValuesArray;
136
        }
137
138
        $identifiers = [];
139
        $i = 0;
140
141
        foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $propertyName) {
142
            $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName);
143
144
            $identifier = $propertyMetadata->isIdentifier();
145
            if (null === $identifier || false === $identifier) {
146
                continue;
147
            }
148
149
            if (!isset($identifierValues[$i])) {
150
                throw new InvalidArgumentException(sprintf('Invalid identifier "%s".', $id));
151
            }
152
153
            $identifiers[$propertyName] = $identifierValues[$i];
154
            ++$i;
155
        }
156
157
        return $identifiers;
158
    }
159
}
160