Completed
Push — standalone ( 444422...a45559 )
by Philip
05:18
created

RestResourceLoader::supports()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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