Pretty Pagination URLs with Laravel 5

18thApril 2016 | Tags:

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 something 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)
{
	PaginateRoute::registerMacros();

	parent::boot($router);
}

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; however it should be relative 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>
    <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">«</span>
  </a>
  @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($paginator) }}" rel="next">»</a>
  </li>
  @else 
  <li>
    <li class="disabled"><a href="#" aria-label="Next"><span aria-hidden="true">»</span>
  </a>
  @endif

</ul>

Just want "back" and "next" buttons?

Simple:

@if(PaginateRoute::hasPreviousPage())
<a href="{{ PaginateRoute::previousPageUrl() }}" rel="prev" class="previous">«</a>
@endif

@if(PaginateRoute::hasNextPage())
<a href="{{ PaginateRoute::nextPageUrl() }}" rel="next" class="next">»</a>
@endif

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

Comments

No comments yet.