Completed
Push — master ( 57bd68...8734c9 )
by Freek
14:15 queued 12:15
created

PaginateRoute::renderRelLinks()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 21
rs 9.0534
cc 4
eloc 13
nc 4
nop 2
1
<?php
2
3
namespace Spatie\PaginateRoute;
4
5
use Illuminate\Routing\Router;
6
use Illuminate\Translation\Translator;
7
use Illuminate\Contracts\Pagination\Paginator;
8
use Illuminate\Contracts\Routing\UrlGenerator;
9
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
10
11
class PaginateRoute
12
{
13
    /**
14
     * @var \Illuminate\Translation\Translator
15
     */
16
    protected $translator;
17
18
    /**
19
     * @var \Illuminate\Routing\Router
20
     */
21
    protected $router;
22
23
    /**
24
     * @var \Illuminate\Contracts\Routing\UrlGenerator
25
     */
26
    protected $urlGenerator;
27
28
    /**
29
     * @var string
30
     */
31
    protected $pageKeyword;
32
33
    /**
34
     * @param \Illuminate\Translation\Translator         $translator
35
     * @param \Illuminate\Routing\Router                 $router
36
     * @param \Illuminate\Contracts\Routing\UrlGenerator $urlGenerator
37
     */
38
    public function __construct(Translator $translator, Router $router, UrlGenerator $urlGenerator)
39
    {
40
        $this->translator = $translator;
41
        $this->router = $router;
42
        $this->urlGenerator = $urlGenerator;
43
44
        // Unfortunately we can't do this in the service provider since routes are booted first
45
        $this->translator->addNamespace('paginateroute', __DIR__.'/../resources/lang');
46
47
        $this->pageKeyword = $this->translator->get('paginateroute::paginateroute.page');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->translator->get('...e::paginateroute.page') can also be of type array. However, the property $pageKeyword is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
48
    }
49
50
    /**
51
     * Return the current page.
52
     *
53
     * @return int
54
     */
55
    public function currentPage()
56
    {
57
        $currentRoute = $this->router->getCurrentRoute();
58
59
        if (! $currentRoute) {
60
            return 1;
61
        }
62
63
        $query = $currentRoute->parameter('pageQuery');
64
65
        return (int) str_replace($this->pageKeyword.'/', '', $query) ?: 1;
66
    }
67
68
    /**
69
     * Check if the given page is the current page.
70
     *
71
     * @param int $page
72
     *
73
     * @return bool
74
     */
75
    public function isCurrentPage($page)
76
    {
77
        return $this->currentPage() === $page;
78
    }
79
80
    /**
81
     * Get the next page number.
82
     *
83
     * @param \Illuminate\Contracts\Pagination\Paginator $paginator
84
     *
85
     * @return string|null
86
     */
87
    public function nextPage(Paginator $paginator)
88
    {
89
        if (! $paginator->hasMorePages()) {
90
            return;
91
        }
92
93
        return $this->currentPage() + 1;
94
    }
95
96
    /**
97
     * Determine wether there is a next page.
98
     *
99
     * @param \Illuminate\Contracts\Pagination\Paginator $paginator
100
     *
101
     * @return bool
102
     */
103
    public function hasNextPage(Paginator $paginator)
104
    {
105
        return $this->nextPage($paginator) !== null;
106
    }
107
108
    /**
109
     * Get the next page URL.
110
     *
111
     * @param \Illuminate\Contracts\Pagination\Paginator $paginator
112
     *
113
     * @return string|null
114
     */
115
    public function nextPageUrl(Paginator $paginator)
116
    {
117
        $nextPage = $this->nextPage($paginator);
118
119
        if ($nextPage === null) {
120
            return;
121
        }
122
123
        return $this->pageUrl($nextPage);
124
    }
125
126
    /**
127
     * Get the previous page number.
128
     *
129
     * @return string|null
130
     */
131
    public function previousPage()
132
    {
133
        if ($this->currentPage() <= 1) {
134
            return;
135
        }
136
137
        return $this->currentPage() - 1;
138
    }
139
140
    /**
141
     * Determine wether there is a previous page.
142
     *
143
     * @return bool
144
     */
145
    public function hasPreviousPage()
146
    {
147
        return $this->previousPage() !== null;
148
    }
149
150
    /**
151
     * Get the previous page URL.
152
     *
153
     * @param bool $full Return the full version of the URL in for the first page
154
     *                   Ex. /users/page/1 instead of /users
155
     *
156
     * @return string|null
157
     */
158
    public function previousPageUrl($full = false)
159
    {
160
        $previousPage = $this->previousPage();
161
162
        if ($previousPage === null) {
163
            return;
164
        }
165
166
        return $this->pageUrl($previousPage, $full);
167
    }
168
169
    /**
170
     * Get all urls in an array.
171
     *
172
     * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
173
     * @param bool                                                  $full      Return the full version of the URL in for the first page
174
     *                                                                         Ex. /users/page/1 instead of /users
175
     *
176
     * @return array
177
     */
178
    public function allUrls(LengthAwarePaginator $paginator, $full = false)
179
    {
180
        if (! $paginator->hasPages()) {
181
            return [];
182
        }
183
184
        $urls = [];
185
186
        for ($page = 1; $page <= $paginator->lastPage(); ++$page) {
187
            $urls[] = $this->pageUrl($page, $full);
188
        }
189
190
        return $urls;
191
    }
192
193
    /**
194
     * Render a plain html list with previous, next and all urls. The current page gets a current class on the list item.
195
     *
196
     * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
197
     * @param bool                                                  $full              Return the full version of the URL in for the first page
198
     *                                                                                 Ex. /users/page/1 instead of /users
199
     * @param string                                                $class             Include class on pagination list
200
     *                                                                                 Ex. <ul class="pagination">
201
     * @param bool                                                  $additionalLinks   Include prev and next links on pagination list
202
     *
203
     * @return string
204
     */
205
    public function renderPageList(LengthAwarePaginator $paginator, $full = false, $class = null, $additionalLinks = false)
206
    {
207
        $urls = $this->allUrls($paginator, $full);
208
209
        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...
210
            $class = " class=\"$class\"";
211
        }
212
213
        $listItems = "<ul{$class}>";
214
215
        if ($this->hasPreviousPage() && $additionalLinks) {
216
            $listItems .= "<li><a href=\"{$this->previousPageUrl()}\">&laquo;</a></li>";
217
        }
218
219
        foreach ($urls as $i => $url) {
220
            $pageNum = $i + 1;
221
            $css = '';
222
223
            if ($pageNum == $this->currentPage()) {
224
                $css = ' class="active"';
225
            }
226
227
            $listItems .= "<li{$css}><a href=\"{$url}\">{$pageNum}</a></li>";
228
        }
229
230
        if ($this->hasNextPage($paginator) && $additionalLinks) {
231
            $listItems .= "<li><a href=\"{$this->nextPageUrl($paginator)}\">&raquo;</a></li>";
232
        }
233
234
        $listItems .= '</ul>';
235
236
        return $listItems;
237
    }
