Completed
Pull Request — master (#57)
by
unknown
01:42 queued 20s
created

PaginateRoute::allUrls()   B

Complexity

Conditions 6
Paths 11

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 8.7857
c 0
b 0
f 0
cc 6
nc 11
nop 2
1
<?php
2
3
namespace Spatie\PaginateRoute;
4
5
use Illuminate\Routing\Router;
6
use Illuminate\Translation\Translator;
7
use Illuminate\Support\Facades\Request;
8
use Illuminate\Routing\RouteParameterBinder;
9
use Illuminate\Contracts\Pagination\Paginator;
10
use Illuminate\Contracts\Routing\UrlGenerator;
11
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
12
13
class PaginateRoute
14
{
15
    /**
16
     * @var \Illuminate\Translation\Translator
17
     */
18
    protected $translator;
19
20
    /**
21
     * @var \Illuminate\Routing\Router
22
     */
23
    protected $router;
24
25
    /**
26
     * @var \Illuminate\Contracts\Routing\UrlGenerator
27
     */
28
    protected $urlGenerator;
29
30
    /**
31
     * @var string
32
     */
33
    protected $pageKeyword;
34
35
    /**
36
     * @param \Illuminate\Translation\Translator         $translator
37
     * @param \Illuminate\Routing\Router                 $router
38
     * @param \Illuminate\Contracts\Routing\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)
78
    {
79
        return $this->currentPage() === $page;
80
    }
81
82
    /**
83
     * Get the next page number.
84
     *
85
     * @param \Illuminate\Contracts\Pagination\Paginator $paginator
86
     *
87
     * @return string|null
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 \Illuminate\Contracts\Pagination\Paginator $paginator
102
     *
103
     * @return bool
104
     */
105
    public function hasNextPage(Paginator $paginator)
106
    {
107
        return $this->nextPage($paginator) !== null;
108
    }
109
110
    /**
111
     * Get the next page URL.
112
     *
113
     * @param \Illuminate\Contracts\Pagination\Paginator $paginator
114
     *
115
     * @return string|null
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 string|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()
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|null
159
     */
160
    public function previousPageUrl($full = false)
161
    {
162
        $previousPage = $this->previousPage();
163
164
        if ($previousPage === null) {
165
            return;
166
        }
167
168
        return $this->pageUrl($previousPage, $full);
169
    }
170
    /**
171
     * Get all urls in an array.
172
     *
173
     * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
174
     * @param bool                                                  $full      Return the full version of the URL in for the first page
175
     *                                                                         Ex. /users/page/1 instead of /users
176
     *
177
     * @return array
178
     */
179
    public function allUrls(LengthAwarePaginator $paginator, $full = false)
180
    {
181
        if (! $paginator->hasPages()) {
182
            return [];
183
        }
184
185
        $urls = [];
186
        $side = $paginator->onEachSide;
0 ignored issues
show
Bug introduced by
Accessing onEachSide on the interface Illuminate\Contracts\Pag...on\LengthAwarePaginator suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
187
        $current = $paginator->currentPage();
188
        $last = $paginator->lastPage();
189
190
        if (!empty($side)) {
191
            $total = $current + $side;
192
            $first = $current - $side;
193
            if ($first < 1) {
194
                $first = 1;
195
                $total += $side;
196
            }
197
            if ($total > $last) {
198
                $total = $last;
199
            }
200
        }
201
        else {
202
            $first = 1;
203
            $total = $last;
204
        }
205
        for ($page = $first; $page <= $total; $page++) {
206
            $urls[$page] = $this->pageUrl($page, $full);
207
        }
208
209
        return $urls;
210
    }
211
212
    /**
213
     * Render a plain html list with previous, next and all urls. The current page gets a current class on the list item.
214
     *
215
     * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
216
     * @param bool                                                  $full              Return the full version of the URL in for the first page
217
     *                                                                                 Ex. /users/page/1 instead of /users
218
     * @param string                                                $class             Include class on pagination list
219
     *                                                                                 Ex. <ul class="pagination">
220
     * @param bool                                                  $additionalLinks   Include prev and next links on pagination list
221
     *
222
     * @return string
223
     */
224
    public function renderPageList(LengthAwarePaginator $paginator, $full = false, $class = null, $additionalLinks = false)
225
    {
226
        $urls = $this->allUrls($paginator, $full);
227
228
        if ($class) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
229
            $class = " class=\"$class\"";
230
        }
231
232
        $listItems = "<ul{$class}>";
233
234
        if ($this->hasPreviousPage() && $additionalLinks) {
235
            $listItems .= "<li><a href=\"{$this->previousPageUrl()}\">&laquo;</a></li>";
236
        }
237
238
        foreach ($urls as $i => $url) {
239
            $pageNum = $i;
240
            $css = '';
241
242
            $link = "<a href=\"{$url}\">{$pageNum}</a>";
243
            if ($pageNum == $this->currentPage()) {
244
                $css = ' class="active"';
245
                $link = $pageNum;
246
            }
247
248
            $listItems .= "<li{$css}>$link</li>";
249
        }
250
251
        if ($this->hasNextPage($paginator) && $additionalLinks) {
252
            $listItems .= "<li><a href=\"{$this->nextPageUrl($paginator)}\">&raquo;</a></li>";
253
        }
254
255
        $listItems .= '</ul>';
256
257
        return $listItems;
258
    }
259
260
    /**
261
     * Render html link tags for SEO indication of previous and next page.
262
     *
263
     * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
264
     * @param bool                                                  $full       Return the full version of the URL in for the first page
265
     *                                                                          Ex. /users/page/1 instead of /users
266
     *
267
     * @return string
268
     */
269
    public function renderRelLinks(LengthAwarePaginator $paginator, $full = false)
270
    {
271
        $urls = $this->allUrls($paginator, $full);
272
273
        $linkItems = '';
274
275
        foreach ($urls as $i => $url) {
276
            $pageNum = $i + 1;
277
278
            switch ($pageNum - $this->currentPage()) {
279
                case -1:
280
                    $linkItems .= "<link rel=\"prev\" href=\"{$url}\" />";
281
                    break;
282
                case 1:
283
                    $linkItems .= "<link rel=\"next\" href=\"{$url}\" />";
284
                    break;
285
            }
286
        }
287
288
        return $linkItems;
289
    }
290
291
    /**
292
     * @deprecated in favor of renderPageList.
293
     *
294
     * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
295
     * @param bool                                                  $full      Return the full version of the URL in for the first page
296
     *                                                                         Ex. /users/page/1 instead of /users
297
     *
298
     * @return string
299
     */
300
    public function renderHtml(LengthAwarePaginator $paginator, $full = false)
301
    {
302
        return $this->renderPageList($paginator, $full);
303
    }
304
305
    /**
306
     * Generate a page URL, based on the request's current URL.
307
     *
308
     * @param int  $page
309
     * @param bool $full Return the full version of the URL in for the first page
310
     *                   Ex. /users/page/1 instead of /users
311
     *
312
     * @return string
313
     */
314
    public function pageUrl($page, $full = false)
315
    {
316
        $currentPageUrl = $this->router->getCurrentRoute()->uri();
317
318
        $url = $this->addPageQuery(str_replace('{pageQuery?}', '', $currentPageUrl), $page, $full);
319
320
        foreach ((new RouteParameterBinder($this->router->getCurrentRoute()))->parameters(app('request')) as $parameterName => $parameterValue) {
321
            $url = str_replace(['{'.$parameterName.'}', '{'.$parameterName.'?}'], $parameterValue, $url);
322
        }
323
324
        $query = Request::getQueryString();
325
326
        $query = $query
327
            ? '?'.$query
328
            : '';
329
330
        return $this->urlGenerator->to($url).$query;
331
    }
332
333
    /**
334
     * Append the page query to a URL.
335
     *
336
     * @param string $url
337
     * @param int    $page
338
     * @param bool   $full Return the full version of the URL in for the first page
339
     *                     Ex. /users/page/1 instead of /users
340
     *
341
     * @return string
342
     */
343
    public function addPageQuery($url, $page, $full = false)
344
    {
345
        // If the first page's URL is requested and $full is set to false, there's nothing to be added.
346
        if ($page === 1 && ! $full) {
347
            return $url;
348
        }
349
350
        return trim($url, '/')."/{$this->pageKeyword}/{$page}";
351
    }
352
353
    /**
354
     * Register the Route::paginate macro.
355
     */
356
    public function registerMacros()
357
    {
358
        $pageKeyword = $this->pageKeyword;
359
        $router = $this->router;
360
361
        $router->macro('paginate', function ($uri, $action) use ($pageKeyword, $router) {
362
            $route = null;
363
364
            $router->group(
365
                ['middleware' => 'Spatie\PaginateRoute\SetPageMiddleware'],
366
                function () use ($pageKeyword, $router, $uri, $action, &$route) {
367
                    $route = $router->get($uri.'/{pageQuery?}', $action)->where('pageQuery', $pageKeyword.'/[0-9]+');
368
                });
369
370
            return $route;
371
        });
372
    }
373
}
374