hook_theme_suggestions_alter does not work for hyphens

Created on 26 February 2018, over 6 years ago
Updated 9 August 2023, about 1 year ago

Problem/Motivation

When using hook_theme_suggestions_alter() there is a subtle error.

When there are hyphens in the suggestion name, everything looks OK - the new suggestion is added to the suggestion list. But actually the template suggested will not get picked up.

Steps to reproduce

Alter a theme suggestion containing a hyphen, where an underscore is expected, like by using an ID:

function custommodule_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
  if ($variables['theme_hook_original'] == "webform_actions") {
    // $variables['element']['#id'] might be "submit--3"
    $suggestions[] = $variables['element']['#form_id'] . '__' . $variables['element']['#id'];
  }
}

For another example and the many replies, see https://www.drupal.org/forum/support/theme-development/2017-04-12/custom...

Proposed resolution

Ensure all hyphens are replaced by underscore after the theme suggestion hooks ran.

Workaround / manual fix

To work around it, change the hyphen into an underscore in hook_theme_suggestions_alter() - the template suggestion will then have these changed to hyphens in the template name - and crucially the template will then be used.

For example, for providing a custom template for webform's submit button section:

In file custommodule.module:

function custommodule_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
  if ($variables['theme_hook_original'] == "webform_actions") {

    $suggestions[] = $variables['element']['#form_id'] . '__' . str_replace('-', '_', $variables['element']['#id']);
  }
}

Remaining tasks

  1. Write a test that shows the issue
  2. Fix the issue in core by ensuring all hypens are replaced by an underscore after HOOK_theme_suggestions_alter ran

User interface changes

None

API changes

None

Data model changes

None

Release notes snippet

TBD

🐛 Bug report
Status

Active

Version

11.0 🔥

Component
Theme 

Last updated 1 day ago

Created by

🇬🇧United Kingdom bailey86

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • Still a very confusing issue.

    See: https://www.drupal.org/forum/support/theme-development/2017-04-12/custom-theme-suggestion-not-recognised

    Using "-" instead "_" fails silently, the suggestion is visible in the Twig debug comments, but doesn't work.

  • 🇩🇪Germany Anybody Porta Westfalica
  • 🇩🇪Germany Anybody Porta Westfalica

    @thomas.frobieter to clarify this - when you switched to "_" then it worked?
    This should make expected vs. actual clearer to fix this.

  • 🇩🇪Germany Anybody Porta Westfalica

    So the proposed solution here is to ensure all hypens are replaced by an underscore in core and not forwarded as-is.

    I'll update the issue summary.

  • 🇩🇪Germany Anybody Porta Westfalica
  • Maybe strtolower() also make sense, the uppercase characters are also used 1:1. Not sure if this also breaks, but seems not that clean.

  • 🇮🇹Italy apaderno Brescia, 🇮🇹

    As long as it a character that PHP allows in function names, there is no need to change it. That is why hyphens in suggested template names do not work: PHP function names cannot contain hyphens.

  • Not sure what this issue has to do with PHP function names. We are talking about adding Twig template override suggestions.

    Typically those suggestions contain variables. If the variable value holds hyphens, the suggestion is added and looks completely fine in the Twig debug comments, but the override will fail silently. This is really bad.

  • 🇩🇪Germany Anybody Porta Westfalica

    @apaderno I think this was misunderstood in #17 while #18 might read a bit unfriendly, but I'm sure wasn't meant that way. :)

    To clarify things:
    This is some kind of DX issue due to confusing. I'll try to explain it more simple:

    When implementing HOOK_theme_suggestions(_alter) and you're providing a suggestion like

    $suggestions[] = 'lorem-ipsum__blabla';
    

    that's of course disallowed because of the hypen ("-") and *should not work*

    BUT looking into the twig debug output, this produces
    'lorem_ipsum__blabla'

    instead of showing
    'lorem-ipsum__blabla' (broken) (that's the bug mentioned here!)
    or just not adding that suggestion!

    That's the point and that's super misleading.

    So there are several ways this could be fixed:

    1. Output the wrong / disallowed in twig debug as-is, so users can see the issue!
    2. Preprocess the $suggestions consistently, so that 'lorem-ipsum__blabla' always results in 'lorem_ipsum__blabla' and works that way! Similar to what's done with CSS classes in https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Component%21Util...
    3. Detect the disalled suggestion characters log a warning

    I hope it's more clear now :)

  • 🇮🇹Italy apaderno Brescia, 🇮🇹

    @Anybody I did not misunderstand.
    I replied to Maybe strtolower() also make sense, the uppercase characters are also used 1:1. and the reply was that it is not necessary, since the characters that Drupal allows in a template filename are the characters that are allowed in PHP function names. The only exception can be hyphen characters that are converted to underscore characters, but that does not always happen.

    For each template file there is at least a PHP function whose name is taken from the template file (for example, template_preprocess_node() for the node.html.twig). When it comes to template files, the matter always involve PHP functions.

  • 🇩🇪Germany Anybody Porta Westfalica

    @apaderno thank you! So to get you right, what's your suggestion then to fix it here? From the options given in #19 (or other)?

  • 🇮🇹Italy apaderno Brescia, 🇮🇹

    @Anybody I would first check there is not code that should replace hyphens with underscores. If there is such code, I would fix it; otherwise I would add the code to replace hyphens in the suggested template name. Logging when an hyphen is replaced could help in debugging cases where the suggested template name is not picked up.

  • 🇮🇹Italy apaderno Brescia, 🇮🇹

    The documentation for hook_theme_suggestions_HOOK() should also make clearer which characters are allowed. Most implementations done by Drupal core replace periods with underscores, but media_theme_suggestions_media() goes further and uses the following code.

    $provider_id = \Drupal::transliteration()->transliterate($provider_id);
    $provider_id = preg_replace('/[^a-z0-9_]+/', '_', mb_strtolower($provider_id));
    $suggestions[] = end($suggestions) . "__provider_{$provider_id}";
    

    If the only allowed characters are letters from A to Z (lower-case or upper-case), numbers, and underscores, that should be clearly stated.

Production build 0.71.5 2024