Now that we have seen how to make SELECT queries of all kinds, let's take a look at how we can use Drupal's built-in pagination capabilities and how pagers work in Drupal 8. We will illustrate these by running some queries and rendering the results inside a table. Refer to Chapter 4, Theming, if you don't remember the theming aspects of outputting a table.
Our playground will be inside a new controller method (SportsController::players()) which maps to the route with the /players path. Refer to Chapter 2, Creating Your First Module, for a refresher on how to create routes if you don't remember.
The first thing we'll do is create a simple query that loads all the players and outputs them inside a table. We'll stick to only showing the player names for simplicity:
/** * Renders a table of players. */ public function players() { $query = $this->database->select('players', 'p') ->fields('p'); $result = $query->execute()->fetchAll(); $header = [$this->t('Name')]; $rows = []; foreach ($result as $row) { $rows[] = [ $row->name ]; } $build = []; $build[] = [ '#theme' => 'table', '#header' => $header, '#rows' => $rows, ]; return $build; }
All of this should already be familiar to you. We are running the query and preparing the data for a table, using the table theme hook to render it. You'll note that we are creating a $build array so that we can include more things in the final output.
By navigating to /players, we should now already see a table with our player names. This will be our baseline from which to explore pagers.
Pagers work by storing some information regarding a query in the global state, namely the total number of items to be paged, the limit of items per page, and an identifier for the respective pager (so we can potentially have multiple pagers at once). All of this information is set using the following code (you don't have to add this anywhere now):
pager_default_initialize($total, $limit, $element = 0);
Moreover, the current page is determined by the query parameter in the URL, named page.
Once the pager is initialized, we have a pager render element we can use to easily render a themed pager that uses this information and builds all the necessary links to move between the pages. As query builders, we then have to read the current page and use that inside our query.
However, there is a much simpler way to work with pagers, and that is using select extenders. These are decorator classes for the SELECT query class we've seen before, and they allow us to decorate it with an extra functionality, such as pagers or sorting; they encapsulate the necessary functionality for handling pagers in the query. So, let's see it in action.
Here is how our player query would look using the PagerSelectExtender:
$limit = 5; // The number of items per page. $query = $this->database->select('players', 'p') ->fields('p') ->extend('\Drupal\Core\Database\Query\PagerSelectExtender') ->limit($limit); $result = $query->execute()->fetchAll();
As you can see, we have an extend() method on the SELECT query builder, which allows us to pass the name of the class that will decorate the resulting SELECT query class. This also provides us with a new method called limit(), through which we specify the number of records to load per page. Under the hood, it uses the range() method we saw earlier. Moreover, when running the query, it initializes the pager for us using pager_default_initialize(), and even determines the current page all on its own. So typically you'll use the extender directly.
So, all we need to do now is render the following pager (below the table):
$build[] = [ '#type' => 'pager' ];
Positively rocket science, right? Not really. If we refresh the page, we should now see only five players in the table, and also a pager below it.
The Pager render element (https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Render%21Element%21Pager.php/class/Pager/8.2.x) has some interesting properties we can use to customize it further. We can append query elements to the resulting links, or even specify another route for the links if we want to. We can, of course, control the label of the pager links, and even the number of links being output. Check out the documentation of this element for more information.
Moreover, for full customization, we also have the option of preprocessing these variables by implementing our own preprocessor for the pager hook (such as template_preprocess_page) and/or overriding the pager.twig.html template file. We learned how to do these things in in Chapter 4, Theming.