Pretty Pagination URLs with Laravel 5

18th April 2016 | Tags:

It’s entirely down to per­sonal pref­er­ence, but a com­mon ques­tion is how to cre­ate “pretty” pag­i­na­tion 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 Lar­avel 5, using this pack­age.

For the pur­poses of this post I’m assum­ing you already have a con­troller method which uses pag­i­na­tion; if you don’t then just cre­ate it as nor­mal (the doc­u­men­ta­tion is here if you need it), then come back.

Installing the Package

Instal­la­tion is, as ever, done via Composer:

composer require spatie/laravel-paginateroute

The next step is to add the ser­vice provider in config/app.php:

'providers' => [
    ...
    'Spatie\PaginateRoute\PaginateRouteServiceProvider',
];

Now add the appro­pri­ate alias, also to config/app.php:

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

Finally, you’ll need to add the macro by mod­i­fy­ing your route ser­vice provider (typ­i­cally in app/Providers/RouteServiceProvider.php):

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

	parent::boot($router);
}

Usage

Once all that’s set up, you can con­fig­ure a route using “pretty pag­i­na­tion URL’s” using the paginate() macro. So, for exam­ple, sup­pose you cur­rently have a route to a blog that looks like this:

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

Sim­ply 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. How­ever it’s impor­tant to note that in this exam­ple I’m using an alter­na­tive approach to spec­i­fy­ing the name of the route; this is because at the time of writ­ing the paginate() macro doesn’t sup­port chain­ing on the name() method.

As far as your con­troller goes, you don’t need to make any fur­ther changes; Eloquent’s paginate() method will “just work”, pick­ing up the appro­pri­ate page num­ber for you.

How­ever, the one fur­ther change you’re going to have to make con­cerns the ren­der­ing of your pag­i­na­tion con­trol so that the links fol­low the cor­rect format.

The way in which Lar­avel ren­ders pag­i­na­tion con­trols has changed a cou­ple of times across dif­fer­ent ver­sions, but as you’re no doubt aware Lar­avel 5 pro­vides the render() method. Instead of using this, we’re going to need to gen­er­ate the markup ourselves.

There are of course a num­ber of types of pag­i­na­tion con­trol, so the exam­ple I’m going to pro­vide may not be appro­pri­ate in your case; how­ever it should be rel­a­tive easy to adapt. I’m also using Bootstrap-​compatible markup here, but you’re free to mod­ify the struc­ture and/​or class names to your taste.

It’s eas­i­est, I find, to put the markup in a par­tial; for example:

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

Here’s what my tem­plate 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?

Sim­ple:

@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 mod­ify the way in which the con­trols are ren­dered, it should be rel­a­tively straight­for­ward to adapt.

Comments

No comments yet.

Links and images are allowed, but please note that rel="nofollow" will be automactically appended to any links.