32
votes

Any easy way to limit how many links are shown with Laravels pagination?

Currently it shows 13 links at most (Prev, 1 2 3 4 5 7 8 .. 78 79 next)

This however is too much for mobile devices and becomes a two line navigation... is there any way to set the links to e.g. only show 10?

I have messed around with the pagination presenter but nothing actually seemed to work.

Thanks

12

12 Answers

2
votes

The old way of defining a custom presenter doesn't work with Laravel 5.3+, the number of links shown seems to be hard-coded in the $onEachSide parameter of Illuminate/Pagination/UrlWindow::make():

public static function make(PaginatorContract $paginator, $onEachSide = 3)

I ended up just writing my own render() function, stealing some code from LengthAwarePaginator

/**
     * Stole come code from LengthAwarePaginator::render() and ::elements() to allow for a smaller UrlWindow
     *
     * @param LengthAwarePaginator $paginator
     * @param int $onEachSide
     * @return string
     */
    public static function render(LengthAwarePaginator $paginator, $onEachSide = 2)
    {
        $window = UrlWindow::make($paginator, $onEachSide);

        $elements = array_filter([
            $window['first'],
            is_array($window['slider']) ? '...' : null,
            $window['slider'],
            is_array($window['last']) ? '...' : null,
            $window['last'],
        ]);

        return LengthAwarePaginator::viewFactory()->make(LengthAwarePaginator::$defaultView, [
            'paginator' => $paginator,
            'elements' => $elements,
        ])->render();
    }
}

We use Twig, so I registered this as a Twig filter, I imagine something similar could be done for Blade.

15
votes

I used css to limit the links that I allowed. Really simple.. this can be extended to show any number of pages, at any number of breakpoints

@media screen and ( max-width: 400px ){

    li.page-item {

        display: none;
    }

    .page-item:first-child,
    .page-item:nth-child( 2 ),
    .page-item:nth-last-child( 2 ),
    .page-item:last-child,
    .page-item.active,
    .page-item.disabled {

        display: block;
    }
}

this specific implementation allows the arrows, the '...', the first page, active page and last page

14
votes

Now Laravel 5.7 has a new pagination method to customize the number of links on each side of the paginator. Thanks to the new method you no longer need a custom pagination view in some cases. Here’s the API you can use to define the link count on each side of the current page:

User::paginate(10)->onEachSide(2);

write that code on your controller.

you can see more detail at https://laravel-news.com/laravel-5-7-pagination-link-customizations

7
votes

For Laravel 5.6+

Publish vendor template:

php artisan vendor:publish --tag=laravel-pagination

Edit bootstrap-4.blade.php as following:

@if ($paginator->hasPages())
<ul class="pagination" role="navigation">
    {{-- Previous Page Link --}}
    @if ($paginator->onFirstPage())
        <li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
            <span class="page-link" aria-hidden="true">&lsaquo;</span>
        </li>
    @else
        <li class="page-item">
            <a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">&lsaquo;</a>
        </li>
    @endif

    <?php
        $start = $paginator->currentPage() - 2; // show 3 pagination links before current
        $end = $paginator->currentPage() + 2; // show 3 pagination links after current
        if($start < 1) {
            $start = 1; // reset start to 1
            $end += 1;
        } 
        if($end >= $paginator->lastPage() ) $end = $paginator->lastPage(); // reset end to last page
    ?>

    @if($start > 1)
        <li class="page-item">
            <a class="page-link" href="{{ $paginator->url(1) }}">{{1}}</a>
        </li>
        @if($paginator->currentPage() != 4)
            {{-- "Three Dots" Separator --}}
            <li class="page-item disabled" aria-disabled="true"><span class="page-link">...</span></li>
        @endif
    @endif
        @for ($i = $start; $i <= $end; $i++)
            <li class="page-item {{ ($paginator->currentPage() == $i) ? ' active' : '' }}">
                <a class="page-link" href="{{ $paginator->url($i) }}">{{$i}}</a>
            </li>
        @endfor
    @if($end < $paginator->lastPage())
        @if($paginator->currentPage() + 3 != $paginator->lastPage())
            {{-- "Three Dots" Separator --}}
            <li class="page-item disabled" aria-disabled="true"><span class="page-link">...</span></li>
        @endif
        <li class="page-item">
            <a class="page-link" href="{{ $paginator->url($paginator->lastPage()) }}">{{$paginator->lastPage()}}</a>
        </li>
    @endif

    {{-- Next Page Link --}}
    @if ($paginator->hasMorePages())
        <li class="page-item">
            <a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">&rsaquo;</a>
        </li>
    @else
        <li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
            <span class="page-link" aria-hidden="true">&rsaquo;</span>
        </li>
    @endif
