Passed
Push — master ( 4a42e0...ba43ac )
by Alexis
01:55
created

Router::CRUD()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 9
nc 6
nop 4
dl 0
loc 18
rs 8.2222
c 0
b 0
f 0
1
<?php
2
3
namespace App\Core\Rest;
4
5
use InvalidArgumentException;
6
use Slim\Interfaces\RouteInterface;
7
use Slim\Interfaces\RouterInterface;
8
9
class Router
10
{
11
    const DEFAULT_KEY = 'id';
12
    const DEFAULT_REQUIREMENT = '[0-9]+';
13
14
    /**
15
     * @var RouterInterface
16
     */
17
    protected $router;
18
19
    /**
20
     * @var string
21
     */
22
    protected $prefix;
23
24
    /**
25
     * @var string
26
     */
27
    protected $key;
28
29
    /**
30
     * @var string
31
     */
32
    protected $requirement;
33
34
    /**
35
     * @var array
36
     */
37
    protected $CRUDMethods;
38
39
    /**
40
     * Constructor.
41
     *
42
     * @param RouterInterface $router
43
     * @param array           $config
44
     */
45
    public function __construct(RouterInterface $router, array $config = [])
46
    {
47
        $this->router = $router;
48
49
        $this->prefix = $config['prefix'] ?? '';
50
        $this->key = $config['key'] ?? self::DEFAULT_KEY;
51
        $this->requirement = $config['requirement'] ?? self::DEFAULT_REQUIREMENT;
52
53
        $this->CRUDMethods = [
54
            'get' => $config['crud']['get'] ?? true,
55
            'cget' => $config['crud']['get_collection'] ?? true,
56
            'post' => $config['crud']['post'] ?? true,
57
            'put' => $config['crud']['put'] ?? true,
58
            'delete' => $config['crud']['delete'] ?? true,
59
            'cdelete' => $config['crud']['delete_collection'] ?? false
60
        ];
61
    }
62
63
    /**
64
     * Gets the URL pattern of a resource.
65
     *
66
     * @param string $collection
67
     * @param bool   $one
68
     * @param array  $options
69
     *
70
     * @return string
71
     */
72
    public function pattern(string $collection, bool $one = true, array $options = [])
73
    {
74
        $key = $options['key'] ?? $this->key;
75
        $requirement = $options['requirement'] ?? $this->requirement;
76
77
        if ($requirement) {
78
            $requirement = ':' . $requirement;
79
        }
80
81
        return $this->prefix . '/' . $collection . ($one ? '/{' . $key . $requirement . '}' : '');
82
    }
83
84
    /**
85
     * Gets the URL pattern of a sub resource.
86
     *
87
     * @param string $parentCollection
88
     * @param string $subCollection
89
     * @param bool   $one
90
     * @param array  $options
91
     *
92
     * @return string
93
     */
94
    public function subPattern(string $parentCollection, string $subCollection, bool $one = true, array $options = [])
95
    {
96
        $parentSingular = $options['parent_singular'] ?? $this->singular($parentCollection);
97
        $subSingular = $options['sub_singular'] ?? $this->singular($subCollection);
98
99
        $parentKey = $options['parent_key'] ?? $parentSingular . '_' . $this->key;
100
        $subKey = $options['sub_key'] ?? $subSingular . '_' . $this->key;
101
102
        $parentRequirement = $options['parent_requirement'] ?? $this->requirement;
103
        $subRequirement = $options['sub_requirement'] ?? $this->requirement;
104
105
        if ($subRequirement) {
106
            $subRequirement = ':' . $subRequirement;
107
        }
108
109
        $parent = $this->pattern($parentCollection, true, [
110
            'key' => $parentKey,
111
            'requirement' => $parentRequirement
112
        ]);
113
114
        return $parent . '/' . $subCollection . ($one ? '/{' . $subKey . $subRequirement . '}' : '');
115
    }
116
117
    /**
118
     * Generates a route for a single resource.
119
     *
120
     * @param string $method
121
     * @param string $collection
122
     * @param string $controller
123
     * @param array  $options
124
     *
125
     * @return RouteInterface
126
     */
127
    public function one(string $method, string $collection, string $controller, array $options = [])
128
    {
129
        if (!$method) {
130
            throw new InvalidArgumentException('The method is required');
131
        }
132
133
        $singular = $options['singular'] ?? $this->singular($collection);
134
135
        if ($collection === $singular) {
136
            throw new InvalidArgumentException('The collection name and its singular must be different');
137
        }
138
139
        $method = strtolower($method);
140
141
        return $this->router->map(
142
            [$method],
143
            $this->pattern($collection, true, $options),
144
            $controller . ':' . $method . ucfirst($singular)
145
        )->setName($method . '_' . $singular);
146
    }
147
148
    /**
149
     * Generates a single route for a sub resource.
150
     *
151
     * @param string $method
152
     * @param string $parentCollection
153
     * @param string $subCollection
154
     * @param string $controller
155
     * @param array  $options
156
     *
157
     * @return RouteInterface
158
     */
159
    public function subOne(string $method, string $parentCollection, string $subCollection, string $controller, array $options = [])
160
    {
161
        if (!$method) {
162
            throw new InvalidArgumentException('The method is required');
163
        }
164
165
        $parentSingular = $options['parent_singular'] ?? $this->singular($parentCollection);
166
        $subSingular = $options['sub_singular'] ?? $this->singular($subCollection);
167
168
        if ($parentCollection === $parentSingular) {
169
            throw new InvalidArgumentException('The parent collection name and its singular must be different');
170
        }
171
172
        if ($subCollection === $subSingular) {
173
            throw new InvalidArgumentException('The sub collection name and its singular must be different');
174
        }
175
176
        $method = strtolower($method);
177
178
        return $this->router->map(
179
            [$method],
180
            $this->subPattern($parentCollection, $subCollection, true, $options),
181
            $controller . ':' . $method . ucfirst($parentSingular) . ucfirst($subSingular)
182
        )->setName($method . '_' . $parentSingular . '_' . $subSingular);
183
    }
184
185
    /**
186
     * Generates a route for a collection.
187
     *
188
     * @param string $method
189
     * @param string $collection
190
     * @param string $controller
191
     *
192
     * @return RouteInterface
193
     */
194
    public function collection(string $method, string $collection, string $controller)
195
    {
196
        $method = strtolower($method);
197
198
        return $this->router->map(
199
            [$method],
200
            $this->pattern($collection, false),
201
            $controller . ':' . $method . ucfirst($collection)
202
        )->setName($method . '_' . $collection);
203
    }
204
205
    /**
206
     * Generates a route for a sub collection.
207
     *
208
     * @param string $method
209
     * @param string $parentCollection
210
     * @param string $subCollection
211
     * @param string $controller
212
     * @param array  $options
213
     *
214
     * @return RouteInterface
215
     */
216
    public function subCollection(string $method, string $parentCollection, string $subCollection, string $controller, array $options = [])
217
    {
218
        $parentSingular = $options['parent_singular'] ?? $this->singular($parentCollection);
219
220
        return $this->router->map(
221
            [$method],
222
            $this->subPattern($parentCollection, $subCollection, false, $options),
223
            $controller . ':get' . ucfirst($parentSingular) . ucfirst($subCollection)
224
        )->setName('get_' . $parentSingular . '_' . $subCollection);
225
    }
226
227
    /**
228
     * Generates all routes for a resource.
229
     *
230
     * @param string $collection
231
     * @param string $controller
232
     * @param array  $middleware
233
     * @param array  $options
234
     *
235
     * @return RouteInterface[]
236
     */
237
    public function CRUD(string $collection, string $controller, array $middleware = [], array $options = [])
238
    {
239
        $routes = [];
240
        foreach ($this->CRUDMethods as $method => $enabled) {
241
            if ($enabled) {
242
                $routes[$method] = $this->$method($collection, $controller, 'post' === $method ? ($options['singular'] ?? '') : $options);
243
            }
244
        }
245
246
        if (!empty($middleware)) {
247
            foreach ($routes as $route) {
248
                foreach ($middleware as $m) {
249
                    $route->add($m);
250
                }
251
            }
252
        }
253
254
        return $routes;
255
    }
256
257
    /**
258
     * Generates all routes for a sub resource.
259
     *
260
     * @param string $parentCollection
261
     * @param string $subCollection
262
     * @param string $controller
263
     * @param array  $middleware
264
     * @param array  $options
265
     *
266
     * @return RouteInterface[]
267
     */
268
    public function subCRUD(string $parentCollection, string $subCollection, string $controller, array $middleware = [], array $options = [])
269
    {
270
        $routes = [];
271
        foreach ($this->CRUDMethods as $method => $enabled) {
272
            if ($enabled) {
273
                $call = $method . 'Sub';
274
                $routes[$method] = $this->$call($parentCollection, $subCollection, $controller, $options);
275
            }
276
        }
277
278
        if (!empty($middleware)) {
279
            foreach ($routes as $route) {
280
                foreach ($middleware as $m) {
281
                    $route->add($m);
282
                }
283
            }
284
        }
285
286
        return $routes;
287
    }
288
289
    /**
290
     * Generates a route for a single resource with the GET method.
291
     *
292
     * @param string $collection
293
     * @param string $controller
294
     * @param array  $options
295
     *
296
     * @return RouteInterface
297
     */
298
    public function get(string $collection, string $controller, array $options = [])
299
    {
300
        return $this->one('GET', $collection, $controller, $options);
301
    }
302
303
    /**
304
     * Generates a route for a single sub resource with the GET method.
305
     *
306
     * @param string $parentCollection
307
     * @param string $subCollection
308
     * @param string $controller
309
     * @param array  $options
310
     *
311
     * @return RouteInterface
312
     */
313
    public function getSub(string $parentCollection, string $subCollection, string $controller, array $options = [])
314
    {
315
        return $this->subOne('GET', $parentCollection, $subCollection, $controller, $options);
316
    }
317
318
    /**
319
     * Generates a route for a collection with the GET method.
320
     *
321
     * @param string $collection
322
     * @param string $controller
323
     *
324
     * @return RouteInterface
325
     */
326
    public function cget(string $collection, string $controller)
327
    {
328
        return $this->collection('GET', $collection, $controller);
329
    }
330
331
    /**
332
     * Generates a route for a sub resource collection with the GET method.
333
     *
334
     * @param string $parentCollection
335
     * @param string $subCollection
336
     * @param string $controller
337
     * @param array  $options
338
     *
339
     * @return RouteInterface
340
     */
341
    public function cgetSub(string $parentCollection, string $subCollection, string $controller, array $options = [])
342
    {
343
        return $this->subCollection('GET', $parentCollection, $subCollection, $controller, $options);
344
    }
345
346
    /**
347
     * Generates a route for a collection with the POST method.
348
     *
349
     * @param string $collection
350
     * @param string $controller
351
     * @param string $singular
352
     *
353
     * @return RouteInterface
354
     */
355
    public function post(string $collection, string $controller, string $singular = '')
356
    {
357
        if (!$singular) {
358
            $singular = $this->singular($collection);
359
        }
360
361
        return $this->router->map(
362
            ['POST'],
363
            $this->pattern($collection, false),
364
            $controller . ':post' . ucfirst($singular)
365
        )->setName('post_' . $singular);
366
    }
367
368
    /**
369
     * Generates a route for a sub resource collection with the POST method.
370
     *
371
     * @param string $parentCollection
372
     * @param string $subCollection
373
     * @param string $controller
374
     * @param array  $options
375
     *
376
     * @return RouteInterface
377
     */
378
    public function postSub(string $parentCollection, string $subCollection, string $controller, array $options = [])
379
    {
380
        $parentSingular = $options['parent_singular'] ?? $this->singular($parentCollection);
381
        $subSingular = $options['sub_singular'] ?? $this->singular($subCollection);
382
383
        return $this->router->map(
384
            ['POST'],
385
            $this->subPattern($parentCollection, $subCollection, false, $options),
386
            $controller . ':post' . ucfirst($parentSingular) . ucfirst($subSingular)
387
        )->setName('post_' . $parentSingular . '_' . $subSingular);
388
    }
389
390
    /**
391
     * Generates a route for a single resource with the PUT method.
392
     *
393
     * @param string $collection
394
     * @param string $controller
395
     * @param array  $options
396
     *
397
     * @return RouteInterface
398
     */
399
    public function put(string $collection, string $controller, array $options = [])
400
    {
401
        return $this->one('PUT', $collection, $controller, $options);
402
    }
403
404
    /**
405
     * Generates a route for a single sub resource with the PUT method.
406
     *
407
     * @param string $parentCollection
408
     * @param string $subCollection
409
     * @param string $controller
410
     * @param array  $options
411
     *
412
     * @return RouteInterface
413
     */
414
    public function putSub(string $parentCollection, string $subCollection, string $controller, array $options = [])
415
    {
416
        return $this->subOne('PUT', $parentCollection, $subCollection, $controller, $options);
417
    }
418
419
    /**
420
     * Generates a route for a single resource with the DELETE method.
421
     *
422
     * @param string $collection
423
     * @param string $controller
424
     * @param array  $options
425
     *
426
     * @return RouteInterface
427
     */
428
    public function delete(string $collection, string $controller, array $options = [])
429
    {
430
        return $this->one('DELETE', $collection, $controller, $options);
431
    }
432
433
    /**
434
     * Generates a route for a single sub resource with the DELETE method.
435
     *
436
     * @param string $parentCollection
437
     * @param string $subCollection
438
     * @param string $controller
439
     * @param array  $options
440
     *
441
     * @return RouteInterface
442
     */
443
    public function deleteSub(string $parentCollection, string $subCollection, string $controller, array $options = [])
444
    {
445
        return $this->subOne('DELETE', $parentCollection, $subCollection, $controller, $options);
446
    }
447
448
    /**
449
     * Generates a route for a collection with the DELETE method.
450
     *
451
     * @param string $collection
452
     * @param string $controller
453
     *
454
     * @return RouteInterface
455
     */
456
    public function cdelete(string $collection, string $controller)
457
    {
458
        return $this->collection('DELETE', $collection, $controller);
459
    }
460
461
    /**
462
     * Generates a route for a sub resource collection with the DELETE method.
463
     *
464
     * @param string $parentCollection
465
     * @param string $subCollection
466
     * @param string $controller
467
     * @param array  $options
468
     *
469
     * @return RouteInterface
470
     */
471
    public function cdeleteSub(string $parentCollection, string $subCollection, string $controller, array $options = [])
472
    {
473
        return $this->subCollection('DELETE', $parentCollection, $subCollection, $controller, $options);
474
    }
475
476
    /**
477
     * Gets a resource name's singular (removes last character).
478
     *
479
     * @param string $collection
480
     *
481
     * @return string
482
     */
483
    public function singular(string $collection): string
484
    {
485
        return substr($collection, 0, -1);
486
    }
487
488
    /**
489
     * Sets URLs prefix.
490
     *
491
     * @param string $prefix
492
     */
493
    public function setPrefix(string $prefix)
494
    {
495
        $this->prefix = $prefix;
496
    }
497
498
    /**
499
     * Gets URLs prefix.
500
     *
501
     * @return string
502
     */
503
    public function getPrefix(): string
504
    {
505
        return $this->prefix;
506
    }
507
508
    /**
509
     * Sets the default key.
510
     *
511
     * @param string $key
512
     */
513
    public function setKey(string $key)
514
    {
515
        $this->key = $key;
516
    }
517
518
    /**
519
     * Gets the default key.
520
     *
521
     * @return string
522
     */
523
    public function getKey(): string
524
    {
525
        return $this->key;
526
    }
527
528
    /**
529
     * Sets the default route placeholder requirement.
530
     *
531
     * @param string $requirement
532
     */
533
    public function setRequirement(string $requirement)
534
    {
535
        $this->requirement = $requirement;
536
    }
537
538
    /**
539
     * Gets the default route placeholder requirement.
540
     *
541
     * @return string
542
     */
543
    public function getRequirement(): string
544
    {
545
        return $this->requirement;
546
    }
547
548
    /**
549
     * Sets the CRUD methods.
550
     *
551
     * @param array $methods
552
     */
553
    public function setCRUDMethods(array $methods)
554
    {
555
        $this->CRUDMethods = $methods;
556
    }
557
558
    /**
559
     * Gets the CRUD methods.
560
     *
561
     * @return array
562
     */
563
    public function getCRUDMethods(): array
564
    {
565
        return $this->CRUDMethods;
566
    }
567
}
568