Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Illuminate\Routing;
4
 
5
use Illuminate\Support\Arr;
6
use Illuminate\Support\Str;
7
 
8
class ResourceRegistrar
9
{
10
    /**
11
     * The router instance.
12
     *
13
     * @var \Illuminate\Routing\Router
14
     */
15
    protected $router;
16
 
17
    /**
18
     * The default actions for a resourceful controller.
19
     *
20
     * @var string[]
21
     */
22
    protected $resourceDefaults = ['index', 'create', 'store', 'show', 'edit', 'update', 'destroy'];
23
 
24
    /**
25
     * The default actions for a singleton resource controller.
26
     *
27
     * @var string[]
28
     */
29
    protected $singletonResourceDefaults = ['show', 'edit', 'update'];
30
 
31
    /**
32
     * The parameters set for this resource instance.
33
     *
34
     * @var array|string
35
     */
36
    protected $parameters;
37
 
38
    /**
39
     * The global parameter mapping.
40
     *
41
     * @var array
42
     */
43
    protected static $parameterMap = [];
44
 
45
    /**
46
     * Singular global parameters.
47
     *
48
     * @var bool
49
     */
50
    protected static $singularParameters = true;
51
 
52
    /**
53
     * The verbs used in the resource URIs.
54
     *
55
     * @var array
56
     */
57
    protected static $verbs = [
58
        'create' => 'create',
59
        'edit' => 'edit',
60
    ];
61
 
62
    /**
63
     * Create a new resource registrar instance.
64
     *
65
     * @param  \Illuminate\Routing\Router  $router
66
     * @return void
67
     */
68
    public function __construct(Router $router)
69
    {
70
        $this->router = $router;
71
    }
72
 
73
    /**
74
     * Route a resource to a controller.
75
     *
76
     * @param  string  $name
77
     * @param  string  $controller
78
     * @param  array  $options
79
     * @return \Illuminate\Routing\RouteCollection
80
     */
81
    public function register($name, $controller, array $options = [])
82
    {
83
        if (isset($options['parameters']) && ! isset($this->parameters)) {
84
            $this->parameters = $options['parameters'];
85
        }
86
 
87
        // If the resource name contains a slash, we will assume the developer wishes to
88
        // register these resource routes with a prefix so we will set that up out of
89
        // the box so they don't have to mess with it. Otherwise, we will continue.
90
        if (str_contains($name, '/')) {
91
            $this->prefixedResource($name, $controller, $options);
92
 
93
            return;
94
        }
95
 
96
        // We need to extract the base resource from the resource name. Nested resources
97
        // are supported in the framework, but we need to know what name to use for a
98
        // place-holder on the route parameters, which should be the base resources.
99
        $base = $this->getResourceWildcard(last(explode('.', $name)));
100
 
101
        $defaults = $this->resourceDefaults;
102
 
103
        $collection = new RouteCollection;
104
 
105
        $resourceMethods = $this->getResourceMethods($defaults, $options);
106
 
107
        foreach ($resourceMethods as $m) {
108
            $route = $this->{'addResource'.ucfirst($m)}(
109
                $name, $base, $controller, $options
110
            );
111
 
112
            if (isset($options['bindingFields'])) {
113
                $this->setResourceBindingFields($route, $options['bindingFields']);
114
            }
115
 
116
            if (isset($options['trashed']) &&
117
                in_array($m, ! empty($options['trashed']) ? $options['trashed'] : array_intersect($resourceMethods, ['show', 'edit', 'update']))) {
118
                $route->withTrashed();
119
            }
120
 
121
            $collection->add($route);
122
        }
123
 
124
        return $collection;
125
    }
126
 
127
    /**
128
     * Route a singleton resource to a controller.
129
     *
130
     * @param  string  $name
131
     * @param  string  $controller
132
     * @param  array  $options
133
     * @return \Illuminate\Routing\RouteCollection
134
     */
135
    public function singleton($name, $controller, array $options = [])
136
    {
137
        if (isset($options['parameters']) && ! isset($this->parameters)) {
138
            $this->parameters = $options['parameters'];
139
        }
140
 
141
        // If the resource name contains a slash, we will assume the developer wishes to
142
        // register these singleton routes with a prefix so we will set that up out of
143
        // the box so they don't have to mess with it. Otherwise, we will continue.
144
        if (str_contains($name, '/')) {
145
            $this->prefixedSingleton($name, $controller, $options);
146
 
147
            return;
148
        }
149
 
150
        $defaults = $this->singletonResourceDefaults;
151
 
152
        $collection = new RouteCollection;
153
 
154
        $resourceMethods = $this->getResourceMethods($defaults, $options);
155
 
156
        foreach ($resourceMethods as $m) {
157
            $route = $this->{'addSingleton'.ucfirst($m)}(
158
                $name, $controller, $options
159
            );
160
 
161
            if (isset($options['bindingFields'])) {
162
                $this->setResourceBindingFields($route, $options['bindingFields']);
163
            }
164
 
165
            $collection->add($route);
166
        }
167
 
168
        return $collection;
169
    }
170
 
171
    /**
172
     * Build a set of prefixed resource routes.
173
     *
174
     * @param  string  $name
175
     * @param  string  $controller
176
     * @param  array  $options
177
     * @return void
178
     */
179
    protected function prefixedResource($name, $controller, array $options)
180
    {
181
        [$name, $prefix] = $this->getResourcePrefix($name);
182
 
183
        // We need to extract the base resource from the resource name. Nested resources
184
        // are supported in the framework, but we need to know what name to use for a
185
        // place-holder on the route parameters, which should be the base resources.
186
        $callback = function ($me) use ($name, $controller, $options) {
187
            $me->resource($name, $controller, $options);
188
        };
189
 
190
        return $this->router->group(compact('prefix'), $callback);
191
    }
192
 
193
    /**
194
     * Build a set of prefixed singleton routes.
195
     *
196
     * @param  string  $name
197
     * @param  string  $controller
198
     * @param  array  $options
199
     * @return void
200
     */
201
    protected function prefixedSingleton($name, $controller, array $options)
202
    {
203
        [$name, $prefix] = $this->getResourcePrefix($name);
204
 
205
        // We need to extract the base resource from the resource name. Nested resources
206
        // are supported in the framework, but we need to know what name to use for a
207
        // place-holder on the route parameters, which should be the base resources.
208
        $callback = function ($me) use ($name, $controller, $options) {
209
            $me->singleton($name, $controller, $options);
210
        };
211
 
212
        return $this->router->group(compact('prefix'), $callback);
213
    }
214
 
215
    /**
216
     * Extract the resource and prefix from a resource name.
217
     *
218
     * @param  string  $name
219
     * @return array
220
     */
221
    protected function getResourcePrefix($name)
222
    {
223
        $segments = explode('/', $name);
224
 
225
        // To get the prefix, we will take all of the name segments and implode them on
226
        // a slash. This will generate a proper URI prefix for us. Then we take this
227
        // last segment, which will be considered the final resources name we use.
228
        $prefix = implode('/', array_slice($segments, 0, -1));
229
 
230
        return [end($segments), $prefix];
231
    }
232
 
233
    /**
234
     * Get the applicable resource methods.
235
     *
236
     * @param  array  $defaults
237
     * @param  array  $options
238
     * @return array
239
     */
240
    protected function getResourceMethods($defaults, $options)
241
    {
242
        $methods = $defaults;
243
 
244
        if (isset($options['only'])) {
245
            $methods = array_intersect($methods, (array) $options['only']);
246
        }
247
 
248
        if (isset($options['except'])) {
249
            $methods = array_diff($methods, (array) $options['except']);
250
        }
251
 
252
        if (isset($options['creatable'])) {
253
            $methods = isset($options['apiSingleton'])
254
                            ? array_merge(['store', 'destroy'], $methods)
255
                            : array_merge(['create', 'store', 'destroy'], $methods);
256
 
257
            return $this->getResourceMethods(
258
                $methods, array_values(Arr::except($options, ['creatable']))
259
            );
260
        }
261
 
262
        if (isset($options['destroyable'])) {
263
            $methods = array_merge(['destroy'], $methods);
264
 
265
            return $this->getResourceMethods(
266
                $methods, array_values(Arr::except($options, ['destroyable']))
267
            );
268
        }
269
 
270
        return $methods;
271
    }
272
 
273
    /**
274
     * Add the index method for a resourceful route.
275
     *
276
     * @param  string  $name
277
     * @param  string  $base
278
     * @param  string  $controller
279
     * @param  array  $options
280
     * @return \Illuminate\Routing\Route
281
     */
282
    protected function addResourceIndex($name, $base, $controller, $options)
283
    {
284
        $uri = $this->getResourceUri($name);
285
 
286
        unset($options['missing']);
287
 
288
        $action = $this->getResourceAction($name, $controller, 'index', $options);
289
 
290
        return $this->router->get($uri, $action);
291
    }
292
 
293
    /**
294
     * Add the create method for a resourceful route.
295
     *
296
     * @param  string  $name
297
     * @param  string  $base
298
     * @param  string  $controller
299
     * @param  array  $options
300
     * @return \Illuminate\Routing\Route
301
     */
302
    protected function addResourceCreate($name, $base, $controller, $options)
303
    {
304
        $uri = $this->getResourceUri($name).'/'.static::$verbs['create'];
305
 
306
        unset($options['missing']);
307
 
308
        $action = $this->getResourceAction($name, $controller, 'create', $options);
309
 
310
        return $this->router->get($uri, $action);
311
    }
312
 
313
    /**
314
     * Add the store method for a resourceful route.
315
     *
316
     * @param  string  $name
317
     * @param  string  $base
318
     * @param  string  $controller
319
     * @param  array  $options
320
     * @return \Illuminate\Routing\Route
321
     */
322
    protected function addResourceStore($name, $base, $controller, $options)
323
    {
324
        $uri = $this->getResourceUri($name);
325
 
326
        unset($options['missing']);
327
 
328
        $action = $this->getResourceAction($name, $controller, 'store', $options);
329
 
330
        return $this->router->post($uri, $action);
331
    }
332
 
333
    /**
334
     * Add the show method for a resourceful route.
335
     *
336
     * @param  string  $name
337
     * @param  string  $base
338
     * @param  string  $controller
339
     * @param  array  $options
340
     * @return \Illuminate\Routing\Route
341
     */
342
    protected function addResourceShow($name, $base, $controller, $options)
343
    {
344
        $name = $this->getShallowName($name, $options);
345
 
346
        $uri = $this->getResourceUri($name).'/{'.$base.'}';
347
 
348
        $action = $this->getResourceAction($name, $controller, 'show', $options);
349
 
350
        return $this->router->get($uri, $action);
351
    }
352
 
353
    /**
354
     * Add the edit method for a resourceful route.
355
     *
356
     * @param  string  $name
357
     * @param  string  $base
358
     * @param  string  $controller
359
     * @param  array  $options
360
     * @return \Illuminate\Routing\Route
361
     */
362
    protected function addResourceEdit($name, $base, $controller, $options)
363
    {
364
        $name = $this->getShallowName($name, $options);
365
 
366
        $uri = $this->getResourceUri($name).'/{'.$base.'}/'.static::$verbs['edit'];
367
 
368
        $action = $this->getResourceAction($name, $controller, 'edit', $options);
369
 
370
        return $this->router->get($uri, $action);
371
    }
372
 
373
    /**
374
     * Add the update method for a resourceful route.
375
     *
376
     * @param  string  $name
377
     * @param  string  $base
378
     * @param  string  $controller
379
     * @param  array  $options
380
     * @return \Illuminate\Routing\Route
381
     */
382
    protected function addResourceUpdate($name, $base, $controller, $options)
383
    {
384
        $name = $this->getShallowName($name, $options);
385
 
386
        $uri = $this->getResourceUri($name).'/{'.$base.'}';
387
 
388
        $action = $this->getResourceAction($name, $controller, 'update', $options);
389
 
390
        return $this->router->match(['PUT', 'PATCH'], $uri, $action);
391
    }
392
 
393
    /**
394
     * Add the destroy method for a resourceful route.
395
     *
396
     * @param  string  $name
397
     * @param  string  $base
398
     * @param  string  $controller
399
     * @param  array  $options
400
     * @return \Illuminate\Routing\Route
401
     */
402
    protected function addResourceDestroy($name, $base, $controller, $options)
403
    {
404
        $name = $this->getShallowName($name, $options);
405
 
406
        $uri = $this->getResourceUri($name).'/{'.$base.'}';
407
 
408
        $action = $this->getResourceAction($name, $controller, 'destroy', $options);
409
 
410
        return $this->router->delete($uri, $action);
411
    }
412
 
413
    /**
414
     * Add the create method for a singleton route.
415
     *
416
     * @param  string  $name
417
     * @param  string  $controller
418
     * @param  array  $options
419
     * @return \Illuminate\Routing\Route
420
     */
421
    protected function addSingletonCreate($name, $controller, $options)
422
    {
423
        $uri = $this->getResourceUri($name).'/'.static::$verbs['create'];
424
 
425
        unset($options['missing']);
426
 
427
        $action = $this->getResourceAction($name, $controller, 'create', $options);
428
 
429
        return $this->router->get($uri, $action);
430
    }
431
 
432
    /**
433
     * Add the store method for a singleton route.
434
     *
435
     * @param  string  $name
436
     * @param  string  $controller
437
     * @param  array  $options
438
     * @return \Illuminate\Routing\Route
439
     */
440
    protected function addSingletonStore($name, $controller, $options)
441
    {
442
        $uri = $this->getResourceUri($name);
443
 
444
        unset($options['missing']);
445
 
446
        $action = $this->getResourceAction($name, $controller, 'store', $options);
447
 
448
        return $this->router->post($uri, $action);
449
    }
450
 
451
    /**
452
     * Add the show method for a singleton route.
453
     *
454
     * @param  string  $name
455
     * @param  string  $controller
456
     * @param  array  $options
457
     * @return \Illuminate\Routing\Route
458
     */
459
    protected function addSingletonShow($name, $controller, $options)
460
    {
461
        $uri = $this->getResourceUri($name);
462
 
463
        unset($options['missing']);
464
 
465
        $action = $this->getResourceAction($name, $controller, 'show', $options);
466
 
467
        return $this->router->get($uri, $action);
468
    }
469
 
470
    /**
471
     * Add the edit method for a singleton route.
472
     *
473
     * @param  string  $name
474
     * @param  string  $controller
475
     * @param  array  $options
476
     * @return \Illuminate\Routing\Route
477
     */
478
    protected function addSingletonEdit($name, $controller, $options)
479
    {
480
        $name = $this->getShallowName($name, $options);
481
 
482
        $uri = $this->getResourceUri($name).'/'.static::$verbs['edit'];
483
 
484
        $action = $this->getResourceAction($name, $controller, 'edit', $options);
485
 
486
        return $this->router->get($uri, $action);
487
    }
488
 
489
    /**
490
     * Add the update method for a singleton route.
491
     *
492
     * @param  string  $name
493
     * @param  string  $base
494
     * @param  string  $controller
495
     * @param  array  $options
496
     * @return \Illuminate\Routing\Route
497
     */
498
    protected function addSingletonUpdate($name, $controller, $options)
499
    {
500
        $name = $this->getShallowName($name, $options);
501
 
502
        $uri = $this->getResourceUri($name);
503
 
504
        $action = $this->getResourceAction($name, $controller, 'update', $options);
505
 
506
        return $this->router->match(['PUT', 'PATCH'], $uri, $action);
507
    }
508
 
509
    /**
510
     * Add the destroy method for a singleton route.
511
     *
512
     * @param  string  $name
513
     * @param  string  $controller
514
     * @param  array  $options
515
     * @return \Illuminate\Routing\Route
516
     */
517
    protected function addSingletonDestroy($name, $controller, $options)
518
    {
519
        $name = $this->getShallowName($name, $options);
520
 
521
        $uri = $this->getResourceUri($name);
522
 
523
        $action = $this->getResourceAction($name, $controller, 'destroy', $options);
524
 
525
        return $this->router->delete($uri, $action);
526
    }
527
 
528
    /**
529
     * Get the name for a given resource with shallowness applied when applicable.
530
     *
531
     * @param  string  $name
532
     * @param  array  $options
533
     * @return string
534
     */
535
    protected function getShallowName($name, $options)
536
    {
537
        return isset($options['shallow']) && $options['shallow']
538
                    ? last(explode('.', $name))
539
                    : $name;
540
    }
541
 
542
    /**
543
     * Set the route's binding fields if the resource is scoped.
544
     *
545
     * @param  \Illuminate\Routing\Route  $route
546
     * @param  array  $bindingFields
547
     * @return void
548
     */
549
    protected function setResourceBindingFields($route, $bindingFields)
550
    {
551
        preg_match_all('/(?<={).*?(?=})/', $route->uri, $matches);
552
 
553
        $fields = array_fill_keys($matches[0], null);
554
 
555
        $route->setBindingFields(array_replace(
556
            $fields, array_intersect_key($bindingFields, $fields)
557
        ));
558
    }
559
 
560
    /**
561
     * Get the base resource URI for a given resource.
562
     *
563
     * @param  string  $resource
564
     * @return string
565
     */
566
    public function getResourceUri($resource)
567
    {
568
        if (! str_contains($resource, '.')) {
569
            return $resource;
570
        }
571
 
572
        // Once we have built the base URI, we'll remove the parameter holder for this
573
        // base resource name so that the individual route adders can suffix these
574
        // paths however they need to, as some do not have any parameters at all.
575
        $segments = explode('.', $resource);
576
 
577
        $uri = $this->getNestedResourceUri($segments);
578
 
579
        return str_replace('/{'.$this->getResourceWildcard(end($segments)).'}', '', $uri);
580
    }
581
 
582
    /**
583
     * Get the URI for a nested resource segment array.
584
     *
585
     * @param  array  $segments
586
     * @return string
587
     */
588
    protected function getNestedResourceUri(array $segments)
589
    {
590
        // We will spin through the segments and create a place-holder for each of the
591
        // resource segments, as well as the resource itself. Then we should get an
592
        // entire string for the resource URI that contains all nested resources.
593
        return implode('/', array_map(function ($s) {
594
            return $s.'/{'.$this->getResourceWildcard($s).'}';
595
        }, $segments));
596
    }
597
 
598
    /**
599
     * Format a resource parameter for usage.
600
     *
601
     * @param  string  $value
602
     * @return string
603
     */
604
    public function getResourceWildcard($value)
605
    {
606
        if (isset($this->parameters[$value])) {
607
            $value = $this->parameters[$value];
608
        } elseif (isset(static::$parameterMap[$value])) {
609
            $value = static::$parameterMap[$value];
610
        } elseif ($this->parameters === 'singular' || static::$singularParameters) {
611
            $value = Str::singular($value);
612
        }
613
 
614
        return str_replace('-', '_', $value);
615
    }
616
 
617
    /**
618
     * Get the action array for a resource route.
619
     *
620
     * @param  string  $resource
621
     * @param  string  $controller
622
     * @param  string  $method
623
     * @param  array  $options
624
     * @return array
625
     */
626
    protected function getResourceAction($resource, $controller, $method, $options)
627
    {
628
        $name = $this->getResourceRouteName($resource, $method, $options);
629
 
630
        $action = ['as' => $name, 'uses' => $controller.'@'.$method];
631
 
632
        if (isset($options['middleware'])) {
633
            $action['middleware'] = $options['middleware'];
634
        }
635
 
636
        if (isset($options['excluded_middleware'])) {
637
            $action['excluded_middleware'] = $options['excluded_middleware'];
638
        }
639
 
640
        if (isset($options['wheres'])) {
641
            $action['where'] = $options['wheres'];
642
        }
643
 
644
        if (isset($options['missing'])) {
645
            $action['missing'] = $options['missing'];
646
        }
647
 
648
        return $action;
649
    }
650
 
651
    /**
652
     * Get the name for a given resource.
653
     *
654
     * @param  string  $resource
655
     * @param  string  $method
656
     * @param  array  $options
657
     * @return string
658
     */
659
    protected function getResourceRouteName($resource, $method, $options)
660
    {
661
        $name = $resource;
662
 
663
        // If the names array has been provided to us we will check for an entry in the
664
        // array first. We will also check for the specific method within this array
665
        // so the names may be specified on a more "granular" level using methods.
666
        if (isset($options['names'])) {
667
            if (is_string($options['names'])) {
668
                $name = $options['names'];
669
            } elseif (isset($options['names'][$method])) {
670
                return $options['names'][$method];
671
            }
672
        }
673
 
674
        // If a global prefix has been assigned to all names for this resource, we will
675
        // grab that so we can prepend it onto the name when we create this name for
676
        // the resource action. Otherwise we'll just use an empty string for here.
677
        $prefix = isset($options['as']) ? $options['as'].'.' : '';
678
 
679
        return trim(sprintf('%s%s.%s', $prefix, $name, $method), '.');
680
    }
681
 
682
    /**
683
     * Set or unset the unmapped global parameters to singular.
684
     *
685
     * @param  bool  $singular
686
     * @return void
687
     */
688
    public static function singularParameters($singular = true)
689
    {
690
        static::$singularParameters = (bool) $singular;
691
    }
692
 
693
    /**
694
     * Get the global parameter map.
695
     *
696
     * @return array
697
     */
698
    public static function getParameters()
699
    {
700
        return static::$parameterMap;
701
    }
702
 
703
    /**
704
     * Set the global parameter mapping.
705
     *
706
     * @param  array  $parameters
707
     * @return void
708
     */
709
    public static function setParameters(array $parameters = [])
710
    {
711
        static::$parameterMap = $parameters;
712
    }
713
 
714
    /**
715
     * Get or set the action verbs used in the resource URIs.
716
     *
717
     * @param  array  $verbs
718
     * @return array
719
     */
720
    public static function verbs(array $verbs = [])
721
    {
722
        if (empty($verbs)) {
723
            return static::$verbs;
724
        } else {
725
            static::$verbs = array_merge(static::$verbs, $verbs);
726
        }
727
    }
728
}