</ul>
@endif

This example handle in a correct way new CSS classes, active link, three dots (correctly, not 1..2 3 4) and is it customizable (number of pages that you want to show).

Example

Three dots handled correctly

5
votes

I had created a new custom presenter to show only 10 links. It involves 3 steps:

Create your own custom presenter

use Illuminate\Pagination\BootstrapPresenter;

class CustomPresenter extends BootstrapPresenter{
    protected function getPageSlider()
    {
        // Changing the original value from 6 to 3 to reduce the link count
        $window = 3;

        // If the current page is very close to the beginning of the page range, we will
        // just render the beginning of the page range, followed by the last 2 of the
        // links in this list, since we will not have room to create a full slider.
        if ($this->currentPage <= $window)
        {
            $ending = $this->getFinish();

            return $this->getPageRange(1, $window + 2).$ending;
        }

        // If the current page is close to the ending of the page range we will just get
        // this first couple pages, followed by a larger window of these ending pages
        // since we're too close to the end of the list to create a full on slider.
        elseif ($this->currentPage >= $this->lastPage - $window)
        {
            $start = $this->lastPage - 8;

            $content = $this->getPageRange($start, $this->lastPage);

            return $this->getStart().$content;
        }

        // If we have enough room on both sides of the current page to build a slider we
        // will surround it with both the beginning and ending caps, with this window
        // of pages in the middle providing a Google style sliding paginator setup.
        else
        {
            $content = $this->getAdjacentRange();

            return $this->getStart().$content.$this->getFinish();
        }
    }

} 

Create your own pagination view (e.g. custom-paginator.php), place in your views folder

<ul class="pagination">
    <?php echo with(new CustomPresenter($paginator))->render(); ?>
</ul>

Update your app/config.view.php

'pagination' => 'custom-paginator',

By making the following changes, you will able to get a 10 links paginator.

Hope this help :D

4
votes

Create one file default.blade.php in new pagination folder in view folder with below code. That means you have override core pagination file with our new pagination file. That will set some limit in pagination link.

@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><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><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

Then Where you want to get pagination then you have to call laravel pagination function with this view file parameter.

 $data->links('pagination.default')
4
votes

From laravel 8 docs. If needed, you may control how many additional links are displayed on each side of the current page using the onEachSide method in blade:

{{ $posts->onEachSide(1)->links() }}
3
votes

I know it's an old question but there is still no way to set in using function links() parameters. I made simple jQuery code, maybe it will help someone. It's much simplier than Zesky's answer. I don't mean better, just simplier :)

JavaScript:

(function($) {
    $('ul.pagination li.active')
        .prev().addClass('show-mobile')
        .prev().addClass('show-mobile');
    $('ul.pagination li.active')
        .next().addClass('show-mobile')
        .next().addClass('show-mobile');
    $('ul.pagination')
        .find('li:first-child, li:last-child, li.active')
        .addClass('show-mobile');
})(jQuery);

CSS:

@media (max-width: /* write what you need, for me it's 560px */) {
    ul.pagination li:not(.show-mobile) {
        display: none;
    }
}

