Completed
Push — master ( e60025...29ef10 )
by Kévin
04:15
created

OperationMethodResolver::getOperationRoute()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 8.8571
c 0
b 0
f 0
cc 6
eloc 11
nc 4
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
namespace ApiPlatform\Core\Bridge\Symfony\Routing;
13
14
use ApiPlatform\Core\Exception\RuntimeException;
15
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
16
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
17
use Symfony\Component\Routing\Route;
18
use Symfony\Component\Routing\RouterInterface;
19
20
/**
21
 * Resolves the HTTP method associated with an operation, extended for Symfony routing.
22
 *
23
 * @author Kévin Dunglas <[email protected]>
24
 * @author Teoh Han Hui <[email protected]>
25
 */
26
final class OperationMethodResolver implements OperationMethodResolverInterface
27
{
28
    private $router;
29
    private $resourceMetadataFactory;
30
31
    public function __construct(RouterInterface $router, ResourceMetadataFactoryInterface $resourceMetadataFactory)
32
    {
33
        $this->router = $router;
34
        $this->resourceMetadataFactory = $resourceMetadataFactory;
35
    }
36
37
    /**
38
     * {@inheritdoc}
39
     */
40
    public function getCollectionOperationMethod(string $resourceClass, string $operationName) : string
41
    {
42
        return $this->getOperationMethod($resourceClass, $operationName, true);
43
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48
    public function getItemOperationMethod(string $resourceClass, string $operationName) : string
49
    {
50
        return $this->getOperationMethod($resourceClass, $operationName, false);
51
    }
52
53
    /**
54
     * {@inheritdoc}
55
     */
56
    public function getCollectionOperationRoute(string $resourceClass, string $operationName) : Route
57
    {
58
        return $this->getOperationRoute($resourceClass, $operationName, true);
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64
    public function getItemOperationRoute(string $resourceClass, string $operationName) : Route
65
    {
66
        return $this->getOperationRoute($resourceClass, $operationName, false);
67
    }
68
69
    /**
70
     * @param string $resourceClass
71
     * @param string $operationName
72
     * @param bool   $collection
73
     *
74
     * @throws RuntimeException
75
     *
76
     * @return string
77
     */
78
    private function getOperationMethod(string $resourceClass, string $operationName, bool $collection) : string
79
    {
80
        $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
81
82
        if ($collection) {
83
            $method = $resourceMetadata->getCollectionOperationAttribute($operationName, 'method');
84
        } else {
85
            $method = $resourceMetadata->getItemOperationAttribute($operationName, 'method');
86
        }
87
88
        if (null !== $method) {
89
            return $method;
90
        }
91
92
        if (null === $routeName = $this->getRouteName($resourceMetadata, $operationName, $collection)) {
93
            throw new RuntimeException(sprintf('Either a "route_name" or a "method" operation attribute must exist for the operation "%s" of the resource "%s".', $operationName, $resourceClass));
94
        }
95
96
        $route = $this->getRoute($routeName);
97
        $methods = $route->getMethods();
98
99
        if (empty($methods)) {
100
            return 'GET';
101
        }
102
103
        return $methods[0];
104
    }
105
106
    /**
107
     * Gets the route related to the given operation.
108
     *
109
     * @param string $resourceClass
110
     * @param string $operationName
111
     * @param bool   $collection
112
     *
113
     * @throws RuntimeException
114
     *
115
     * @return Route
116
     */
117
    private function getOperationRoute(string $resourceClass, string $operationName, bool $collection) : Route
118
    {
119
        $routeName = $this->getRouteName($this->resourceMetadataFactory->create($resourceClass), $operationName, $collection);
120
        if (null !== $routeName) {
121
            return $this->getRoute($routeName);
122
        }
123
124
        $operationNameKey = sprintf('_api_%s_operation_name', $collection ? 'collection' : 'item');
125
126
        foreach ($this->router->getRouteCollection()->all() as $routeName => $route) {
127
            $currentResourceClass = $route->getDefault('_api_resource_class');
128
            $currentOperationName = $route->getDefault($operationNameKey);
129
130
            if ($resourceClass === $currentResourceClass && $operationName === $currentOperationName) {
131
                return $route;
132
            }
133
        }
134
135
        throw new RuntimeException(sprintf('No route found for operation "%s" for type "%s".', $operationName, $resourceClass));
136
    }
137
138
    /**
139
     * Gets the route name or null if not defined.
140
     *
141
     * @param ResourceMetadata $resourceMetadata
142
     * @param string           $operationName
143
     * @param bool             $collection
144
     *
145
     * @return string|null
146
     */
147
    private function getRouteName(ResourceMetadata $resourceMetadata, string $operationName, bool $collection)
148
    {
149
        if ($collection) {
150
            return $resourceMetadata->getCollectionOperationAttribute($operationName, 'route_name');
151
        }
152
153
        return $resourceMetadata->getItemOperationAttribute($operationName, 'route_name');
154
    }
155
156
    /**
157
     * Gets the route with the given name.
158
     *
159
     * @param string $routeName
160
     *
161
     * @throws RuntimeException
162
     *
163
     * @return Route
164
     */
165
    private function getRoute(string $routeName) : Route
166
    {
167
        foreach ($this->router->getRouteCollection() as $name => $route) {
168
            if ($routeName === $name) {
169
                return $route;
170
            }
171
        }
172
173
        throw new RuntimeException(sprintf('The route "%s" does not exist.', $routeName));
174
    }
175
}
176