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\Pagination;
4
 
5
use ArrayAccess;
6
use Closure;
7
use Exception;
8
use Illuminate\Contracts\Support\Htmlable;
9
use Illuminate\Database\Eloquent\Model;
10
use Illuminate\Database\Eloquent\Relations\Pivot;
11
use Illuminate\Http\Resources\Json\JsonResource;
12
use Illuminate\Support\Arr;
13
use Illuminate\Support\Collection;
14
use Illuminate\Support\Str;
15
use Illuminate\Support\Traits\ForwardsCalls;
16
use Illuminate\Support\Traits\Tappable;
17
use Traversable;
18
 
19
/**
20
 * @mixin \Illuminate\Support\Collection
21
 */
22
abstract class AbstractCursorPaginator implements Htmlable
23
{
24
    use ForwardsCalls, Tappable;
25
 
26
    /**
27
     * All of the items being paginated.
28
     *
29
     * @var \Illuminate\Support\Collection
30
     */
31
    protected $items;
32
 
33
    /**
34
     * The number of items to be shown per page.
35
     *
36
     * @var int
37
     */
38
    protected $perPage;
39
 
40
    /**
41
     * The base path to assign to all URLs.
42
     *
43
     * @var string
44
     */
45
    protected $path = '/';
46
 
47
    /**
48
     * The query parameters to add to all URLs.
49
     *
50
     * @var array
51
     */
52
    protected $query = [];
53
 
54
    /**
55
     * The URL fragment to add to all URLs.
56
     *
57
     * @var string|null
58
     */
59
    protected $fragment;
60
 
61
    /**
62
     * The cursor string variable used to store the page.
63
     *
64
     * @var string
65
     */
66
    protected $cursorName = 'cursor';
67
 
68
    /**
69
     * The current cursor.
70
     *
71
     * @var \Illuminate\Pagination\Cursor|null
72
     */
73
    protected $cursor;
74
 
75
    /**
76
     * The paginator parameters for the cursor.
77
     *
78
     * @var array
79
     */
80
    protected $parameters;
81
 
82
    /**
83
     * The paginator options.
84
     *
85
     * @var array
86
     */
87
    protected $options;
88
 
89
    /**
90
     * The current cursor resolver callback.
91
     *
92
     * @var \Closure
93
     */
94
    protected static $currentCursorResolver;
95
 
96
    /**
97
     * Get the URL for a given cursor.
98
     *
99
     * @param  \Illuminate\Pagination\Cursor|null  $cursor
100
     * @return string
101
     */
102
    public function url($cursor)
103
    {
104
        // If we have any extra query string key / value pairs that need to be added
105
        // onto the URL, we will put them in query string form and then attach it
106
        // to the URL. This allows for extra information like sortings storage.
107
        $parameters = is_null($cursor) ? [] : [$this->cursorName => $cursor->encode()];
108
 
109
        if (count($this->query) > 0) {
110
            $parameters = array_merge($this->query, $parameters);
111
        }
112
 
113
        return $this->path()
114
            .(str_contains($this->path(), '?') ? '&' : '?')
115
            .Arr::query($parameters)
116
            .$this->buildFragment();
117
    }
118
 
119
    /**
120
     * Get the URL for the previous page.
121
     *
122
     * @return string|null
123
     */
124
    public function previousPageUrl()
125
    {
126
        if (is_null($previousCursor = $this->previousCursor())) {
127
            return null;
128
        }
129
 
130
        return $this->url($previousCursor);
131
    }
132
 
133
    /**
134
     * The URL for the next page, or null.
135
     *
136
     * @return string|null
137
     */
138
    public function nextPageUrl()
139
    {
140
        if (is_null($nextCursor = $this->nextCursor())) {
141
            return null;
142
        }
143
 
144
        return $this->url($nextCursor);
145
    }
146
 
147
    /**
148
     * Get the "cursor" that points to the previous set of items.
149
     *
150
     * @return \Illuminate\Pagination\Cursor|null
151
     */
152
    public function previousCursor()
153
    {
154
        if (is_null($this->cursor) ||
155
            ($this->cursor->pointsToPreviousItems() && ! $this->hasMore)) {
156
            return null;
157
        }
158
 
159
        if ($this->items->isEmpty()) {
160
            return null;
161
        }
162
 
163
        return $this->getCursorForItem($this->items->first(), false);
164
    }
165
 
166
    /**
167
     * Get the "cursor" that points to the next set of items.
168
     *
169
     * @return \Illuminate\Pagination\Cursor|null
170
     */
171
    public function nextCursor()
172
    {
173
        if ((is_null($this->cursor) && ! $this->hasMore) ||
174
            (! is_null($this->cursor) && $this->cursor->pointsToNextItems() && ! $this->hasMore)) {
175
            return null;
176
        }
177
 
178
        if ($this->items->isEmpty()) {
179
            return null;
180
        }
181
 
182
        return $this->getCursorForItem($this->items->last(), true);
183
    }
184
 
185
    /**
186
     * Get a cursor instance for the given item.
187
     *
188
     * @param  \ArrayAccess|\stdClass  $item
189
     * @param  bool  $isNext
190
     * @return \Illuminate\Pagination\Cursor
191
     */
192
    public function getCursorForItem($item, $isNext = true)
193
    {
194
        return new Cursor($this->getParametersForItem($item), $isNext);
195
    }
196
 
197
    /**
198
     * Get the cursor parameters for a given object.
199
     *
200
     * @param  \ArrayAccess|\stdClass  $item
201
     * @return array
202
     *
203
     * @throws \Exception
204
     */
205
    public function getParametersForItem($item)
206
    {
207
        return collect($this->parameters)
208
            ->flip()
209
            ->map(function ($_, $parameterName) use ($item) {
210
                if ($item instanceof JsonResource) {
211
                    $item = $item->resource;
212
                }
213
 
214
                if ($item instanceof Model &&
215
                    ! is_null($parameter = $this->getPivotParameterForItem($item, $parameterName))) {
216
                    return $parameter;
217
                } elseif ($item instanceof ArrayAccess || is_array($item)) {
218
                    return $this->ensureParameterIsPrimitive(
219
                        $item[$parameterName] ?? $item[Str::afterLast($parameterName, '.')]
220
                    );
221
                } elseif (is_object($item)) {
222
                    return $this->ensureParameterIsPrimitive(
223
                        $item->{$parameterName} ?? $item->{Str::afterLast($parameterName, '.')}
224
                    );
225
                }
226
 
227
                throw new Exception('Only arrays and objects are supported when cursor paginating items.');
228
            })->toArray();
229
    }
230
 
231
    /**
232
     * Get the cursor parameter value from a pivot model if applicable.
233
     *
234
     * @param  \ArrayAccess|\stdClass  $item
235
     * @param  string  $parameterName
236
     * @return string|null
237
     */
238
    protected function getPivotParameterForItem($item, $parameterName)
239
    {
240
        $table = Str::beforeLast($parameterName, '.');
241
 
242
        foreach ($item->getRelations() as $relation) {
243
            if ($relation instanceof Pivot && $relation->getTable() === $table) {
244
                return $this->ensureParameterIsPrimitive(
245
                    $relation->getAttribute(Str::afterLast($parameterName, '.'))
246
                );
247
            }
248
        }
249
    }
250
 
251
    /**
252
     * Ensure the parameter is a primitive type.
253
     *
254
     * This can resolve issues that arise the developer uses a value object for an attribute.
255
     *
256
     * @param  mixed  $parameter
257
     * @return mixed
258
     */
259
    protected function ensureParameterIsPrimitive($parameter)
260
    {
261
        return is_object($parameter) && method_exists($parameter, '__toString')
262
                        ? (string) $parameter
263
                        : $parameter;
264
    }
265
 
266
    /**
267
     * Get / set the URL fragment to be appended to URLs.
268
     *
269
     * @param  string|null  $fragment
270
     * @return $this|string|null
271
     */
272
    public function fragment($fragment = null)
273
    {
274
        if (is_null($fragment)) {
275
            return $this->fragment;
276
        }
277
 
278
        $this->fragment = $fragment;
279
 
280
        return $this;
281
    }
282
 
283
    /**
284
     * Add a set of query string values to the paginator.
285
     *
286
     * @param  array|string|null  $key
287
     * @param  string|null  $value
288
     * @return $this
289
     */
290
    public function appends($key, $value = null)
291
    {
292
        if (is_null($key)) {
293
            return $this;
294
        }
295
 
296
        if (is_array($key)) {
297
            return $this->appendArray($key);
298
        }
299
 
300
        return $this->addQuery($key, $value);
301
    }
302
 
303
    /**
304
     * Add an array of query string values.
305
     *
306
     * @param  array  $keys
307
     * @return $this
308
     */
309
    protected function appendArray(array $keys)
310
    {
311
        foreach ($keys as $key => $value) {
312
            $this->addQuery($key, $value);
313
        }
314
 
315
        return $this;
316
    }
317
 
318
    /**
319
     * Add all current query string values to the paginator.
320
     *
321
     * @return $this
322
     */
323
    public function withQueryString()
324
    {
325
        if (! is_null($query = Paginator::resolveQueryString())) {
326
            return $this->appends($query);
327
        }
328
 
329
        return $this;
330
    }
331
 
332
    /**
333
     * Add a query string value to the paginator.
334
     *
335
     * @param  string  $key
336
     * @param  string  $value
337
     * @return $this
338
     */
339
    protected function addQuery($key, $value)
340
    {
341
        if ($key !== $this->cursorName) {
342
            $this->query[$key] = $value;
343
        }
344
 
345
        return $this;
346
    }
347
 
348
    /**
349
     * Build the full fragment portion of a URL.
350
     *
351
     * @return string
352
     */
353
    protected function buildFragment()
354
    {
355
        return $this->fragment ? '#'.$this->fragment : '';
356
    }
357
 
358
    /**
359
     * Load a set of relationships onto the mixed relationship collection.
360
     *
361
     * @param  string  $relation
362
     * @param  array  $relations
363
     * @return $this
364
     */
365
    public function loadMorph($relation, $relations)
366
    {
367
        $this->getCollection()->loadMorph($relation, $relations);
368
 
369
        return $this;
370
    }
371
 
372
    /**
373
     * Load a set of relationship counts onto the mixed relationship collection.
374
     *
375
     * @param  string  $relation
376
     * @param  array  $relations
377
     * @return $this
378
     */
379
    public function loadMorphCount($relation, $relations)
380
    {
381
        $this->getCollection()->loadMorphCount($relation, $relations);
382
 
383
        return $this;
384
    }
385
 
386
    /**
387
     * Get the slice of items being paginated.
388
     *
389
     * @return array
390
     */
391
    public function items()
392
    {
393
        return $this->items->all();
394
    }
395
 
396
    /**
397
     * Transform each item in the slice of items using a callback.
398
     *
399
     * @param  callable  $callback
400
     * @return $this
401
     */
402
    public function through(callable $callback)
403
    {
404
        $this->items->transform($callback);
405
 
406
        return $this;
407
    }
408
 
409
    /**
410
     * Get the number of items shown per page.
411
     *
412
     * @return int
413
     */
414
    public function perPage()
415
    {
416
        return $this->perPage;
417
    }
418
 
419
    /**
420
     * Get the current cursor being paginated.
421
     *
422
     * @return \Illuminate\Pagination\Cursor|null
423
     */
424
    public function cursor()
425
    {
426
        return $this->cursor;
427
    }
428
 
429
    /**
430
     * Get the query string variable used to store the cursor.
431
     *
432
     * @return string
433
     */
434
    public function getCursorName()
435
    {
436
        return $this->cursorName;
437
    }
438
 
439
    /**
440
     * Set the query string variable used to store the cursor.
441
     *
442
     * @param  string  $name
443
     * @return $this
444
     */
445
    public function setCursorName($name)
446
    {
447
        $this->cursorName = $name;
448
 
449
        return $this;
450
    }
451
 
452
    /**
453
     * Set the base path to assign to all URLs.
454
     *
455
     * @param  string  $path
456
     * @return $this
457
     */
458
    public function withPath($path)
459
    {
460
        return $this->setPath($path);
461
    }
462
 
463
    /**
464
     * Set the base path to assign to all URLs.
465
     *
466
     * @param  string  $path
467
     * @return $this
468
     */
469
    public function setPath($path)
470
    {
471
        $this->path = $path;
472
 
473
        return $this;
474
    }
475
 
476
    /**
477
     * Get the base path for paginator generated URLs.
478
     *
479
     * @return string|null
480
     */
481
    public function path()
482
    {
483
        return $this->path;
484
    }
485
 
486
    /**
487
     * Resolve the current cursor or return the default value.
488
     *
489
     * @param  string  $cursorName
490
     * @return \Illuminate\Pagination\Cursor|null
491
     */
492
    public static function resolveCurrentCursor($cursorName = 'cursor', $default = null)
493
    {
494
        if (isset(static::$currentCursorResolver)) {
495
            return call_user_func(static::$currentCursorResolver, $cursorName);
496
        }
497
 
498
        return $default;
499
    }
500
 
501
    /**
502
     * Set the current cursor resolver callback.
503
     *
504
     * @param  \Closure  $resolver
505
     * @return void
506
     */
507
    public static function currentCursorResolver(Closure $resolver)
508
    {
509
        static::$currentCursorResolver = $resolver;
510
    }
511
 
512
    /**
513
     * Get an instance of the view factory from the resolver.
514
     *
515
     * @return \Illuminate\Contracts\View\Factory
516
     */
517
    public static function viewFactory()
518
    {
519
        return Paginator::viewFactory();
520
    }
521
 
522
    /**
523
     * Get an iterator for the items.
524
     *
525
     * @return \ArrayIterator
526
     */
527
    public function getIterator(): Traversable
528
    {
529
        return $this->items->getIterator();
530
    }
531
 
532
    /**
533
     * Determine if the list of items is empty.
534
     *
535
     * @return bool
536
     */
537
    public function isEmpty()
538
    {
539
        return $this->items->isEmpty();
540
    }
541
 
542
    /**
543
     * Determine if the list of items is not empty.
544
     *
545
     * @return bool
546
     */
547
    public function isNotEmpty()
548
    {
549
        return $this->items->isNotEmpty();
550
    }
551
 
552
    /**
553
     * Get the number of items for the current page.
554
     *
555
     * @return int
556
     */
557
    public function count(): int
558
    {
559
        return $this->items->count();
560
    }
561
 
562
    /**
563
     * Get the paginator's underlying collection.
564
     *
565
     * @return \Illuminate\Support\Collection
566
     */
567
    public function getCollection()
568
    {
569
        return $this->items;
570
    }
571
 
572
    /**
573
     * Set the paginator's underlying collection.
574
     *
575
     * @param  \Illuminate\Support\Collection  $collection
576
     * @return $this
577
     */
578
    public function setCollection(Collection $collection)
579
    {
580
        $this->items = $collection;
581
 
582
        return $this;
583
    }
584
 
585
    /**
586
     * Get the paginator options.
587
     *
588
     * @return array
589
     */
590
    public function getOptions()
591
    {
592
        return $this->options;
593
    }
594
 
595
    /**
596
     * Determine if the given item exists.
597
     *
598
     * @param  mixed  $key
599
     * @return bool
600
     */
601
    public function offsetExists($key): bool
602
    {
603
        return $this->items->has($key);
604
    }
605
 
606
    /**
607
     * Get the item at the given offset.
608
     *
609
     * @param  mixed  $key
610
     * @return mixed
611
     */
612
    public function offsetGet($key): mixed
613
    {
614
        return $this->items->get($key);
615
    }
616
 
617
    /**
618
     * Set the item at the given offset.
619
     *
620
     * @param  mixed  $key
621
     * @param  mixed  $value
622
     * @return void
623
     */
624
    public function offsetSet($key, $value): void
625
    {
626
        $this->items->put($key, $value);
627
    }
628
 
629
    /**
630
     * Unset the item at the given key.
631
     *
632
     * @param  mixed  $key
633
     * @return void
634
     */
635
    public function offsetUnset($key): void
636
    {
637
        $this->items->forget($key);
638
    }
639
 
640
    /**
641
     * Render the contents of the paginator to HTML.
642
     *
643
     * @return string
644
     */
645
    public function toHtml()
646
    {
647
        return (string) $this->render();
648
    }
649
 
650
    /**
651
     * Make dynamic calls into the collection.
652
     *
653
     * @param  string  $method
654
     * @param  array  $parameters
655
     * @return mixed
656
     */
657
    public function __call($method, $parameters)
658
    {
659
        return $this->forwardCallTo($this->getCollection(), $method, $parameters);
660
    }
661
 
662
    /**
663
     * Render the contents of the paginator when casting to a string.
664
     *
665
     * @return string
666
     */
667
    public function __toString()
668
    {
669
        return (string) $this->render();
670
    }
671
}