Completed
Pull Request — master (#721)
by Han Hui
03:12
created

IriConverter::getRouteName()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 8.2222
c 0
b 0
f 0
cc 7
eloc 9
nc 6
nop 2
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\Symfony\Routing;
13
14
use ApiPlatform\Core\Api\IriConverterInterface;
15
use ApiPlatform\Core\Api\UrlGeneratorInterface;
16
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
17
use ApiPlatform\Core\Exception\InvalidArgumentException;
18
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
19
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
20
use ApiPlatform\Core\Util\ClassInfoTrait;
21
use Symfony\Component\PropertyAccess\PropertyAccess;
22
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
23
use Symfony\Component\Routing\Exception\ExceptionInterface as RoutingExceptionInterface;
24
use Symfony\Component\Routing\RouterInterface;
25
26
/**
27
 * {@inheritdoc}
28
 *
29
 * @author Kévin Dunglas <[email protected]>
30
 */
31
final class IriConverter implements IriConverterInterface
32
{
33
    use ClassInfoTrait;
34
35
    private $propertyNameCollectionFactory;
36
    private $propertyMetadataFactory;
37
    private $itemDataProvider;
38
    private $routeNameResolver;
39
    private $router;
40
    private $propertyAccessor;
41
42
    public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ItemDataProviderInterface $itemDataProvider, RouteNameResolverInterface $routeNameResolver, RouterInterface $router, PropertyAccessorInterface $propertyAccessor = null)
43
    {
44
        $this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
45
        $this->propertyMetadataFactory = $propertyMetadataFactory;
46
        $this->itemDataProvider = $itemDataProvider;
47
        $this->routeNameResolver = $routeNameResolver;
48
        $this->router = $router;
49
        $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
50
    }
51
52
    /**
53
     * {@inheritdoc}
54
     */
55
    public function getItemFromIri(string $iri, bool $fetchData = false)
56
    {
57
        try {
58
            $parameters = $this->router->match($iri);
59
        } catch (RoutingExceptionInterface $exception) {
60
            throw new InvalidArgumentException(sprintf('No route matches "%s".', $iri), $exception->getCode(), $exception);
61
        }
62
63
        if (!isset($parameters['_api_resource_class']) || !isset($parameters['id'])) {
64
            throw new InvalidArgumentException(sprintf('No resource associated to "%s".', $iri));
65
        }
66
67
        if ($item = $this->itemDataProvider->getItem($parameters['_api_resource_class'], $parameters['id'], null, $fetchData)) {
68
            return $item;
69
        }
70
71
        throw new InvalidArgumentException(sprintf('Item not found for "%s".', $iri));
72
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77
    public function getIriFromItem($item, int $referenceType = UrlGeneratorInterface::ABS_PATH) : string
78
    {
79
        $resourceClass = $this->getObjectClass($item);
80
        $routeName = $this->routeNameResolver->getRouteName($resourceClass, false);
81
82
        $identifiers = $this->generateIdentifiersUrl($this->getIdentifiersFromItem($item));
83
84
        return $this->router->generate($routeName, ['id' => implode(';', $identifiers)], $referenceType);
85
    }
86
87
    /**
88
     * {@inheritdoc}
89
     */
90
    public function getIriFromResourceClass(string $resourceClass, int $referenceType = UrlGeneratorInterface::ABS_PATH) : string
91
    {
92
        try {
93
            return $this->router->generate($this->routeNameResolver->getRouteName($resourceClass, true), [], $referenceType);
94
        } catch (RoutingExceptionInterface $e) {
95
            throw new InvalidArgumentException(sprintf('Unable to generate an IRI for "%s".', $resourceClass), $e->getCode(), $e);
96
        }
97
    }
98
99
    /**
100
     * Find identifiers from an Item (Object).
101
     *
102
     * @param object $item
103
     *
104
     * @throws RuntimeException
105
     *
106
     * @return array
107
     */
108
    private function getIdentifiersFromItem($item) : array
109
    {
110
        $identifiers = [];
111
        $resourceClass = $this->getObjectClass($item);
112
113
        foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $propertyName) {
114
            $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName);
115
116
            $identifier = $propertyMetadata->isIdentifier();
117
            if (null === $identifier || false === $identifier) {
118
                continue;
119
            }
120
121
            $identifiers[$propertyName] = $this->propertyAccessor->getValue($item, $propertyName);
122
123
            if (!is_object($identifiers[$propertyName])) {
124
                continue;
125
            }
126
127
            $relatedResourceClass = $this->getObjectClass($identifiers[$propertyName]);
128
            $relatedItem = $identifiers[$propertyName];
129
130
            unset($identifiers[$propertyName]);
131
132
            foreach ($this->propertyNameCollectionFactory->create($relatedResourceClass) as $relatedPropertyName) {
133
                $propertyMetadata = $this->propertyMetadataFactory->create($relatedResourceClass, $relatedPropertyName);
134
135
                if ($propertyMetadata->isIdentifier()) {
136
                    $identifiers[$propertyName] = $this->propertyAccessor->getValue($relatedItem, $relatedPropertyName);
137
                }
138
            }
139
140
            if (empty($identifiers[$propertyName])) {
141
                throw new \RuntimeException(sprintf('%s identifiers cannot be found', $resourceClass));
142
            }
143
        }
144
145
        return $identifiers;
146
    }
147
148
    /**
149
     * Generate the identifier url.
150
     *
151
     * @param array $identifiers
152
     *
153
     * @return string[]
154
     */
155
    private function generateIdentifiersUrl(array $identifiers) : array
156
    {
157
        if (1 === count($identifiers)) {
158
            return [rawurlencode(array_values($identifiers)[0])];
159
        }
160
161
        foreach ($identifiers as $name => $value) {
162
            $identifiers[$name] = sprintf('%s=%s', $name, $value);
163
        }
164
165
        return $identifiers;
166
    }
167
}
168