I'm building a custom module that modifies the main theme (barrio bootstrap in this case) to establish a landing page. In that process, it hides blocks in some regions and takes over rendering the main menu.
When I try to replace the default menu template used by the primary menu, menu--main.html.twig
with my own template mymenu--main.html.twig
which is exactly the same TWIG code (other than debugging text), it doesn't render the menu links. My custom template is being used, however for some reason it's not rendering the menu links the same.
I'm doing this in a custom module called main_sections
which uses the main_sections_theme
hook, inside the file main_sections.module
, to override the page template with a custom landing page template called, phtml--landing.html.twig
.
To render the main menu on this custom page template, I pass the main menu as a TWIG variable called main_menu_def
. This seems to work just fine and renders my main menu on my landing page, as expected, and is using the un-modified default menu template from the theme, menu--main.html.twig
.
However, when I edit the menu object's #theme
variable, to use my custom menu template, mymenu--main.html.twig
before passing the variable to the page template, phtml--landing
, it no longer renders the menu links.
To illustrate the difference I have passed two menu variables, the first called main_menu_def
which is unmodified, and main_menu
which is a copy with the theme variable changed to my custom menu template.
main_sections.module
In this file, I have a function, main_sections_block_access
that is used to hide unwanted regions, and the theme hook, main_sections_theme
in which I pull the main menu object and edit the theme template, as well as register my custom templates, phtml--landing
and mymenu--main
.
<?php
// use Drupal\block\Controller as bController;
// // use core\modules\block\src\Controller\;
use Drupal\block\Entity\Block;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Updater\Theme;
use Symfony\Component\Console\Completion\Suggestion;
/**
* Implements hook_block_access().
* Used to hide blocks on regions not used on pages set by main_sections module
*/
function main_sections_block_access(Block $block, $operation, AccountInterface $account) {
// get active path
$current_path = \Drupal::service('path.current')->getPath();
// $path_alias = \Drupal::service('path_alias.manager')->getAliasByPath($nodeid);
// \Drupal::service('path.matcher')->matchPath($path_alias, '/landing/*')
// path on which to hide blocks
$hide_on_path = '/landing';
// if the current path matches hide blocks path
if ( \Drupal::service('path.matcher')->matchPath($current_path, $hide_on_path) ){
// list of regions on which to hide blocks
$hide_regions = array(
'primary_menu',
'header',
'header_form',
'breadcrumb'
);
// Get the block's visibilty condition configuration
// These are the same visibility settings found on the block config page in the admin UI
$config = $block->getVisibilityConditions()->getConfiguration();
foreach ($hide_regions as $region){
// if block is on a region to hide, then update settings to hide it
if($block->getRegion() === $region){
// add the path on which block should be hidden to visibility pages list
$config['pages'] = $hide_on_path;
// set visibility pages to 'Hide for listed pages'
$config['negate'] = TRUE;
// update block with new visiblity pages config
$block->setVisibilityConfig('request_path', $config);
}
} //END foreach region
} //END path match
} // END of block access hook
/**
* Implements hook_theme()
* Primary module hooks for test_module module.
*/
function main_sections_theme($existing, $type, $theme, $path) {
$blocks = \Drupal\block\Entity\Block::loadMultiple();
$block_ids = [];
$block_regions = [];
$condition_list = [];
$visibilities = [];
foreach($blocks as $block){
$block_ids[] = $block->id();
$block_regions[] = $block->getRegion();
$conditions = $block->getVisibilityConditions()->getConfiguration();
$visibility = $block->getVisibilityConditions();
$condition_list[] = $conditions;
$visibilities[] = $visibility;
} //for each block
// $state = \Drupal::state()->getMultiple();
$theme = \Drupal::config('system.theme')->get('default');
// get the menu tree interface object
$menu_tree_interface = \Drupal::menuTree();
// create menu tree parameters object
$menu_parameters = new \Drupal\Core\Menu\MenuTreeParameters();
// get menu tree object for the 'main' navigation menu
$main_menu_tree = $menu_tree_interface->load('main', $menu_parameters);
// create array of desired manipulators to apply to menu tree
$manipulators = array(
// Only show links that are accessible for the current user.
array(
'callable' => 'menu.default_tree_manipulators:checkAccess',
),
// Use the default sorting of menu links.
array(
'callable' => 'menu.default_tree_manipulators:generateIndexAndSort',
),
);
// apply manipulators to the menu tree using \Drupal::menuTree()->transform()
$tree = $menu_tree_interface->transform($main_menu_tree, $manipulators);
// build transformed menu tree into renderable array object for TWIG
$main_menu = $menu_tree_interface->build($tree);
$main_menu_def = $main_menu;
// $menu_html = \Drupal::service('renderer')->render($main_menu);
// change to using local theme
// $main_menu['#theme'] = 'navigation/mymenu--main';
// $main_menu['#theme'] = 'navigation/mymenu__main';
// WORKING reference
$main_menu['#theme'] = 'mymenu__main';
// $main_menu['#theme'] = 'menu__main';
// get list of site configuration entities
$site_config = \Drupal::configFactory()->listAll();
// get array of all system site information configuration parameters
$site_info = \Drupal::config('system.site')->get();
// get the site slogan the Drupal system site information configuration
$site_slogan = \Drupal::config('system.site')->get('slogan');
// the route for the site slogan is 'system.site_information_settings'
// $config_main_menu = \Drupal::config('system.menu.main')->get();
return [
'pphtml__landing' => [
// 'render element' => 'children',
'template' => 'layout/phtml--landing',
'variables' => [
'test_var' => 'test text variable',
'theme' => $theme,
'menu_theme' => $main_menu['#theme'],
'path' => $path,
'type' => $type,
// 'existing' => $existing,
// 'main_menu_tree' => $main_menu_tree,
'main_menu' => $main_menu,
'main_menu_tree' => $main_menu_tree,
'main_menu_def' => $main_menu_def,
// 'site_config' => $site_config,
// 'site_information' => $site_info,
'site_slogan' => $site_slogan,
// 'block_ids' => $block_ids,
'block_regions' => $block_regions,
'condition_list' => $condition_list,
'existing' => $existing,
// 'visibilites' => $visibilities,
// 'config_main_menu' => $config_main_menu,
// 'vars' => $variables,
],
// 'base hook' => 'node',
],
'mymenu__main' => [
'template' => 'navigation/mymenu--main',
'variables' => [
'main_menu' => $main_menu,
'menus' => $main_menu_tree,
],
// 'render element' => 'children',
// 'base hook' => 'menu__main',
// 'render element' => 'menu',
]
];
}
phtml--landing.html.twig
Here is the custom landing page template.
<div class="layout-container">
<header role="banner">
<div>
<p>TESTING HEAD</p>
</div>
{{ kint() }}
</header>
<div class="container">
<div> Menu that automatically uses default bootstrap barrio menu--main twig template </div>
<nav class="navbar navbar-dark bg-primary navbar-expand-lg menu--main">
<div class="container-fluid contextual-region block block-menu navigation justify-content-center">
{{ main_menu_def }}
</div>
</nav>
<div> Menu with #theme render array variable changed to mymenu--main twig template </div>
<nav class="navbar navbar-dark bg-primary navbar-expand-lg menu--main">
<div class="container-fluid contextual-region block block-menu navigation justify-content-center">
{{ main_menu }}
</div>
</nav>
<div class="row">
<p>TESTING BODY</p>
<div>{{ kint(theme) }}</div>
<div>{{ kint(menu_theme) }}</div>
<div>{{ kint(main_menu) }}</div>
<div>{{ kint(main_menu_tree) }}</div>
</div>
</div>
<footer role="contentinfo">
{{ page.footer }}
<div>
<p>FOOTER TESTING</p>
</div>
</footer>
</div>
mymenu--main.html.twig
This is my custom menu template, which is a copy of the default theme's menu template, menu--main
with an added text line and kint for debugging.
{% import _self as menus %}
{# {% import _self as main_menu %} #}
{#
We call a macro which calls itself to render the full tree.
@see http://twig.sensiolabs.org/doc/tags/macro.html
#}
{{ menus.menu_links(items, attributes, 0) }}
{# {{ main_menu.menu_links(items, attributes, 0) }} #}
<div style="color:lime"> My Main Menu </div>
{{ kint() }}
{{ kint(menus) }}
{{ kint(main_menu) }}
{% macro menu_links(items, attributes, menu_level) %}
{% import _self as menus %}
{# {% import _self as main_menu %} #}
{% if items %}
{% if menu_level == 0 %}
<ul{{ attributes.addClass('nav navbar-nav')|without('id') }}>
{% else %}
<ul class="dropdown-menu">
{% endif %}
{% for item in items %}
{%
set classes = [
menu_level ? 'dropdown-item' : 'nav-item',
item.is_expanded ? 'menu-item--expanded',
item.is_collapsed ? 'menu-item--collapsed',
item.in_active_trail ? 'active',
item.below ? 'dropdown',
]
%}
<li{{ item.attributes.addClass(classes) }}>
{%
set link_classes = [
not menu_level ? 'nav-link',
item.in_active_trail ? 'active',
item.below ? 'dropdown-toggle',
item.url.getOption('attributes').class ? item.url.getOption('attributes').class | join(' '),
'nav-link-' ~ item.url.toString() | clean_class,
]
%}
{% if item.below %}
{{ link(item.title, item.url, {'class': link_classes, 'data-bs-toggle': 'dropdown', 'aria-expanded': 'false', 'aria-haspopup': 'true' }) }}
{{ menus.menu_links(item.below, attributes, menu_level + 1) }}
{% else %}
{{ link(item.title, item.url, {'class': link_classes}) }}
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endmacro %}
Resulting Output
The resulting page shows both menus, the top one is using the un-modified menu variable main_menu_def
which uses the menu--main
barrio bootstrap template, and renders as expected, and the 2nd one using the modified menu variable, main_menu
, which uses my custom mymenu--main
template which does not render the links.
Here are the variables of the non-working menu block, expanded and the inspector showing it is pulling the desired template.
What am I doing wrong with this setup? One of the parts I'm not sure about is how to establish multiple theme template overrides through hooks, properly. Especially as one template override (ie the mymenu--main template) is rendered inside another template override (phtml--landing, in this case). This is what I worked out to get these templates to be recognized and used, but clearly it's not quite right.