Part Two: Creating a Custom Pagination View in Laravel

July 28, 2014 —John Koster

This is part two of two in a series of posts on how to create a custom pagination view in Laravel. Before reading this part, it is highly recommended that your first read Part One: Creating a Custom Pagination View in Laravel of this series. In the first part, we created a new pagination view that let's users enter the page number in a text box to jump to a page. I ended that post with a warning about how that method would not work out-of-the-box for data sets that have been filtered, such as when you are displaying search results to a user.

Let's consider the following set-up like this:

Creating a custom, searchable, pagination view in Laravel

When our user enter's a number in the text box and hits enter, search term jack should be present in the URL (since the form is using a GET request here). The pagination view in the first part of this series will not pull this information in. So how do we go about doing it?

#Passing Form Data to the Paginator

The method I've come up with involves creating hidden form input elements. The pagination view from part one looks like this right now:

1<?php
2 $presenter = new Illuminate\Pagination\BootstrapPresenter($paginator);
3?>
4 
5<?php if ($paginator->getLastPage() > 1): ?>
6 <ul class="pagination">
7 <?php echo $presenter->render(); ?>
8 <li>
9 <?php
10 echo Form::open(array('url' => $paginator->getUrl(0), 'method' => 'GET'));
11 ?>
12 <input type="number" name="page" min="0" max="<?php echo $paginator->getLastPage(); ?>" value="<?php echo $paginator->getCurrentPage(); ?>" placeholder="Page #" class="form-control" style="width: 150px; float: left; margin-left: 20px;" >
13 <?php echo Form::close(); ?>
14 </li>
15 </ul>
16<?php endif; ?>

What we need to do is create the hidden input elements and include them in the generated view. That way, whenever a user enters a number into the textbox, the form will send the original search terms with the page number. Cool, I know. Now we just need to get input values and create our input elements!

#Creating a View Composer

To do this, let's create a view composer. From the Laravel doc's this is a view composer:

View composers are callbacks or class methods that are called when a view is rendered. If you have data that you want bound to a given view each time that view is rendered throughout your application, a view composer can organize that code into a single location. Therefore, view composers may function like "view models" or "presenters".

Essentially, we have our view named 'enterable.blade.php', and we create a view composer and link it to the view. Every time we render that view, the view composer executes, which we can use to send data to our actual views. This helps us keep our views clean and simple. I've created a view composer named EnterableComposer in the root namespace (just save it anywhere Laravel and Composer can find it).

View composers need to have a method named compose that accepts the $view as its argument. So our view composer looks like this right now:

1<?php
2 
3class EnterableComposer {
4 
5 public function compose($view)
6 {
7 
8 }
9 
10}

Well, that's boring. We need it to do something. We know we are going to need the input from the user and a place store our HTML that we generate. We can add these inside the compose($view) method like this:

1 
2...
3 
4// Get any input values that may have been set.
5$inputData = Input::all();
6 
7// Hidden form data.
8$hiddenFormData = '';
9 
10// Here we are going to remove the 'page' value if it has been set,
11// since we have our own textbox named 'page'.
12unset($inputData['page']);
13 
14...

"Wait! What's with that unset() thing on that last line there? We didn't talk about that?!" you say. Calm down, its okay. We need to remove the page variable from the input array we receive, since the user will be entering it in themselves.

Now all we need to do is generate the actual hidden form elements! Well, we can do that like this (this goes after the unset line inside the compose($view) method):

1 
2...
3 
4// Loop over all the remaining input items and create hidden form input
5// elements that we will need in our paginator.
6foreach($inputData as $inputName => $inputValue)
7{
8 if (is_array($inputValue))
9 {
10 if (count($inputValue) > 0)
11 {
12 foreach($inputValue as $nestedValue)
13 {
14 $hiddenFormData .= Form::hidden($inputName.'[]', $nestedValue);
15 }
16 }
17 }
18 else
19 {
20 $hiddenFormData .= Form::hidden($inputName, $inputValue);
21 }
22}
23 
24$view->with('hiddenPaginatorInputFields', $hiddenFormData);
25 
26...

This might look a little confusing right now, but its not that bad. What we are doing is iterating over the values stored in the $inputData array, and storing the key in a variable named $inputName and storing the value in the variable $inputValue. Ignoring the if statement for now, we are simply creating an HTML fragment using Form::hidden by passing the values stored in $inputName and $inputValue.

If $inputName was first_name and $inputValue was jack, we would get an HTML fragment like this:

1<input type="hidden" name="first_name" value="jack">

That's what happens in the else block of the if statement. So what happens when $inputValue is an array itself? The exact same thing happens, where the code will iterate over all the values in $inputValue and create new fragments for each nested value. The only difference is that we add the string [] to the end of the new input name, to create an input array. Let's say we had this array:

1<php
2 
3$input = [
4 'cities' => [
5 'Austin',
6 'New York',
7 'Boston',
8 ]
9 ];

The HTML fragment that would be produced looks like this:

1<input type="hidden" name="cities[]" value="Austin">
2<input type="hidden" name="cities[]" value="New York">
3<input type="hidden" name="cities[]" value="Boston">

#Linking the View Composer to the View

Well that's all well and good. We still need to get those hidden input fields to our pagination view. In our routes file, we need to link the view composer to the view. We can do that like this:

1<php
2 
3View::composer('enterable', 'EnterableComposer');

In the above code sample, enterable is the name of the view, and EnterableComposer is the name of the view composer.

We do need to make an adjustment to our enterable view though. We need to actually put the hidden input elements in our form. We can do that like this

1...
2 
3// Send our hidden input fields to the user.
4if (isset($hiddenPaginatorInputFields))
5{
6 echo $hiddenPaginatorInputFields;
7}
8 
9...

The above code should appear after the echo Form::open() line (from the previous post). Now our pagination view will have the hidden input fields embedded in it, and when our user enters a number in the text box and hits enter, it will send all the search criteria along with the page number; and everyone will be happy.

#One Last Consideration

Now that our text box will send the hidden input values, what should we do about the actual pagination links? When a user clicks on one of those, it just sends the page number, and none of the search data. That's no good. Well, in our view where we call the links() method on our collection, we can do some special things. Let's pretend we are outputting our pagination links with this line of code:

1{{  $users->links()  }}

We can use the appends() method to append any input or URL parameters to the generated link. However, if we did that we still face the issue of sending the current page of the results, which we do not want. To resolve this issue, we can filter the input array, using the array_except function together with the Paginator::getPageName() function (this function returns the input name the paginator class is using internally, the default is page). Doing all of this sounds complicated, but it looks like this:

1{{  $users->appends(array_except(Input::query(), Paginator::getPageName()))->links()  }}

Now we have links that will send the input data along with the page name to display.

Some absolutely amazing
people

The following amazing people help support this site and my open source projects ♥️
If you're interesting in supporting my work and want to show up on this list, check out my GitHub Sponsors Profile.