I have a content type article that has a field for Category. In order to help our editors find related articles, I included a table on the article Edit page that pulls in other article entities that have the same category as the current one.

I did this by implementing a hook_form_alter hook and adding some markup to the $form object after querying for all related articles.

// Query for matching articles
$query = \Drupal::entityQuery('node');
$query->condition('type', 'article');
$query->condition('field_articles_category.entity:taxonomy_term.tid', $categoryId);
$query->sort('created', $sortDirection);
$query->range($offset, $limit);

/* Omitting logic to load the Node objects by revision ID's */
// $articles is an array of Node objects of type article

$articlesTable = []
foreach ($articles as $index => $article) {
   $articlesTable[$index] ['articleTitle'] = [
      '#type' => 'link', '#title' => $article->getTitle(), '#url' => $article->toUrl()

$form['related_articles'] = [
   '#type' => 'details',
   '#title' => 'Articles of the Same category',
   'articlesTable' => $articlesTable

The table displays just fine and shows the expected articles. But there are sometimes hundreds of articles in a particular category so I would like to paginate this table for performance reasons (and to make sure the page is not too long to scroll through). How can I paginate such a table?

I've seen some articles mention creating a pagination controller and somehow setting the AJAX callback for that controller, but haven't found a concrete example yet to follow.

1 Answer 1


You can use tableselect instead of details something like:

   $query = \Drupal::database()->select('node', 'n')
      ->condition('type', 'article', '=');
    $query->fields('n', ['nid']);
    $query->innerJoin('node__field_articles_category', 'category', 'category.field_articles_category_target_id = ' . $categoryId);
    //For the pagination we need to extend the pagerselectextender and
    //limit in the query
    $pager = $query->extend('Drupal\Core\Database\Query\PagerSelectExtender')
    $article_ids = $pager->execute()->fetchAll(\PDO::FETCH_OBJ);
    $rows = [];
    foreach ($article_ids as $index => $article_id) {
      $article = Node::load($article_id->nid);
      $rows[$index + 1] = [
        'title' => $article->getTitle(),
        'url'   => Drupal::l($article->getTitle(), $article->toUrl()),

    $form['related_articles']['table'] = [
      '#type'    => 'tableselect',
      '#header'  => ['title' => t('title'), 'url' => t('Url')],
      '#options' => $rows,
      '#empty'   => t('No users found'),
    // Finally add the pager.
    $form['related_articles']['pager'] = array(
      '#type' => 'pager',

Result will be like: enter image description here

  • Thanks for the response. This will still query all the contents (up to the $limit) at once, correct? My concern is if I have thousands of Articles, it will start to slow down rendering the edit page, which is why I was looking for a solution where next batch of contents are queried only when they are requested, instead of all at once. Jan 1, 2019 at 0:11
  • You are welcome, now the query extend PagerSelectExtender,so for example if you set $limit to 10 the select will load 10 by 10 element.
    – berramou
    Jan 1, 2019 at 14:38
  • 1
    Unfortunately, clicking on a link in the pager (Next/Last, etc.) navigates away from the page and the Editors loses any unsaved edits. That's why I was looking for an AJAX type solution. Jan 2, 2019 at 16:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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