Navigate to next page / previous page based on entity reference field instead of Book module I am trying to create next/previous page links like the Book module.

I have two content types, Chapter and Page.

The Chapter content type contains an entity reference field (unlimited) for pages.

So, each chapter contains an arbitrary number of pages in the entity reference field Pages in Chapter, and these are re-ordered to determine the order of the pages.

Now, on each Page, I want to make links to the previous and next pages based on the values of the parent Chapter's entity reference field Pages in Chapter.

Specifically, using a view or block or similar on the Page, I need to get the Chapter that references the current Page, iterate through the values of the entity reference field Pages in Chapter, find the Page that is immediately before and the Page that is immediately after the current Page, and then output this as a simple unordered list.

I can code a little but I'm not very good so I'm having trouble finding an approach to implement this.

What I tried

I tried to build this in Views.  The problem I encountered was that it is very difficult to iterate through the subdivision entity reference field and filter out the previous and next links.

This is a similar question for Drupal 7 but since Drupal 8 includes so much more in core (Views and Entity References, for example), I think it's fair to ask the question again.

Why not just use the Book module?

I only need the next/previous page functionality, and ideally it would be based on my entity reference field Pages in Chapter.  The book module navigation in Drupal 8 doesn't follow the same patterns as the rest of core (it's not a block, and it's not a menu).  I spent a couple hours this afternoon going through the Book issue queue and cleaning up old issues, and it seemed there is very little effort to "modernize" the module, so I thought it would be better to try to implement this feature on my own.
I can code a little but I'm not very good so I'm having trouble
  finding an approach to implement this.


I started 7 years ago from zero, I only knew HTML and CSS. I learned and mastered Rules &amp; Views. But like 4 or 5 years ago, I ran into scenarios like this one where the existing modules didn't quite do what I needed or felt I had to do overkill/bulky-overhead workarounds with existing modules. So I learned PHP from online resources and I learned how to create custom modules for Drupal.

I highly suggest you improve your backend/coding game. Specially since Rules in D8 is barebones compared to D7.


  I tried to build this in Views. The problem I encountered was that it
  is very difficult to iterate through the subdivision entity reference
  field and filter out the previous and next links.


While it's possible to programmatically solve it with Views, it feels overkill/unnecessary to do this in Views just for prev and next links. A better approach is to create prev_url and next_url variables for Twig. Using a preprocess function we can create and pass those variables to the appropriate template file, node.html.twig in this case.

I've put lots of comments, so you can follow and hopefully learn a thing or two.

In mytheme.theme (since this particular hook can also be used in theme, rather than creating a custom module).

/**
 * Implements hook_preprocess_HOOK() for node.html.twig.
 */
function mytheme_preprocess_node(&amp;$variables) {
  $node = $variables['elements']['#node']; // obtain current page node object
  if ($node-&gt;getType() == 'page') { // check it's a "page" content type
    $page_nid = $node-&gt;id(); // get the current page node id
    // get Chapter nids using Entity Field Query
    $query = \Drupal::entityQuery('node')
      -&gt;condition('type', 'chapter')
      -&gt;condition('field_reference', $page_nid); // check if current page NID is set on any Chapter's reference field
    $chapter_nids = $query-&gt;execute(); // returns array of Chapter nids
    if (!empty($chapter_nids)) { // check we have a result
      $chapter_nid = reset($chapter_nids); // Extracts the first array value, since we assume current page can only belong to one Chapter.
      $chapter_node = Drupal\node\Entity\Node::load($chapter_nid); // load chapter node
      $referenced_page_nodes = $chapter_node-&gt;field_reference; // get referenced nodes
      foreach ($referenced_page_nodes as $key =&gt; $referenced_item) { // loop though referenced nodes
        if ($referenced_item-&gt;entity) {
          if ($node-&gt;id() == $referenced_item-&gt;entity-&gt;id()) { // check if current page nid equals referenced nid
            $prev_key = $key - 1; // we'll use these keys to access prev/next array values
            $next_key = $key + 1;
            if (!empty($referenced_page_nodes[$prev_key])) { // check if previous key exists
              $prev_url = $referenced_page_nodes[$prev_key]-&gt;entity-&gt;toUrl()-&gt;toString(); // get prev URL
              $variables['prev_url'] = $prev_url; // creates prev_url variable in node.html.twig
            }
            if (!empty($referenced_page_nodes[$next_key])) { // check if next key exists
              $next_url = $referenced_page_nodes[$next_key]-&gt;entity-&gt;toUrl()-&gt;toString(); // get next URL
              $variables['next_url'] = $next_url; // creates next_url variable in node.html.twig
            }
            break; // since we found it, exit from loop as no point in continuing to loop through.
          }
        }
      }
    }
  }
}


Note: Rename field_reference (2 places) to the machine name of your actual reference field in Chapter content type.

In mytheme/templates/node.html.twig, you can place them after the {{ content }} part

{% if prev_url is not empty %}
  &lt;span&gt;&lt;a href="{{ prev_url }}"&gt;{{ 'Previous page'|t }}&lt;/a&gt;&lt;/span&gt;
{% endif %}
{% if next_url is not empty %}
  &lt;span&gt;&lt;a href="{{ next_url }}"&gt;{{ 'Next page'|t }}&lt;/a&gt;&lt;/span&gt;
{% endif %}