This code make visible only few li elements. Active, two before, two after, prev/next arrows. That make only 7 visible elements at most instead of 15.

1
votes

My table was too narrow so, I decided to show only first and the last li (The Next and Back arrows) of pagination with jQuery filter(). You can customize this further.

$('ul.pagination li').hide().filter(':lt(1), :nth-last-child(1)').show();

Make sure to add it before the end of body tag.

1
votes

It's an old question, but just thought I'd share how I've recently achieved reducing the number of pagination links in a Laravel 5.2 application:

Inside your ViewServiceProvider:

 \Illuminate\Pagination\AbstractPaginator::presenter(function($paginator) {
      return new \App\Pagination\BootstrapPresenter($paginator);
 });

App\Pagination\BootstrapPresenter.php

<?php namespace App\Pagination;

use Illuminate\Pagination\UrlWindow;
use Illuminate\Contracts\Pagination\Paginator as PaginatorContract;
use Illuminate\Pagination\BootstrapThreePresenter as LaravelBootstrapThreePresenter;

class BootstrapPresenter extends LaravelBootstrapThreePresenter
{
    /**
     * Create a new Bootstrap presenter instance.
     *
     * @param  \Illuminate\Contracts\Pagination\Paginator  $paginator
     * @param  \Illuminate\Pagination\UrlWindow|null  $window
     * @return void
     */
    public function __construct(PaginatorContract $paginator, UrlWindow $window = null)
    {
        $this->paginator = $paginator;

        // Make the pagination window smaller (default is 3).
        $this->window = UrlWindow::make($paginator, 1);
    }
}

From:

enter image description here

To:

enter image description here

0
votes

There are many ways to go about it depending on exactly how you want it to behave.

For my needs, I wanted a quick solution to shorten it up so it doesn't break my mobile layouts.

Laravel 5.3

Edit this file: (or the one you're using in your paginator calls)

/vendor/pagination/bootstrap-4.blade.php

I add a single line to break out of the link rendering loop after 2 links.

{{-- Array Of Links --}}
    @if (is_array($element))
        @foreach ($element as $page => $url)
             @if ($page == $paginator->currentPage())
                 <li class="page-item active"><span class="page-link">{{ $page }}</span></li>
              @else
                  <li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
                  {{--Add the line below to limit rendered links--}}
                  <?php if($loop->count > 2) break; ?>
              @endif
         @endforeach
     @endif
0
votes

This is an older question, but none of the answers here are up to date with the latest Laravel documentation. For newer versions of Laravel (Laravel 5.6+), there is a somewhat easy fix to this problem, but it does take publishing the vendor pagination views, as per Laravel Documentation. Assuming you haven't already published or changed the default pagination view, the command you run is:

php artisan vendor:publish --tag=laravel-pagination

Once the views are published, you will find bootstrap-4.blade.php in the resources/views/vendor/pagination directory. You can edit this file and make the pagination links appear however you want. In order to reduce the number of links displayed by default, I simply used a little bit of inline php to set an index and limit the number of links to display, as shown below:

{{-- Array Of Links --}}
@if (is_array($element))
    <?php $index = 0; ?>
    @foreach ($element as $page => $url)
        @if($index<4)
            @if ($page == $paginator->currentPage())
                <li class="page-item active" aria-current="page"><span class="page-link">{{ $page }}</span></li>
            @else
                <li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
            @endif
        @endif
        <?php $index++ ?>
    @endforeach
@endif

Most of this code is in the default blade view for bootstrap-4.blade.php, but I added the $index=0, $index<4, $index++ code to LIMIT the number of links on the left side of the paginator to 4.

This is the correct way to handle this problem as per Laravel documentation, without editing composer vendor files, or trying to hack the system in some other way. I do realize that JR Lawhorne posted a similar answer, but it does not include the entire process of publishing the vendor file before posting. Hope this helps others.