Completed
Push — master ( c7e2cd...8a3b59 )
by Samuel
13s
created

OperationMethodResolver::getOperationMethod()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.2
c 0
b 0
f 0
cc 4
eloc 11
nc 6
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\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
        return $this->getRoute($routeName)->getMethods()[0] ?? 'GET';
100
    }
101
102
    /**
103
     * Gets the route related to the given operation.
104
     *
105
     * @param string $resourceClass
106
     * @param string $operationName
107
     * @param string $operationType
108
     *
109
     * @throws RuntimeException
110
     *
111
     * @return Route
112
     */
113
    private function getOperationRoute(string $resourceClass, string $operationName, string $operationType): Route
114
    {
115
        $routeName = $this->getRouteName($this->resourceMetadataFactory->create($resourceClass), $operationName, $operationType);
116
        if (null !== $routeName) {
117
            return $this->getRoute($routeName);
118
        }
119
120
        $operationNameKey = sprintf('_api_%s_operation_name', $operationType);
121
122
        foreach ($this->router->getRouteCollection()->all() as $routeName => $route) {
123
            $currentResourceClass = $route->getDefault('_api_resource_class');
124
            $currentOperationName = $route->getDefault($operationNameKey);
125
126
            if ($resourceClass === $currentResourceClass && $operationName === $currentOperationName) {
127
                return $route;
128
            }
129
        }
130
131
        throw new RuntimeException(sprintf('No route found for operation "%s" for type "%s".', $operationName, $resourceClass));
132
    }
133
134
    /**
135
     * Gets the route name or null if not defined.
136
     *
137
     * @param ResourceMetadata $resourceMetadata
138
     * @param string           $operationName
139
     * @param string           $operationType
140
     *
141
     * @return string|null
142
     */
143
    private function getRouteName(ResourceMetadata $resourceMetadata, string $operationName, string $operationType)
144
    {
145
        if (OperationType::ITEM === $operationType) {
146
            return $resourceMetadata->getItemOperationAttribute($operationName, 'route_name');
147
        }
148
149
        return $resourceMetadata->getCollectionOperationAttribute($operationName, 'route_name');
150
    }
151
152
    /**
153
     * Gets the route with the given name.
154
     *
155
     * @param string $routeName
156
     *
157
     * @throws RuntimeException
158
     *
159
     * @return Route
160
     */
161
    private function getRoute(string $routeName): Route
162
    {
163
        foreach ($this->router->getRouteCollection() as $name => $route) {
164
            if ($routeName === $name) {
165
                return $route;
166
            }
167
        }
168
169
        throw new RuntimeException(sprintf('The route "%s" does not exist.', $routeName));
170
    }
171
}
172