Drupal es capaz de mostrar paginadores de manera automática en listados por defecto de entidades y views (si se configura la vista para usar paginador). En este post se explica cómo aprovechar los paginadores de Drupal en funcionalidad personalizada.
Base de código
Para los ejemplos de código, se usa un módulo personalizado "example_module" con los siguientes archivos:
example_module/example_module.routing.yml
En este archivo se crean 3 rutas, una para cada ejemplo de paginador.
example_module.entity_query_pager:
path: '/entity-query-pager'
defaults:
_controller: '\Drupal\example_module\Controller\PagerController::entityQuery'
_title: 'Entity query pager'
requirements:
_permission: 'access content'
example_module.select_pager:
path: '/select-pager'
defaults:
_controller: '\Drupal\example_module\Controller\PagerController::select'
_title: 'Select pager'
requirements:
_permission: 'access content'
example_module.api_pager:
path: '/api-pager'
defaults:
_controller: '\Drupal\example_module\Controller\PagerController::api'
_title: 'API pager'
requirements:
_permission: 'access content'
example_module/src/Controller/PagerController.php
<?php
declare(strict_types = 1);
namespace Drupal\example_module\Controller;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Query\PagerSelectExtender;
use Drupal\Core\Pager\PagerManagerInterface;
use Drupal\Core\Url;
use GuzzleHttp\ClientInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Controller for showing custom pagers.
*/
class PagerController extends ControllerBase {
/**
* Class constructor.
*/
public function __construct(
protected PagerManagerInterface $pagerManager,
protected Connection $database,
protected ClientInterface $httpClient,
) {}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('pager.manager'),
$container->get('database'),
$container->get('http_client'),
);
}
/**
* Entity query pager.
*/
public function entityQuery() {
$nids = $this->entityTypeManager()->getStorage('node')->getQuery()
->accessCheck(TRUE)
->condition('type', 'news')
->pager()
->execute();
$nodes = $this->entityTypeManager()->getStorage('node')->loadMultiple($nids);
return [
'items' => $this->entityTypeManager()->getViewBuilder('node')->viewMultiple($nodes, 'teaser'),
'pager' => [
'#type' => 'pager',
],
];
}
/**
* Select pager.
*/
public function select() {
$results = $this->database->select('node_field_data', 'n')
->condition('type', 'news')
->fields('n', ['title'])
->extend(PagerSelectExtender::class)
->execute()
->fetchCol();
return [
'items' => array_map(fn ($result) => ['#markup' => '<h2>' . $result . '</h2>'], $results),
'pager' => [
'#type' => 'pager',
],
];
}
/**
* API pager.
*/
public function api(Request $request) {
$base_url = 'https://pokeapi.co/api/v2/pokemon';
$url = Url::fromUri($base_url, [
'query' => [
'limit' => 10,
'offset' => $request->query->get('page', 0) * 10,
],
]);
$response = $this->httpClient->request('GET', $url->toString());
$results = Json::decode($response->getBody()->getContents());
$total = $results['count'];
$this->pagerManager->createPager($total, 10);
return [
'items' => array_map(fn ($result) => ['#markup' => '<h2>' . $result['name'] . '</h2>'], $results['results']),
'pager' => [
'#type' => 'pager',
],
];
}
}
Nota: Ni la instalación de módulos personalizados ni la inyección de dependencias se explican en este artículo.
Paginador usando entityQuery()
Paginación usando la consulta de entidades de Drupal.
Esta página (/entity-query-pager) corresponde a la función "entityQuery" del controlador.
Paso a paso
- Se genera una consulta de entidades usando entityTypeManager. En este punto, lo importante es la inicialización del paginador usando el método "pager". Este método recibe el número de elementos por página (10 por defecto) y el id del paginador (se usa en caso de que haya más de un paginador en la misma página, no es necesario en este ejemplo)
- Usando entityTypeManager de nuevo, se cargan los nodos con los ids resultantes de la consulta
- Se devuelve un render array con los resultados (usando el modo de vista resumen en este caso). La clave en este punto es añadir al render array un elemento de tipo "pager". Como ya se ha inicializado el paginador en la consulta, Drupal se encarga del resto
Paginador usando select()
Paginación usando una consulta SQL normal a través del DAL de Drupal.
Esta página (/select-pager) corresponde a la función "select" del controlador.
Paso a paso
- Se genera una consulta a la base de datos usando el generador de consultas SQL de Drupal. En este punto, lo importante es la inicialización del paginador usando el método "extend(PagerSelectExtender::class)". Este método sirve para extender una consulta SQL con parámetros anteriormente defnidos (por ejemplo esta clase para la paginación o la clase "TableSortExtender" para ordenación de tablas)
- Nota: para limitar el número de elementos por página, en lugar de usar el método "range", hay que usar el método "limit" tras la extensión con el paginador. El método "limit" recibe como parámetro el número de elementos por página. 10 por defecto
- Se devuelve un render array con los resultados (los elementos se envuelven en un h2 para mejor visualización). La clave en este punto es añadir al render array un elemento de tipo "pager". Como ya se ha inicializado el paginador en la consulta, Drupal se encarga del resto
Paginador con datos de API
Paginación ejecutando llamadas a una API. En este caso, la API de pokémon.
Esta página (/api-pager) corresponde a la función "api" del controlador.
Paso a paso
- Se genera una petición a la api de pokémon (en este caso, al listado general de pokémon). En este ejemplo no se hace control de excepciones
- Documentación de la api, donde se explica por ejemplo cómo paginar resultados
- Usando la respuesta de la petición, se genera el paginador. Para ello, se usa el servicio "pager.manager", al que hay que pasarle como argumento el total (la api lo devuelve en la clave "count") y el número de elementos por página a mostrar
- Se devuelve un render array con los resultados (los elementos se envuelven en un h2 para mejor visualización). La clave en este punto es añadir al render array un elemento de tipo "pager". Como ya se ha inicializado el paginador usando el servicio, Drupal se encarga del resto