4

I have a list of items which is fetched from an REST API and displayed in a page 10 at a time. These items are not stored in Drupal database in any way. I managed to have a basic pager which is just a Previous & Next. But as per design the list should have a full pager.

So, how can I add a Full Pager in such page?

11

There are some examples on drupal.org of adding a pager to the page, if you want to use the one that core uses:

https://api.drupal.org/api/drupal/core%21includes%21pager.inc/function/pager_default_initialize/8.2.x

The second example should work for you:

  $page = pager_find_page();
  $num_per_page = \Drupal::config('mymodule.settings')->get('num_per_page');
  $offset = $num_per_page * $page;
  $result = mymodule_remote_search($keywords, $offset, $num_per_page);

  // Now that we have the total number of results, initialize the pager.
  pager_default_initialize($result->total, $num_per_page);

  // Create a render array with the search results.
  $render = [];
  $render[] = [
    '#theme' => 'search_results',
    '#results' => $result->data,
    '#type' => 'remote',
  ];

  // Finally, add the pager to the render array, and return.
  $render[] = ['#type' => 'pager'];
  return $render;

I have done this before in my own wired up search pages, it should solve your issue.

You can replace $num_per_page above with 10, if you do not have a custom module to store the setting.

You could go further, and in your code create a service that handles just attaching the pager. I was doing some sandboxing exploring the search API, and came up with this. First, you need to define your service and inject it to your controller (I refer to mine as pager_service).

  /**
   * @var \Drupal\velirsearch\Service\PagerService
   */
  private $pager_service;

  /**
   * SearchResults constructor.
   * @param \Drupal\velirsearch\Service\PagerService $pager_service
   */
  public function __construct(PagerService $pager_service) {
    $this->pager_service = $pager_service;
  }

  /**
   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
   * @return static
   */
  public static function create(ContainerInterface $container) {
    $pager_service = $container->get('velirsearch.pager_service');
    return new static($pager_service);
  }

Then, a method that attaches a pager:

     /**
       * Convenient method to obtain a pager to attach from the pager service.
       * @param $totalRows
       * @param $limit
       * @return array
       */
      protected function attachPager($totalRows, $limit) {
        return $this->pager_service->getPager($totalRows, $limit);
      }

Then, the service:

namespace Drupal\velirsearch\Service;

use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\HttpFoundation\RequestStack;

class PagerService implements ContainerAwareInterface {

  use ContainerAwareTrait;

  /**
   * @var \Symfony\Component\HttpFoundation\Request
   */
  private $requestStack;

  /**
   * PagerService constructor.
   * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
   */
  public function __construct(RequestStack $requestStack) {
    $this->requestStack = $requestStack;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    $requestStack = $container->get('request_stack');
    return new static($requestStack);
  }

  /**
   * @param $totalRows
   * @param $limit
   * @return array
   */
  public function getPager($totalRows, $limit = 10) {
    pager_default_initialize($totalRows, $limit);
    return ['#type' => 'pager'];
  }
}

Then, when you need it...

  $render[] = $this->attachPager($results->getResultCount(), 10);

  return $render;

You don't necessarily have to go that far, I was just looking for ways to reduce the code needed to attach a pager to different instances of search result controllers. YMMV.

Note that I did not use the third argument to pager_default_initialize, $element, which you will need if your page will have multiple pagers on it.

getResultCount(), by the way, is a method provided from Search API ResultSet class.

edit: now looking at my answer, your pager service does not need to inject the RequestStack at all. I was probably thinking of another idea and never finished it or figured I did not need it.

  • Important, but easy to miss detail: when adding $render[] = ['#theme' => 'my_pretty_results'... make sure it's $render[] =... and not $render =.... The code sample I was working with had it done without the [], which still produced the output results, but failed to add the pager. The system's flexibility hid the problem. – coladict Jan 25 at 9:24
0

That's a pretty damn good answer, Kevin! I would suggest an improvement though. And that's in the way you inject the request_stack in your service. You may lose the create() method there and just inject it when you are declaring the service in your .services.yml like this:

services: 
  yourmodule.pager_service: 
    class: Drupal\yourmodule\PagerService 
    arguments: ['@request_stack']

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.