Completed
Push — master ( faed70...56c59d )
by Philip
13:07
created

RestResourceLoader::getController()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4.5923

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 6
cts 9
cp 0.6667
rs 9.2
c 0
b 0
f 0
cc 4
eloc 9
nc 6
nop 1
crap 4.5923
1
<?php
2
3
namespace Dontdrinkandroot\RestBundle\Routing;
4
5
use Dontdrinkandroot\RestBundle\Metadata\Annotation\Method;
6
use Dontdrinkandroot\RestBundle\Metadata\ClassMetadata;
7
use Dontdrinkandroot\RestBundle\Metadata\PropertyMetadata;
8
use Metadata\MetadataFactoryInterface;
9
use Symfony\Component\Config\FileLocatorInterface;
10
use Symfony\Component\Config\Loader\Loader;
11
use Symfony\Component\Finder\Finder;
12
use Symfony\Component\Finder\SplFileInfo;
13
use Symfony\Component\HttpFoundation\Request;
14
use Symfony\Component\Routing\Route;
15
use Symfony\Component\Routing\RouteCollection;
16
17
/**
18
 * @author Philip Washington Sorst <[email protected]>
19
 */
20
class RestResourceLoader extends Loader
21
{
22
    /**
23
     * @var FileLocatorInterface
24
     */
25
    private $fileLocator;
26
27
    /**
28
     * @var MetadataFactoryInterface
29
     */
30
    private $metadataFactory;
31
32
    /**
33
     * @var bool
34
     */
35
    private $securityEnabled;
36
37 14
    public function __construct(
38
        FileLocatorInterface $fileLocator,
39
        MetadataFactoryInterface $metadataFactory,
40
        bool $securityEnabled
41
    ) {
42 14
        $this->fileLocator = $fileLocator;
43 14
        $this->metadataFactory = $metadataFactory;
44 14
        $this->securityEnabled = $securityEnabled;
45 14
    }
46
47
    /**
48
     * {@inheritdoc}
49
     */
50 14
    public function load($resource, $type = null)
51
    {
52 14
        $locatedResource = $this->fileLocator->locate($resource);
53 14
        $files = [];
54 14
        if (is_dir($locatedResource)) {
55 12
            $finder = new Finder();
56 12
            foreach ($finder->in($locatedResource)->name('*.php')->files() as $file) {
57
                /** @var SplFileInfo $file */
58 12
                $files[] = $file->getRealPath();
59
            }
60
        } else {
61 2
            $files[] = $locatedResource;
62
        }
63
64 14
        $routes = new RouteCollection();
65
66 14
        if ($this->securityEnabled) {
67 14
            $this->loadAccessTokenController($routes);
68
        }
69
70 14
        foreach ($files as $file) {
71 14
            $class = $this->findClass($file);
72 14
            if (false === $class) {
73
                throw new \Exception(sprintf('Couldn\'t find class for %s', $file));
74
            }
75
            /** @var ClassMetadata $classMetadata */
76 14
            $classMetadata = $this->metadataFactory->getMetadataForClass($class);
77 14
            if ($classMetadata->isRestResource()) {
78
79 14
                $namePrefix = $classMetadata->getNamePrefix();
80 14
                $pathPrefix = $classMetadata->getPathPrefix();
81 14
                $controller = $this->getController($classMetadata);
82
83
                $defaults = [
84 14
                    '_entityClass' => $class,
85 14
                    '_format'      => 'json'
86
                ];
87
88 14
                if (null !== $method = $classMetadata->getMethod(Method::LIST)) {
89 14
                    $listRoute = new Route($pathPrefix);
90 14
                    $listRoute->setMethods(Request::METHOD_GET);
91 14
                    $listRoute->setDefaults(
92 14
                        array_merge(
93 14
                            $defaults,
94
                            [
95 14
                                '_controller'      => $controller . ':listAction',
96 14
                                '_defaultincludes' => $method->defaultIncludes
97
                            ]
98
                        )
99
                    );
100 14
                    $routes->add($namePrefix . '.list', $listRoute);
101
                }
102
103 14
                if (null !== $method = $classMetadata->getMethod(Method::POST)) {
104 12
                    $postRoute = new Route($pathPrefix);
105 12
                    $postRoute->setMethods(Request::METHOD_POST);
106 12
                    $postRoute->setDefaults(
107 12
                        array_merge(
108 12
                            $defaults,
109
                            [
110 12
                                '_controller'      => $controller . ':postAction',
111 12
                                '_defaultincludes' => $method->defaultIncludes
112
                            ]
113
                        )
114
                    );
115 12
                    $routes->add($namePrefix . '.post', $postRoute);
116
                }
117
118 14
                if (null !== $method = $classMetadata->getMethod(Method::GET)) {
119 14
                    $getRoute = new Route($pathPrefix . '/{id}');
120 14
                    $getRoute->setMethods(Request::METHOD_GET);
121 14
                    $getRoute->setDefaults(
122 14
                        array_merge(
123 14
                            $defaults,
124
                            [
125 14
                                '_controller'      => $controller . ':getAction',
126 14
                                '_defaultincludes' => $method->defaultIncludes
127
                            ]
128
                        )
129
                    );
130 14
                    $routes->add($namePrefix . '.get', $getRoute);
131
                }
132
133 14
                if (null !== $method = $classMetadata->getMethod(Method::PUT)) {
134 12
                    $putRoute = new Route($pathPrefix . '/{id}');
135 12
                    $putRoute->setMethods([Request::METHOD_PUT, Request::METHOD_PATCH]);
136 12
                    $putRoute->setDefaults(
137 12
                        array_merge(
138 12
                            $defaults,
139
                            [
140 12
                                '_controller'      => $controller . ':putAction',
141 12
                                '_defaultincludes' => $method->defaultIncludes
142
                            ]
143
                        )
144
                    );
145 12
                    $routes->add($namePrefix . '.put', $putRoute);
146
                }
147
148 14
                if (null !== $method = $classMetadata->getMethod(Method::DELETE)) {
149 12
                    $deleteRoute = new Route($pathPrefix . '/{id}');
150 12
                    $deleteRoute->setMethods(Request::METHOD_DELETE);
151 12
                    $deleteRoute->setDefaults(
152 12
                        array_merge(
153 12
                            $defaults,
154
                            [
155 12
                                '_controller'      => $controller . ':deleteAction',
156 12
                                '_defaultincludes' => $method->defaultIncludes
157
                            ]
158
                        )
159
                    );
160 12
                    $routes->add($namePrefix . '.delete', $deleteRoute);
161
                }
162
163
                /** @var PropertyMetadata $propertyMetadata */
164 14
                foreach ($classMetadata->propertyMetadata as $propertyMetadata) {
165 14
                    if ($propertyMetadata->isSubResource()) {
166
167 12
                        $subResourcePath = strtolower($propertyMetadata->name);
168 12
                        if (null !== $propertyMetadata->getSubResourcePath()) {
169
                            $subResourcePath = $propertyMetadata->getSubResourcePath();
170
                        }
171
172 12
                        if (null !== $method = $propertyMetadata->getMethod(Method::LIST)) {
173 12
                            $subResourceFullPath = $pathPrefix . '/{id}/' . $subResourcePath;
174 12
                            $subResourceRoute = new Route($subResourceFullPath);
175 12
                            $subResourceRoute->setMethods(Request::METHOD_GET);
176 12
                            $subResourceRoute->setDefaults(
177 12
                                array_merge(
178 12
                                    $defaults,
179
                                    [
180 12
                                        '_controller'      => $controller . ':listSubresourceAction',
181 12
                                        'subresource'      => $propertyMetadata->name,
182 12
                                        '_defaultincludes' => $method->defaultIncludes
183
                                    ]
184
                                )
185
                            );
186 12
                            $routes->add($namePrefix . '.' . $propertyMetadata->name . '.list', $subResourceRoute);
187
                        }
188
189 12
                        if (null !== $method = $propertyMetadata->getMethod(Method::POST)) {
190 12
                            $subResourceFullPath = $pathPrefix . '/{id}/' . $subResourcePath;
191 12
                            $subResourceRoute = new Route($subResourceFullPath);
192 12
                            $subResourceRoute->setMethods(Request::METHOD_POST);
193 12
                            $subResourceRoute->setDefaults(
194 12
                                array_merge(
195 12
                                    $defaults,
196
                                    [
197 12
                                        '_controller'      => $controller . ':postSubresourceAction',
198 12
                                        'subresource'      => $propertyMetadata->name,
199 12
                                        '_defaultincludes' => $method->defaultIncludes
200
                                    ]
201
                                )
202
                            );
203 12
                            $routes->add($namePrefix . '.' . $propertyMetadata->name . '.post', $subResourceRoute);
204
                        }
205
206 12
                        if (null !== $method = $propertyMetadata->getMethod(Method::PUT)) {
207 12
                            $subResourceFullPath = $pathPrefix . '/{id}/' . $subResourcePath . '/{subId}';
208 12
                            $subResourceRoute = new Route($subResourceFullPath);
209 12
                            $subResourceRoute->setMethods(Request::METHOD_PUT);
210 12
                            $subResourceRoute->setDefaults(
211 12
                                array_merge(
212 12
                                    $defaults,
213
                                    [
214 12
                                        '_controller'      => $controller . ':putSubresourceAction',
215 12
                                        'subresource'      => $propertyMetadata->name,
216 12
                                        '_defaultincludes' => $method->defaultIncludes
217
                                    ]
218
                                )
219
                            );
220 12
                            $routes->add($namePrefix . '.' . $propertyMetadata->name . '.put', $subResourceRoute);
221
                        }
222
223 12
                        if (null !== $method = $propertyMetadata->getMethod(Method::DELETE)) {
224 12
                            if ($propertyMetadata->isCollection()) {
225 12
                                $subResourceFullPath = $pathPrefix . '/{id}/' . $subResourcePath . '/{subId}';
226
                            } else {
227 12
                                $subResourceFullPath = $pathPrefix . '/{id}/' . $subResourcePath;
228
                            }
229 12
                            $subResourceRoute = new Route($subResourceFullPath);
230 12
                            $subResourceRoute->setMethods(Request::METHOD_DELETE);
231 12
                            $subResourceRoute->setDefaults(
232 12
                                array_merge(
233 12
                                    $defaults,
234
                                    [
235 12
                                        '_controller' => $controller . ':deleteSubresourceAction',
236 12
                                        'subresource' => $propertyMetadata->name,
237
                                    ]
238
                                )
239
                            );
240 14
                            $routes->add($namePrefix . '.' . $propertyMetadata->name . '.delete', $subResourceRoute);
241
                        }
242
                    }
243
                }
244
            }
245
        }
246
247 14
        return $routes;
248
    }
249
250
    /**
251
     * Taken from {@see AnnotationFileLoader}
252
     * TODO: Evaluate if there is a library method or extract.
253
     *
254
     * @param $file
255
     *
256
     * @return bool|string
257
     */
258 14
    protected function findClass($file)
259
    {
260 14
        $class = false;
261 14
        $namespace = false;
262 14
        $tokens = token_get_all(file_get_contents($file));
263 14
        for ($i = 0; isset($tokens[$i]); ++$i) {
264 14
            $token = $tokens[$i];
265
266 14
            if (!isset($token[1])) {
267 14
                continue;
268
            }
269
270 14
            if (true === $class && T_STRING === $token[0]) {
271 14
                return $namespace . '\\' . $token[1];
272
            }
273
274 14
            if (true === $namespace && T_STRING === $token[0]) {
275 14
                $namespace = $token[1];
276 14
                while (isset($tokens[++$i][1]) && in_array($tokens[$i][0], array(T_NS_SEPARATOR, T_STRING))) {
277 14
                    $namespace .= $tokens[$i][1];
278
                }
279 14
                $token = $tokens[$i];
280
            }
281
282 14
            if (T_CLASS === $token[0]) {
283 14
                $class = true;
284
            }
285
286 14
            if (T_NAMESPACE === $token[0]) {
287 14
                $namespace = true;
288
            }
289
        }
290
291
        return false;
292
    }
293
294
    /**
295
     * {@inheritdoc}
296
     */
297 14
    public function supports($resource, $type = null)
298
    {
299 14
        return 'ddr_rest' === $type;
300
    }
301
302
    /**
303
     * @param ClassMetadata $classMetadata
304
     *
305
     * @return string
306
     */
307 14
    protected function getController(ClassMetadata $classMetadata)
308
    {
309
//        $controller = DoctrineRestResourceController::class;
310 14
        $controller = 'ddr_rest.controller.doctrine';
311 14
        if (null !== $classMetadata->getController()) {
312
            $controller = $classMetadata->getController();
313
        }
314
315 14
        if (strpos($controller, ':') !== false) {
316
            throw new \RuntimeException(sprintf('Short controller notation is not permitted for "%s"', $controller));
317
        }
318
319 14
        if (strpos($controller, '\\') !== false) {
320
            $controller .= ':';
321
        }
322
323 14
        return $controller;
324
    }
325
326 14
    private function loadAccessTokenController(RouteCollection $routes)
327
    {
328 14
        $route = new Route('accesstokens');
329 14
        $route->setMethods('POST');
330 14
        $route->setDefault('_controller', 'ddr_rest.controller.access_token:createAction');
331 14
        $route->setDefault('_format', 'json');
332 14
        $routes->add('ddr_rest.accesstoken.create', $route);
333
334 14
        $route = new Route('accesstokens');
335 14
        $route->setMethods('GET');
336 14
        $route->setDefault('_controller', 'ddr_rest.controller.access_token:listAction');
337 14
        $route->setDefault('_format', 'json');
338 14
        $routes->add('ddr_rest.accesstoken.list', $route);
339
340 14
        $route = new Route('accesstokens/{token}');
341 14
        $route->setMethods('DELETE');
342 14
        $route->setDefault('_controller', 'ddr_rest.controller.access_token:deleteAction');
343 14
        $route->setDefault('_format', 'json');
344 14
        $routes->add('ddr_rest.accesstoken.delete', $route);
345 14
    }
346
}
347