February 15, 2017 • 2 minute read
Reducing the number of links in Laravel's default paginator template
Laravel's pagination features make it really simple to paginate your database queries and provide your users with links to navigate between pages of results. By default, these links are styled using standard Bootstrap classes. If you want to change the template used for the pagination links, you can run the following artisan command:
$ php artisan vendor:publish --tag=laravel-pagination
This will create a new vendor
subdirectory in your views
folder, and inside here you'll find the standard pagination view under pagination/default.blade.php
.
This is great, but by default Laravel will insert three dots (...) between pages when you have a large number of pages. This is much better than printing out links for every single page, but the number of links displayed is still quite high and will still typically lead to wrapping on mobile devices.
At first glance, customising this may seem difficult, as the pagination template loops over an $elements
array that is defined in Illuminate\Pagination\LengthAwarePaginator
. This uses a class named Illuminate\Pagination\UrlWindow
to construct arrays of links and the three dots items as appropriate. Although the get
method used here has an argument onEachSide
that should allow you to configure how many links show either side of the dots, there is no straightforward way of hooking into this without creating your own pagination classes.
Fortunately, you can sidestep doing this if you're happy enough adding some logic to the pagination template to determine when to show the three dots. The code below will show no more than 2 pages either side of the current page, and will show three dots as well as the first page and last page links as appropriate. I've applied a hidden-xs
class to the three dots and first page/last page links to ensure that the pagination links appear on a single line as much as possible on mobile.
@if ($paginator->hasPages())
<ul class="pagination pagination">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<li class="disabled"><span>«</span></li>
@else
<li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">«</a></li>
@endif
@if($paginator->currentPage() > 3)
<li class="hidden-xs"><a href="{{ $paginator->url(1) }}">1</a></li>
@endif
@if($paginator->currentPage() > 4)
<li class="disabled hidden-xs"><span>...</span></li>
@endif
@foreach(range(1, $paginator->lastPage()) as $i)
@if($i >= $paginator->currentPage() - 2 && $i <= $paginator->currentPage() + 2)
@if ($i == $paginator->currentPage())
<li class="active"><span>{{ $i }}</span></li>
@else
<li><a href="{{ $paginator->url($i) }}">{{ $i }}</a></li>
@endif
@endif
@endforeach
@if($paginator->currentPage() < $paginator->lastPage() - 3)
<li class="disabled hidden-xs"><span>...</span></li>
@endif
@if($paginator->currentPage() < $paginator->lastPage() - 2)
<li class="hidden-xs"><a href="{{ $paginator->url($paginator->lastPage()) }}">{{ $paginator->lastPage() }}</a></li>
@endif
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<li><a href="{{ $paginator->nextPageUrl() }}" rel="next">»</a></li>
@else
<li class="disabled"><span>»</span></li>
@endif
</ul>
@endif
Note, if you have hundreds of pages and need to support mobile devices with narrow widths, such as the iPhone 5S or below, you may want to add a pagination-sm
class on to the main <div>
to avoid wrapping. Alternatively, you could resort to just showing the previous, next and current page links on mobile.