Completed
Pull Request — master (#1572)
by Desjardins
05:48 queued 02:23
created

getCollectionOperationRoute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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