238
239
    /**
240
     * Render html link tags for SEO indication of previous and next page.
241
     *
242
     * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
243
     * @param bool                                                  $full       Return the full version of the URL in for the first page
244
     *                                                                          Ex. /users/page/1 instead of /users
245
     *
246
     * @return string
247
     */
248
    public function renderRelLinks(LengthAwarePaginator $paginator, $full = false)
249
    {
250
        $urls = $this->allUrls($paginator, $full);
251
252
        $linkItems = '';
253
254
        foreach ($urls as $i => $url) {
255
            $pageNum = $i + 1;
256
257
            switch ($pageNum - $this->currentPage()) {
258
                case -1:
259
                    $linkItems .= "<link rel=\"prev\" href=\"{$url}\" />";
260
                    break;
261
                case 1:
262
                    $linkItems .= "<link rel=\"next\" href=\"{$url}\" />";
263
                    break;
264
            }
265
        }
266
267
        return $linkItems;
268
    }
269
270
    /**
271
     * @deprecated in favor of renderPageList.
272
     *
273
     * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
274
     * @param bool                                                  $full      Return the full version of the URL in for the first page
275
     *                                                                         Ex. /users/page/1 instead of /users
276
     *
277
     * @return string
278
     */
279
    public function renderHtml(LengthAwarePaginator $paginator, $full = false)
280
    {
281
        return $this->renderPageList($paginator, $full);
282
    }
283
284
    /**
285
     * Generate a page URL, based on the request's current URL.
286
     *
287
     * @param int  $page
288
     * @param bool $full Return the full version of the URL in for the first page
289
     *                   Ex. /users/page/1 instead of /users
290
     *
291
     * @return string
292
     */
293
    public function pageUrl($page, $full = false)
294
    {
295
        $currentPageUrl = $this->router->getCurrentRoute()->getUri();
296
297
        $url = $this->addPageQuery(str_replace('{pageQuery?}', '', $currentPageUrl), $page, $full);
298
299
        foreach ($this->router->getCurrentRoute()->bindParameters(app('request')) as $parameterName => $parameterValue) {
300
            $url = str_replace(['{'.$parameterName.'}', '{'.$parameterName.'?}'], $parameterValue, $url);
301
        }
302
303
        return $this->urlGenerator->to($url);
304
    }
305
306
    /**
307
     * Append the page query to a URL.
308
     *
309
     * @param string $url
310
     * @param int    $page
311
     * @param bool   $full Return the full version of the URL in for the first page
312
     *                     Ex. /users/page/1 instead of /users
313
     *
314
     * @return string
315
     */
316
    public function addPageQuery($url, $page, $full = false)
317
    {
318
        // If the first page's URL is requested and $full is set to false, there's nothing to be added.
319
        if ($page === 1 && ! $full) {
320
            return $url;
321
        }
322
323
        return trim($url, '/')."/{$this->pageKeyword}/{$page}";
324
    }
325
326
    /**
327
     * Register the Route::paginate macro.
328
     */
329
    public function registerMacros()
330
    {
331
        $pageKeyword = $this->pageKeyword;
332
        $router = $this->router;
333
334
        $router->macro('paginate', function ($uri, $action) use ($pageKeyword, $router) {
335
            $route = null;
336
337
            $router->group(
338
                ['middleware' => 'Spatie\PaginateRoute\SetPageMiddleware'],
339
                function () use ($pageKeyword, $router, $uri, $action, &$route) {
340
                    $route = $router->get($uri.'/{pageQuery?}', $action)->where('pageQuery', $pageKeyword.'/[0-9]+');
341
                });
342
343
            return $route;
344
        });
345
    }
346
}
347