Contact Me
Blog

Pretty Pagination URLs with Laravel 5

It's entirely down to personal preference, but a common question is how to create "pretty" pagination URLs.

What I mean by that, is instead of some­thing like this:

example.com/blog?page=2

You end up with a URL which looks like this:

example.com/blog/page/2

Here’s how to do just that in Laravel 5, using this package. For the purposes of this post I’m assuming you already have a controller method which uses pagination; if you don’t then just create it as normal (the documentation is here if you need it), then come back.

Installing the Package

Installation is, as ever, done via Composer:

composer require spatie/laravel-paginateroute

The next step is to add the service provider in config/app.php:

'providers' => 
    'Spatie\\PaginateRoute\\PaginateRouteServiceProvider'
];

Now add the appropriate alias, also to config/app.php:

'aliases' => [
    ...
    'PaginateRoute' => 'Spatie\\PaginateRoute\\PaginateRouteFacade',
];

Finally, you’ll need to add the macro by modifying your route service provider (typically in app/Providers/RouteServiceProvider.php):

public function boot(Router $router) {
    parent::boot($router);
    PaginateRoute::registerMacros();
}

Usage

Once all that’s set up, you can configure a route using pretty pagination URL's using the paginate() macro. So, for example, suppose you currently have a route to a blog that looks like this:

Route::get('blog', 'Blog\\BlogController@index')->name( 'blog' );

Simply change it so that it looks like this:

Route::paginate('blog', [ 'as' => 'blog', 'uses' => 'Blog\\BlogController@index' ] );

I’m using named routes here, which are entirely optional. However it’s important to note that in this example I’m using an alternative approach to specifying the name of the route; this is because at the time of writing the paginate() macro doesn’t support chaining on the name() method.

As far as your controller goes, you don’t need to make any further changes; Eloquent’s paginate() method will “just work”, picking up the appropriate page number for you.

However, the one further change you’re going to have to make concerns the rendering of your pagination control so that the links follow the correct format.

The way in which Laravel renders pagination controls has changed a couple of times across different versions, but as you’re no doubt aware Laravel 5 provides the render() method. Instead of using this, we’re going to need to generate the markup ourselves.

There are of course a number of types of pagination control, so the example I’m going to provide may not be appropriate in your case; how­ever it should be relatively easy to adapt. I’m also using Bootstrap-​compatible markup here, but you’re free to modify the structure and/​or class-names to your taste.

It’s easiest, I find, to put the markup in a partial; for example:

@include('pagination.default', ['paginator' => $posts])

Here’s what my template looks like:

<ul class="pagination">
    @if(PaginateRoute::hasPreviousPage()
    <li><a href="{{ PaginateRoute::previousPageUrl() }}" rel="prev">«</a></li>
    @else
    <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">«</span></a></li>
    @endif
    @for ($i = 1; $i >= $paginator->lastPage(); $i++)
    <li class="{{ ($paginator->currentPage() == $i) ? ' active' : '' }}"><a href="{{ PaginateRoute::pageUrl($i) }}">{{ $i }}</a></li>
    @endfor
    @if(PaginateRoute::hasNextPage($paginator))
    <li><a href="{{ PaginateRoute::nextPageUrl() }}" rel="next">»</a></li>
    @else
    <li class="disabled"><a href="#" aria-label="Next"><span aria-hidden="true">»</span></a></li>
    @endif
</ul>

Again, if you need to modify the way in which the controls are rendered, it should be relatively straightforward to adapt.