Completed
Push — master ( 9320fe...c48500 )
by Vipul
04:22 queued 03:10
created

PaginateRoute::renderHtml()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
namespace Vipertecpro\PaginateRoute;
4
5
use Illuminate\Pagination\LengthAwarePaginator;
6
use Illuminate\Contracts\Pagination\Paginator;
7
use Illuminate\Contracts\Routing\UrlGenerator;
8
use Illuminate\Routing\RouteParameterBinder;
9
use Illuminate\Routing\Router;
10
use Illuminate\Support\Facades\Request;
11
use Illuminate\Translation\Translator;
12
13
class PaginateRoute
14
{
15
    /**
16
     * @var Translator
17
     */
18
    protected $translator;
19
20
    /**
21
     * @var Router
22
     */
23
    protected $router;
24
25
    /**
26
     * @var UrlGenerator
27
     */
28
    protected $urlGenerator;
29
30
    /**
31
     * @var string|array
32
     */
33
    protected $pageKeyword;
34
35
    /**
36
     * @param Translator $translator
37
     * @param Router $router
38
     * @param UrlGenerator $urlGenerator
39
     */
40
    public function __construct(Translator $translator, Router $router, UrlGenerator $urlGenerator)
41
    {
42
        $this->translator = $translator;
43
        $this->router = $router;
44
        $this->urlGenerator = $urlGenerator;
45
46
        // Unfortunately we can't do this in the service provider since routes are booted first
47
        $this->translator->addNamespace('paginateroute', __DIR__.'/../resources/lang');
48
49
        $this->pageKeyword = $this->translator->get('paginateroute::paginateroute.page');
50
    }
51
52
    /**
53
     * Return the current page.
54
     *
55
     * @return int
56
     */
57
    public function currentPage()
58
    {
59
        $currentRoute = $this->router->getCurrentRoute();
60
61
        if (! $currentRoute) {
62
            return 1;
63
        }
64
65
        $query = $currentRoute->parameter('pageQuery');
66
67
        return (int) str_replace($this->pageKeyword.'/', '', $query) ?: 1;
68
    }
69
70
    /**
71
     * Check if the given page is the current page.
72
     *
73
     * @param int $page
74
     *
75
     * @return bool
76
     */
77
    public function isCurrentPage($page): bool
78
    {
79
        return $this->currentPage() === $page;
80
    }
81
82
    /**
83
     * Get the next page number.
84
     *
85
     * @param Paginator $paginator
86
     *
87
     * @return int|void
88
     */
89
    public function nextPage(Paginator $paginator)
90
    {
91
        if (! $paginator->hasMorePages()) {
92
            return;
93
        }
94
95
        return $this->currentPage() + 1;
96
    }
97
98
    /**
99
     * Determine wether there is a next page.
100
     *
101
     * @param Paginator $paginator
102
     *
103
     * @return bool
104
     */
105
    public function hasNextPage(Paginator $paginator): bool
106
    {
107
        return $this->nextPage($paginator) !== null;
108
    }
109
110
    /**
111
     * Get the next page URL.
112
     *
113
     * @param Paginator $paginator
114
     *
115
     * @return string|void
116
     */
117
    public function nextPageUrl(Paginator $paginator)
118
    {
119
        $nextPage = $this->nextPage($paginator);
120
121
        if ($nextPage === null) {
122
            return;
123
        }
124
125
        return $this->pageUrl($nextPage);
126
    }
127
128
    /**
129
     * Get the previous page number.
130
     *
131
     * @return int|void|null
132
     */
133
    public function previousPage()
134
    {
135
        if ($this->currentPage() <= 1) {
136
            return;
137
        }
138
139
        return $this->currentPage() - 1;
140
    }
141
142
    /**
143
     * Determine wether there is a previous page.
144
     *
145
     * @return bool
146
     */
147
    public function hasPreviousPage(): bool
148
    {
149
        return $this->previousPage() !== null;
150
    }
151
152
    /**
153
     * Get the previous page URL.
154
     *
155
     * @param bool $full Return the full version of the URL in for the first page
156
     *                   Ex. /users/page/1 instead of /users
157
     *
158
     * @return string|void|null
159
     */
160
    public function previousPageUrl($full = false): ?string
161
    {
162
        $previousPage = $this->previousPage();
163
164
        if ($previousPage === null) {
165
            return;
166
        }
167
168
        return $this->pageUrl($previousPage, $full);
169
    }
170
171
    /**
172
     * Get all urls in an array.
173
     *
174
     * @param LengthAwarePaginator $paginator
175
     * @param bool                                                  $full      Return the full version of the URL in for the first page
176
     *                                                                         Ex. /users/page/1 instead of /users
177
     *
178
     * @return array
179
     */
180
    public function allUrls(LengthAwarePaginator $paginator, $full = false): array
181
    {
182
        if (! $paginator->hasPages()) {
183
            return [];
184
        }
185
186
        $urls = [];
187
        $left = $this->getLeftPoint($paginator);
188
        $right = $this->getRightPoint($paginator);
189
        for ($page = $left; $page <= $right; $page++) {
190
            $urls[$page] = $this->pageUrl($page, $full);
191
        }
192
193
        return $urls;
194
    }
195
196
    /**
197
     * Get the left most point in the pagination element.
198
     *
199
     * @param LengthAwarePaginator $paginator
200
     * @return int
201
     */
202
    public function getLeftPoint(LengthAwarePaginator $paginator): int
203
    {
204
        $side = $paginator->onEachSide;
205
        $current = $paginator->currentPage();
206
        $last = $paginator->lastPage();
207
208
        if (! empty($side)) {
209
            $x = $current + $side;
210
            $offset = $x >= $last ? $x - $last : 0;
211
            $left = $current - $side - $offset;
212
        }
213
        if(!isset($left) || $left < 1){
214
            return 1;
215
        }
216
        return $left;
217
    }
218
219
    /**
220
     * Get the right or last point of the pagination element.
221
     *
222
     * @param LengthAwarePaginator $paginator
223
     * @return int
224
     */
225
    public function getRightPoint(LengthAwarePaginator $paginator): int
226
    {
227
        $side = $paginator->onEachSide;
228
        $current = $paginator->currentPage();
229
        $last = $paginator->lastPage();
230
231
        if (! empty($side)) {
232
            $offset = $current <= $side ? $side - $current + 1 : 0;
233
            $right = $current + $side + $offset;
234
        }
235
        if(!isset($right) || $right > $last){
236
            return $last;
237
        }
238
        return $right;
239
    }
240
241
    /**
242
     * Render a plain html list with previous, next and all urls. The current page gets a current class on the list item.
243
     *
244
     * @param LengthAwarePaginator $paginator
245
     * @param bool                                                  $full              Return the full version of the URL in for the first page
246
     *                                                                                 Ex. /users/page/1 instead of /users
247
     * @param string                                                $class             Include class on pagination list
248
     *                                                                                 Ex. <ul class="pagination">
249
     * @param bool                                                  $additionalLinks   Include prev and next links on pagination list
250
     *
251
     * @return string
252
     */
253
254
    public function renderPageList(LengthAwarePaginator $paginator, $full = false, $class = null, $additionalLinks = false)
255
    {
256
        $urls = $this->allUrls($paginator, $full);
257
        if ($class !== null) {
258
            $class = " class=\"$class\"";
259
        }
260
        $listItems = "<ul{$class}>";
261
        if ($this->hasPreviousPage() && $additionalLinks) {
262
            $listItems .= "<li class='page-item'> <a class='page-link' href=\"{$this->previousPageUrl()}\">&laquo;</a></li>";
263
        }
264
        foreach ($urls as $i => $url) {
265
            $pageNum = $i;
266
            $css = ' class="page-item"';
267
            $link = "<a class='page-link' href=\"{$url}\">{$pageNum}</a>";
268
            if ($pageNum === $this->currentPage()) {
269
                $css = ' class="page-item active"';
270
                $link = "<span class='page-link' href=\"{$url}\">{$pageNum}</span>";
271
            }
272
            $listItems .= "<li{$css}>$link</li>";
273
        }
274
        if ($this->hasNextPage($paginator) && $additionalLinks) {
275
            $listItems .= "<li class='page-item'> <a class='page-link' href=\"{$this->nextPageUrl($paginator)}\">&raquo;</a></li>";
276
        }
277
        $listItems .= '</ul>';
278
        return $listItems;
279
    }
280
    /**
281
     * Render html link tags for SEO indication of previous and next page.
282
     *
283
     * @param LengthAwarePaginator $paginator
284
     * @param bool                                                  $full       Return the full version of the URL in for the first page
285
     *                                                                          Ex. /users/page/1 instead of /users
286
     *
287
     * @return string
288
     */
289
    public function renderRelLinks(LengthAwarePaginator $paginator, $full = false): string
290
    {
291
        $urls = $this->allUrls($paginator, $full);
292
293
        $linkItems = '';
294
295
        foreach ($urls as $i => $url) {
296
            $pageNum = $i + 1;
297
298
            switch ($pageNum - $this->currentPage()) {
299
                case -1:
300
                    $linkItems .= "<link rel=\"prev\" href=\"{$url}\" />";
301
                    break;
302
                case 1:
303
                    $linkItems .= "<link rel=\"next\" href=\"{$url}\" />";
304
                    break;
305
            }
306
        }
307
308
        return $linkItems;
309
    }
310
311
    /**
312
     * @param LengthAwarePaginator $paginator
313
     * @param bool $full      Return the full version of the URL in for the first page
314
     *                                                                         Ex. /users/page/1 instead of /users
315
     *
316
     * @return string
317
     *@deprecated in favor of renderPageList.
318
     *
319
     */
320
    public function renderHtml(LengthAwarePaginator $paginator, $full = false): string
321
    {
322
        return $this->renderPageList($paginator, $full);
323
    }
324
325
    /**
326
     * Generate a page URL, based on the request's current URL.
327
     *
328
     * @param int  $page
329
     * @param bool $full Return the full version of the URL in for the first page
330
     *                   Ex. /users/page/1 instead of /users
331
     *
332
     * @return string
333
     */
334
    public function pageUrl($page, $full = false): string
335
    {
336
        $currentPageUrl = $this->router->getCurrentRoute()->uri();
337
338
        $url = $this->addPageQuery(str_replace('{pageQuery?}', '', $currentPageUrl), $page, $full);
339
340
        foreach ((new RouteParameterBinder($this->router->getCurrentRoute()))->parameters(app('request')) as $parameterName => $parameterValue) {
0 ignored issues
show
Bug introduced by
It seems like $this->router->getCurrentRoute() can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
341
            $url = str_replace(['{'.$parameterName.'}', '{'.$parameterName.'?}'], $parameterValue, $url);
342
        }
343
344
        $query = Request::getQueryString();
345
346
        $query = $query
347
            ? '?'.$query
348
            : '';
349
350
        return $this->urlGenerator->to($url).$query;
351
    }
352
353
    /**
354
     * Append the page query to a URL.
355
     *
356
     * @param string $url
357
     * @param int    $page
358
     * @param bool   $full Return the full version of the URL in for the first page
359
     *                     Ex. /users/page/1 instead of /users
360
     *
361
     * @return string
362
     */
363
    public function addPageQuery($url, $page, $full = false): string
364
    {
365
        // If the first page's URL is requested and $full is set to false, there's nothing to be added.
366
        if ($page === 1 && ! $full) {
367
            return $url;
368
        }
369
370
        return trim($url, '/')."/{$this->pageKeyword}/{$page}";
371
    }
372
373
    /**
374
     * Register the Route::paginate macro.
375
     */
376
    public function registerMacros(): void
377
    {
378
        $pageKeyword = $this->pageKeyword;
379
        $router = $this->router;
380
381
        $router->macro('paginate', function ($uri, $action) use ($pageKeyword, $router) {
382
            $route = null;
383
384
            $router->group(
385
                ['middleware' => 'Vipertecpro\PaginateRoute\SetPageMiddleware'],
386
                function () use ($pageKeyword, $router, $uri, $action, &$route) {
387
                    $route = $router->get($uri.'/{pageQuery?}', $action)->where('pageQuery', $pageKeyword.'/[0-9]+');
388
                });
389
390
            return $route;
391
        });
392
    }
393
}
394