RSS Amplifier

jamieonkeys · Feb 12, 2023

Drupal how-to: list menu items, outputting a thumbnail image assigned to each item

0
Sign in to vote or save

This page cannot be shown here. You can still read it on the original site — the toolbar below keeps your place in the directory.

Disclaimer: I’m not a Drupal expert and share this post only to help others who may have a similar use case. I make no guarantees of best practice! This should work in Drupal 8 onwards. Refinements welcome in the comments. The scenario here is that you want to create a number of landing pages which, within the body of each landing page, list their immediate sub-pages. Along with a hyperlink, you…

Disclaimer: I’m not a Drupal expert and share this post only to help others who may have a similar use case. I make no guarantees of best practice! This should work in Drupal 8 onwards. Refinements welcome in the comments.

The scenario here is that you want to create a number of landing pages which, within the body of each landing page, list their immediate sub-pages. Along with a hyperlink, you also want to output a thumbnail image which has been assigned to each sub-page.

Say you were building a website for a professional wrestling organisation called the Scottish Wrestling Federation (SWF) and had a number of categories under a parent Roster page: Men, Women, Broadcast team and Referees. Each landing page is a gallery of photos which link to a relevant profile page.

First create a custom Image field called field_wrestler_photo (most of the roster are wrestlers so we’ll ignore the fact that a few are referees and announcers in terms of the naming). Then set up a Menu block called Sub-pages within the Main content region at /admin/structure/block. You should now see a theme hook suggestion at /admin/structure/block/manage/sub_pages#edit-settings-style (within the HTML and style options settings). Let’s name the block menu__sub_pages.

Next add a function called swf_preprocess_menu__sub_pages() to our swf.theme file. This function will process the URL and alt text data and assign it to properties of $variables. The data will then be available to us in menu--sub_pages.html.twig in the templates/navigation folder.

We’ll be using the Node interface within our preprocess function in swf.theme, so first include it towards the top of the file:

use Drupal\node\Entity\Node;

Begin our function by setting up a loop to get the data for each page:

function swf_preprocess_menu__sub_pages(&$variables) {
foreach ($variables['items'] as $key => $item) { // Loop through each page
// ...
}
}

For each page, get its URL (eg /roster/women/jean-meikle) and node path (eg /node/23). From the node path use preg_match() to get the node ID (23 in this example):

function swf_preprocess_menu__sub_pages(&$variables) {
foreach ($variables['items'] as $key => $item) {
$page_url = $variables['items'][$key]['url']->toString(); // Get page’s URL
$node_path = \Drupal::service('path_alias.manager')->getPathByAlias($page_url); // Get node path, eg /node/23
if (preg_match('/node\/(\d+)/', $node_path, $matches)) {
$node_id = intval($matches[1]); // Get the numeric node ID
// ...
}
}
}

Next, check for the property field_wrestler_photo and whether it has a value. Then get the Node object for this node ID:

function swf_preprocess_menu__sub_pages(&$variables) {
foreach ($variables['items'] as $key => $item) {
$page_url = $variables['items'][$key]['url']->toString();
$node_path = \Drupal::service('path_alias.manager')->getPathByAlias($page_url);
if (preg_match('/node\/(\d+)/', $node_path, $matches)) {
$node_id = intval($matches[1]);

if (
$node_id &&
Node::load($node_id)->field_wrestler_photo && // Has wrestler photo field
count(Node::load($node_id)->field_wrestler_photo) // Field has value
) {
$node = Node::load($node_id); // Get node object
// ...
}
}
}
}

You can now access the alt text, and use getFileUri() and the file_url_generator service to get the image’s URL. Finally, assign them to $variable properties so you can reference them in the Twig templates:

function swf_preprocess_menu__sub_pages(&$variables) {
foreach ($variables['items'] as $key => $item) {
$page_url = $variables['items'][$key]['url']->toString();
$node_path = \Drupal::service('path_alias.manager')->getPathByAlias($page_url);
if (preg_match('/node\/(\d+)/', $node_path, $matches)) {
$node_id = intval($matches[1]);

if (
$node_id &&
Node::load($node_id)->field_wrestler_photo &&
count(Node::load($node_id)->field_wrestler_photo)
) {
$node = Node::load($node_id);
$image_alt_text = $node->field_wrestler_photo->alt; // Get alt text
$file_uri = $node->field_wrestler_photo->entity->getFileUri(); // Get file URI
$image_url = \Drupal::service('file_url_generator')->generateString($file_uri); // Get image URL
$variables['items'][$key]['image_alt_text'] = $image_alt_text; // Store alt text
$variables['items'][$key]['image_url'] = $image_url; // Store image URL
}
}
}
}

Create a Twig template for the roster lists in templates/navigation/menu--sub_pages.html.twig:

{% import _self as menus %}

{{ menus.build_menu(items, attributes, 0) }}

{% macro build_menu(items, attributes, menu_level) %}
{% import _self as menus %}
{% if items %}
{% for item in items %}
{{ menus.add_link(item, attributes, menu_level) }}
{% endfor %}
{% endif %}
{% endmacro %}

{% macro add_link(item, attributes, menu_level) %}
<div>
<a href="{{ item.url }}">
<img src="{{ item.image_url }}" alt="{{ item.image_alt_text }}" width="240" height="360"> {# Wrestler image #}
</a>
<div>{{ item.title }}</div> {# Wrestler name #}
</div>
{% endmacro %}

You can now create a page for each person in the roster and arrange them into categories by editing the menu struture at /admin/structure/menu/manage/main.

You probably want to restrict the sub-pages block to only appear on the Roster pages by adding relevant paths at /admin/structure/block/manage/sub_pages#edit-visibility-request-path: /roster, roster/men etc.

We’ve focused here on a custom image field, but you can access any default or custom field in a similar fashion. For example, you could add field_wrestler_height and field_wrestler_weight fields and list each wrestler’s height and weight under their photo.

Read on /posts/drupal-menu-gallery/

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.