In a recent project, we were using an external service that pulls our site into their system and injects their content into our project. The issue we ran into was that the third party was overwriting all links in the template we provided with their HTTP host, causing the menu system from our Drupal site to break. To get around this we needed to generate absolute links for all of our menus.
In order to accomplish this we used template_preprocess() to dynamically provide our site's hostname as a variable:
/**
* Implements template_preprocess().
*/
function MYTHEME_preprocess(&$vars, $hook) {
if ($hook == 'menu') {
$vars['host'] = \Drupal::request()->getHost();
}
}
Then, we copied the menu.html.twig file into our theme and made the following adjustments:
{#
#}
{% import _self as menus %}
{#
We call a macro which calls itself to render the full tree.
#}
{{ menus.menu_links(items, attributes, 0, host) }}
{{ kint(host) }}
{% macro menu_links(items, attributes, menu_level, host) %}
{% import _self as menus %}
{% if items %}
{% for item in items %}
{% if not item.url.isExternal() %}
{% set item_path = item.url|render %}
{{ item.title }}
{% else %}
{{ link(item.title, item.url) }}
{% endif %}
{% if item.below %}
{{ menus.menu_links(item.below, attributes, menu_level + 1, host) }}
{% endif %}
{% endfor %}
{% endif %}
{% endmacro %}
Here, we passed the 'host' variable provided by template_preprocess() into the 'menu_links' macro.
Within the macro, we check whether the item.url is external. If it is, we print it as normal because it will already be absolute.
If it is not external, we render the internal url into a variable, and manually create our link with our hostname variable from the attributes array.
Props to Andy Blum for working through much of this fix.
One potential issue:
Attributes that are normally passed through the link() function might be left off the manually created anchor.