Passed
Pull Request — master (#1837)
by Amrouche
03:04
created

IriConverter::getIriFromPlainIdentifier()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 9.2
c 0
b 0
f 0
cc 4
eloc 13
nc 3
nop 3
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\Symfony\Routing;
15
16
use ApiPlatform\Core\Api\IdentifiersExtractor;
17
use ApiPlatform\Core\Api\IdentifiersExtractorInterface;
18
use ApiPlatform\Core\Api\IriConverterInterface;
19
use ApiPlatform\Core\Api\IriToIdentifierConverterInterface;
20
use ApiPlatform\Core\Api\OperationType;
21
use ApiPlatform\Core\Api\UrlGeneratorInterface;
22
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
23
use ApiPlatform\Core\DataProvider\OperationDataProviderTrait;
24
use ApiPlatform\Core\DataProvider\SubresourceDataProviderInterface;
25
use ApiPlatform\Core\Exception\InvalidArgumentException;
26
use ApiPlatform\Core\Exception\InvalidIdentifierException;
27
use ApiPlatform\Core\Exception\ItemNotFoundException;
28
use ApiPlatform\Core\Exception\RuntimeException;
29
use ApiPlatform\Core\Identifier\Normalizer\ChainIdentifierDenormalizer;
30
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
31
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
32
use ApiPlatform\Core\Util\AttributesExtractor;
33
use ApiPlatform\Core\Util\ClassInfoTrait;
34
use Symfony\Component\PropertyAccess\PropertyAccess;
35
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
36
use Symfony\Component\Routing\Exception\ExceptionInterface as RoutingExceptionInterface;
37
use Symfony\Component\Routing\RouterInterface;
38
39
/**
40
 * {@inheritdoc}
41
 *
42
 * @author Kévin Dunglas <[email protected]>
43
 */
44
final class IriConverter implements IriToIdentifierConverterInterface, IriConverterInterface
45
{
46
    use ClassInfoTrait;
47
    use OperationDataProviderTrait;
48
49
    private $routeNameResolver;
50
    private $router;
51
    private $identifiersExtractor;
52
53
    public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ItemDataProviderInterface $itemDataProvider, RouteNameResolverInterface $routeNameResolver, RouterInterface $router, PropertyAccessorInterface $propertyAccessor = null, IdentifiersExtractorInterface $identifiersExtractor = null, ChainIdentifierDenormalizer $identifierDenormalizer = null, SubresourceDataProviderInterface $subresourceDataProvider = null)
54
    {
55
        $this->itemDataProvider = $itemDataProvider;
56
        $this->routeNameResolver = $routeNameResolver;
57
        $this->router = $router;
58
        $this->identifiersExtractor = $identifiersExtractor;
59
        $this->identifierDenormalizer = $identifierDenormalizer;
60
        $this->subresourceDataProvider = $subresourceDataProvider;
61
62
        if (null === $identifiersExtractor) {
63
            @trigger_error(sprintf('Not injecting "%s" is deprecated since API Platform 2.3 and will not be possible anymore in API Platform 3.', IdentifiersExtractorInterface::class), E_USER_DEPRECATED);
64
            $this->identifiersExtractor = new IdentifiersExtractor($propertyNameCollectionFactory, $propertyMetadataFactory, $propertyAccessor ?? PropertyAccess::createPropertyAccessor());
65
        }
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71
    public function getItemFromIri(string $iri, array $context = [])
72
    {
73
        try {
74
            $parameters = $this->router->match($iri);
75
        } catch (RoutingExceptionInterface $e) {
76
            throw new InvalidArgumentException(sprintf('No route matches "%s".', $iri), $e->getCode(), $e);
77
        }
78
79
        if (!isset($parameters['_api_resource_class'])) {
80
            throw new InvalidArgumentException(sprintf('No resource associated to "%s".', $iri));
81
        }
82
83
        $attributes = AttributesExtractor::extractAttributes($parameters);
84
85
        try {
86
            $identifiers = $this->extractIdentifiers($parameters, $attributes);
87
        } catch (InvalidIdentifierException $e) {
88
            throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
89
        }
90
91
        if ($this->identifierDenormalizer) {
92
            $context[ChainIdentifierDenormalizer::HAS_IDENTIFIER_DENORMALIZER] = true;
93
        }
94
95
        if (isset($attributes['subresource_operation_name'])) {
96
            if ($item = $this->getSubresourceData($identifiers, $attributes, $context)) {
97
                return $item;
98
            }
99
100
            throw new ItemNotFoundException(sprintf('Item not found for "%s".', $iri));
101
        }
102
103
        if ($item = $this->getItemData($identifiers, $attributes, $context)) {
104
            return $item;
105
        }
106
107
        throw new ItemNotFoundException(sprintf('Item not found for "%s".', $iri));
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113
    public function getIriFromPlainIdentifier($id, string $resourceClass, int $referenceType = UrlGeneratorInterface::ABS_PATH): string
114
    {
115
        $routeName = $this->routeNameResolver->getRouteName($resourceClass, OperationType::ITEM);
116
        try {
117
            return $this->router->generate($routeName, ['id' => \is_array($id) ? implode(';', $id) : $id], $referenceType);
118
        } catch (RuntimeException $e) {
119
            throw new InvalidArgumentException(sprintf(
120
                'Unable to generate an IRI for the item of type "%s"',
121
                $resourceClass
122
            ), $e->getCode(), $e);
123
        } catch (RoutingExceptionInterface $e) {
124
            throw new InvalidArgumentException(sprintf(
125
                'Unable to generate an IRI for the item of type "%s"',
126
                $resourceClass
127
            ), $e->getCode(), $e);
128
        }
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134
    public function getIriFromItem($item, int $referenceType = UrlGeneratorInterface::ABS_PATH): string
135
    {
136
        $resourceClass = $this->getObjectClass($item);
137
        $routeName = $this->routeNameResolver->getRouteName($resourceClass, OperationType::ITEM);
138
139
        try {
140
            $identifiers = $this->generateIdentifiersUrl($this->identifiersExtractor->getIdentifiersFromItem($item));
0 ignored issues
show
Bug introduced by
The method getIdentifiersFromItem() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

140
            $identifiers = $this->generateIdentifiersUrl($this->identifiersExtractor->/** @scrutinizer ignore-call */ getIdentifiersFromItem($item));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
141
142
            return $this->router->generate($routeName, ['id' => implode(';', $identifiers)], $referenceType);
143
        } catch (RuntimeException $e) {
144
            throw new InvalidArgumentException(sprintf(
145
                'Unable to generate an IRI for the item of type "%s"',
146
                $resourceClass
147
            ), $e->getCode(), $e);
148
        } catch (RoutingExceptionInterface $e) {
149
            throw new InvalidArgumentException(sprintf(
150
                'Unable to generate an IRI for the item of type "%s"',
151
                $resourceClass
152
            ), $e->getCode(), $e);
153
        }
154
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159
    public function getIriFromResourceClass(string $resourceClass, int $referenceType = UrlGeneratorInterface::ABS_PATH): string
160
    {
161
        try {
162
            return $this->router->generate($this->routeNameResolver->getRouteName($resourceClass, OperationType::COLLECTION), [], $referenceType);
163
        } catch (RoutingExceptionInterface $e) {
164
            throw new InvalidArgumentException(sprintf('Unable to generate an IRI for "%s".', $resourceClass), $e->getCode(), $e);
165
        }
166
    }
167
168
    /**
169
     * {@inheritdoc}
170
     */
171
    public function getItemIriFromResourceClass(string $resourceClass, array $identifiers, int $referenceType = UrlGeneratorInterface::ABS_PATH): string
172
    {
173
        try {
174
            return $this->router->generate($this->routeNameResolver->getRouteName($resourceClass, OperationType::ITEM), $identifiers, $referenceType);
175
        } catch (RoutingExceptionInterface $e) {
176
            throw new InvalidArgumentException(sprintf('Unable to generate an IRI for "%s".', $resourceClass), $e->getCode(), $e);
177
        }
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183
    public function getSubresourceIriFromResourceClass(string $resourceClass, array $context, int $referenceType = UrlGeneratorInterface::ABS_PATH): string
184
    {
185
        try {
186
            return $this->router->generate($this->routeNameResolver->getRouteName($resourceClass, OperationType::SUBRESOURCE, $context), $context['subresource_identifiers'], $referenceType);
187
        } catch (RoutingExceptionInterface $e) {
188
            throw new InvalidArgumentException(sprintf('Unable to generate an IRI for "%s".', $resourceClass), $e->getCode(), $e);
189
        }
190
    }
191
192
    /**
193
     * Generate the identifier url.
194
     *
195
     * @param array $identifiers
196
     *
197
     * @return string[]
198
     */
199
    private function generateIdentifiersUrl(array $identifiers): array
200
    {
201
        if (1 === \count($identifiers)) {
202
            return [rawurlencode((string) array_values($identifiers)[0])];
203
        }
204
205
        foreach ($identifiers as $name => $value) {
206
            $identifiers[$name] = sprintf('%s=%s', $name, $value);
207
        }
208
209
        return $identifiers;
210
    }
211
}
212