Updated: Comment #206
Problem/Motivation
Twig debugging output is supposed to show all template suggestions. Currently, the debug output does not show all the template suggestions that the Drupal render system knows about.
Here are a few examples where this feature fails:
- Views (a core feature used in many places) templates do not show any template suggestions. For example, go to
/admin/content
and look at the Filter form at the top of the page; Twig debug shows:'views_exposed_form'
, but Drupal's theme system is given this list of suggestions:['views_exposed_form__content__page_1', 'views_exposed_form__page_1', 'views_exposed_form__default', 'views_exposed_form__content__page', 'views_exposed_form__page', 'views_exposed_form__content', 'views_exposed_form']
(defined inDrupal\views\Form\ViewsExposedForm::buildForm()
). - Using the default Bartik theme, use Drupal's search and look at the list of search results. Twig debug shows:
x item-list--search-results.html.twig, x item-list--search-results.html.twig, * item-list.html.twig
, but Drupal's theme system is given this list of suggestions:['item_list__search_results__node_search', 'item_list__search_results']
(defined inDrupal\search\Controller\SearchController::view()
).
The underlying cause of this issue is when #theme
is set as an array of suggestions. The twig_debug output only works* when #theme
is a string or when suggestions are added via suggestions_alter
hooks.
* where "works" is defined as super buggy output, showing duplicates and out-of-order suggestions. See #2752443: Incorrect order and duplicate theme hook suggestions
When #theme
is set as an array of suggestions, Drupal\Core\Theme\ThemeManager::render()
will call the theme suggestions alter hooks with the base hook and will conditionally add an entry from the #theme
array if that entry is actually implemented in the system. If none of #theme
array suggestion entries are implemented, only the base hook is sent to the theme suggestions alter hooks and the code handling the twig debug comments code.
Here's a specific example of what Drupal core does now:
#theme
is set to['item_list__search_results__node_search', 'item_list__search_results']
inDrupal\search\Controller\SearchController::view()
Drupal\Core\Theme\ThemeManager::render()
will look for implementations of the theme suggestions in the#theme
entry.- If the only implemented Twig template is
item-list.html.twig
(like in the Stable themes), the theme suggestion alter hooks will be passed this list of suggestions: item_list, [list of suggestions fromhook_theme_suggestions_item_list()
(if this function existed)] - If the
item-list--search-results.html.twig
Twig template is implemented (like most core themes), the theme suggestion alter hooks will be passed this list of suggestions: item_list, [list of suggestions fromhook_theme_suggestions_item_list()
], item_list__search_results
- If the only implemented Twig template is
- The
twig_render_template()
generates the Twig debugging comments by looking at the list of theme suggestions returned by the theme suggestion alter hooks (passed in$variables['theme_hook_suggestions']
). But it doesn't know about theitem_list__search_results__node_search
suggestion in Step 1, so the list is wrong.
Proposed resolution
Theoretically, we could fix this by #2923506: Deprecate and discourage use of the "array of theme hooks" feature, but that is a loooong road of deprecations that is completely stalled. And since the "array of theme hooks" feature has been in core since 6.x and has been used frequently in contrib and in core 9.1.x and earlier, we need to fix the bug now.
To minimize the possible side effects to changing how Twig chooses templates, we should not modify any existing lines of code inside OMG. No. This is technically possible, but a "proper list of theme suggestions" requires super ugly code if we're not going to modify existing lines of code. See comment #204 for proof in the form of a patch that implements this option.Drupal\Core\Theme\ThemeManager::render()
and only add code to generate a proper list of theme suggestions.
Instead of conditionally appending suggestions from #theme
on to the end of the suggestions from hook_theme_suggestions_HOOK()
, we should always append those suggestions so that hook_theme_suggestions_alter()
and hook_theme_suggestions_HOOK_alter()
can see them.
Extending the example above, no matter what item_list Twig templates are implemented, the theme suggestion alter hooks will be passed this list of suggestions: item_list, [list of suggestions from hook_theme_suggestions_item_list()
], item_list__search_results, item_list__search_results__node_search
This will make it easy to output an accurate suggestion list in twig debugging comments.
We will need to add extensive tests to make sure we aren't breaking anything.
This solution also fixes #2752443: Incorrect order and duplicate theme hook suggestions.
Remaining tasks
- Final polishing and reviews
- Commit
User interface changes
Twig debug output will display all templates suggestions, no matter the source of those suggestions.
API changes
The theme suggestions from #theme
will always be added to the end of the hook_theme_suggestions_HOOK()
list of suggestions before sending them to hook_theme_suggestions_alter()
and hook_theme_suggestions_HOOK_alter()
.
Release notes snippet
Previously, theme suggestions from #theme
(like views_exposed_form__content__page
) were only sometimes added to the $suggestions
variable passed to hook_theme_suggestions_alter()
and hook_theme_suggestions_HOOK_alter()
. Now those suggestions are always passed to alter hooks.
Comment | File | Size | Author |
---|---|---|---|
#245 | 2118743-nr-bot.txt | 90 bytes | needs-review-queue-bot |
#234 | 2118743-231.patch | 81.59 KB | Giuseppe87 |
Issue fork drupal-2118743
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #1
dawehnerThank you for creating the issue.
Before
:
After
Comment #3
oetseli CreditAttribution: oetseli commentedI have another suggestion and patch that changes the way Twig debug prints out the theme call itself.
Instead of doing this:
It does this:
Views does this a lot. That there are no theme_hook_suggestions, but rather a lot of hooks in theme()-function's $hook array. What do you think?
Comment #6
oetseli CreditAttribution: oetseli commented3: theme-change-twig-debug-theme-call-to-theme-hooks-2118743-3.patch queued for re-testing.
Comment #8
star-szrSo far my work on #2201781: Pass all theme hook suggestions to theme preprocess functions to allow for suggestion-specific overrides looks like it will fix this. Postponing for now.
Comment #9
star-szrRe-activating because that issue got shot down.
Comment #10
dawehner@Cottser
Do you have a suggestion which of the two aproaches would you prefer?
Comment #11
lauriiiI like #1 more so I rerolled it and most likely fixed tests.
Comment #13
lauriiiComment #15
star-szrThanks for working on this @lauriii!
Comment #16
dawehnerI wanted to ensure that we have some test coverage in views, you never know.
Comment #20
botanic_spark CreditAttribution: botanic_spark commentedIt seams that TwigDebugTest is not passing because debug info is in wrong order. This happens because of the array_reverse in
My solution is to reverse it again in the twig.engine but just in case when #theme element is passed.
Comment #21
dawehnerThis is really not obvious ... instead of explaning WHAT you do here, try to explain WHY you do it, because this is information not encoded in the code itself.
Comment #22
botanic_spark CreditAttribution: botanic_spark commentedI spoke with @lauriii alot about this during last few days.
In theme.inc there is this part
For some reason array_reverse is done here, but this is only for case when suggestion is passed directly to theme function. For example
'#theme' => 'node__foo__bar',
But the thing is that in all other cases, the suggestions array is not reversed and this is braking the tests.
In twig.engine this array is reversed again in order to display debug info properly, but order of suggestions is different for case when
'#theme' => 'node__foo__bar',
is passed.So my "dirty" fix was to reverse it again in twig.engine but just for that specific case. This made tests to pass.
Both @lauriii and me agreed that this is not the solution, but it made clear what is the problem, and we need to avoid this double or even triple reversing.
I will keep working on this, and post some update when i have more info.
Ofcourse, every suggestion is welcome.
Comment #23
botanic_spark CreditAttribution: botanic_spark commentedTo avoid multiple array inversion i just removed
from theme.inc
This was already tried in #11 but for some reason tests were not passing.
I have tried this correction on my local testbot instance and it's passing.
Comment #26
lauriiiComment #27
jjcarrionI have made just the reroll
Comment #28
jjcarrionComment #29
dawehnerWe do have tests, not part of twig tests itself but at least for the views problem.
Comment #30
alexpottIt seems strange and slightly dangerous that we're altering this code to improve debug output.
Is this even needed now? And if we do remove this - can be we remove the array_unique in twig.engine?
Comment #31
botanic_spark CreditAttribution: botanic_spark commentedComment #32
Antti J. Salminen CreditAttribution: Antti J. Salminen commentedComment #33
Antti J. Salminen CreditAttribution: Antti J. Salminen commentedAdding a test that should expose a problem in the current implementation: When theme is passed an array of hooks that contains a base hook (as is often the case), using hook_theme_suggestions_HOOK doesn't work. This is because the base hook from the array will always be found before the suggestion. Working on a fix...
#30:
1. ThemeManager might be a more natural place for the debug output as the selection of the hook isn't really the theme engine's concern. Would need a more general equivalent for twig_debug to move it there.
2. I think we can get rid of it. The current patch relies on it but after fixing the issue that the test exposes, the cases where it's needed should also go away.
Comment #35
Antti J. Salminen CreditAttribution: Antti J. Salminen commentedAdding a couple of tests for the debug markup. I noticed the current committed version has duplicate suggestions in the debug output when there are specific suggestions in the hook passed to theme(). Also added a basic test for the array of hooks.
Comment #36
Antti J. Salminen CreditAttribution: Antti J. Salminen commentedJust triggering test runs, still needs work.
Comment #39
star-szrJust want to say thanks for working on this @Antti J. Salminen!
Comment #40
star-szrTagging and bumping.
Comment #41
Antti J. Salminen CreditAttribution: Antti J. Salminen commentedHaven't been able to work very fast on this unfortunately but here's some progress. I believe this would be what the approach suggested in the first patch looks like with everything in place.
What do you think about the assumption this makes that for arrays, base hooks should only be the last element? Without it those base hooks override direct suggestions but not other suggestions. It would mean it's impossible to represent the complete suggestions order with just a flat list.
Comment #43
Antti J. Salminen CreditAttribution: Antti J. Salminen commentedThe tests-only patch had some issues that should now be fixed. Additionally made some extremely small adjustments. Also adding an interdiff to the patch in #27. I'm thinking the suggestion selection in theme() could be reworked a bit from this.
Comment #45
rteijeiro CreditAttribution: rteijeiro commentedFixed a small nitpick.
Comment #46
Antti J. Salminen CreditAttribution: Antti J. Salminen commentedReworked the generation of the derived suggestions, fixed an issue in the patch and improved a test to detect it. If the tests don't come up with any problems this could use some looking at by others I think.
Comment #47
akalata CreditAttribution: akalata commentedSince we're refactoring theme() function in #2348381: [META-0 theme functions left] Convert/refactor core theme functions, is this issue going to be applicable in D8?
Comment #48
Antti J. Salminen CreditAttribution: Antti J. Salminen commented#47: To me it looked like #2348381 only concerns theme hook implementations and in that case there would be no overlap.
Comment #49
akalata CreditAttribution: akalata commented@Antti, the goal of #2348381 is to replace theme() functions with template files. If we're doing that, then this issue (fixing theme() function debugging) will be useless.
Comment #50
lauriii@akalata theme() functions will be still in there and there will be contribs using them so I wouldn't say refactoring it is useless. Even though they are highly deprecated.
Comment #51
Antti J. Salminen CreditAttribution: Antti J. Salminen commented@Akalata: The debug output that is being fixed is presented as a list of templates and can be implemented with templates. Are you saying that issue plans to also replace the current suggestions?
Comment #52
akalata CreditAttribution: akalata commented@laurii @Antti ah, I see. My initial read of the issue was that template suggestions coming from *.html.twig files were working, and that ones coming from theme() functions were not.
Comment #53
star-szrThis might be a better title then :)
Comment #54
akalata CreditAttribution: akalata commentedDebugging output BEFORE
Debugging output AFTER
I don't know testing well enough to review the added tests, sorry.
Comment #55
star-szrBeen meaning to review this for a while so here's an initial pass, I'm not sure on some of the logic changes in ThemeManager just yet, need to do some manual testing/debugging on those.
Thanks for keeping on this @Antti J. Salminen and thank you @akalata for testing!
Could probably use a better summary line here, saying something about arrays.
Minor: Needs some small adjustments to meet https://www.drupal.org/coding-standards#array.
Minor: Go all the way with short array syntax, change the last
array()
:)I know it's uglier to test but IMO we should really be testing the order of the suggestions because that is very important.
Seems like this comment doesn't apply anymore so let's remove it. Correct me if I'm wrong :)
Minor: Coding standards issue, extra space inside parens.
Comment #56
Antti J. Salminen CreditAttribution: Antti J. Salminen as a volunteer commentedThanks for the nice review Cottser and also thanks for testing Akalata! I've tried to fix the issues in this patch. Also reformatted the tests and fixed some other small things I noticed.
Hoping to get feedback on the logic changes, especially the assumption that only the last direct array suggestion can be a base hook. :) It seems reasonable to me to force there to be a linear list for the suggestion priorities. It matches how it's used in the places I've found and also the original intent of the feature (going from specific to generic, alternative to just providing a pattern).
Comment #57
star-szr@Antti J. Salminen thanks for keeping on this! I definitely missed your patch in all the craziness of DrupalCon :) changes look very good!
Can you expand a bit more on this please? Perhaps with some code examples?
Comment #58
star-szrBump. Patch still applies :)
Comment #59
hussainwebRerolled.
Comment #62
hussainwebFixing tests (hopefully).
Comment #63
akalata CreditAttribution: akalata commentedI will take a look at reviewing this.
Comment #64
akalata CreditAttribution: akalata commentedAnother round of manual testing of template suggestion debug info:
Content admin page as a good set of debug messages to look at for *.html.twig suggestions: I compared the beginning of the system_main content block down to just inside the beginning of the exposed filters form. Existing correct template suggestions have not changed, and new template suggestions are showing up for views_view and views_exposed_form hooks.
I was trying to find debug info for the few remaining core theme functions, though I realized that those might not be showing up because they aren't Twig-based? If this is the case, the "THEME DEBUG" and "THEME HOOK" might be misleading (since I expected theme hook information to also show up here). If we can't extend the debug info to theme_ functions, maybe we need to rename the labels? Or is this an RTFM moment since services.yml clearly states that it's just for Twig functions?
I'm not sure if the request for elaboration in #57 happened elsewhere, but I think the logic is sound (and certainly how I've expected template suggestions to work). I also tested to be sure that the order of suggestions follows the order described in the documentation (and updated documentation where it differed from actual behavior in HEAD).
Comment #65
Antti J. Salminen CreditAttribution: Antti J. Salminen as a volunteer commentedSorry, I pretty much just forgot about expanding on what I meant with the difference.
Here's an example:
I haven't seen any actual use with base hooks in the middle like this and it's definitely pointless without multiple different base hooks involved. To me it seems like it could be just a side-effect of the current implementation.
@Akalata: Thanks for the review. Your point about theme functions not having debug information is good and it is caused by the printing of the debug information being part of the Twig engine currently. I had the thought that this functionality isn't really specific to the used theme engine and could be moved to a better place myself earlier as well.
Comment #66
akalata CreditAttribution: akalata commented@Antti can you think of any case where multiple base hooks would be an optimal solution? Or where the base hook isn't the last hook in the list of suggestions?
While technically possible to do with the code, I don't think it's something that people should be doing - so I'm personally not worried about it, but interested in other perspectives.
Comment #67
akalata CreditAttribution: akalata commentedalso re: #65, adding suggestions for theme_ function implementations would be cool, but would be out of scope for this issue (but would be great to have!). Could we think about changing the label as an intermediate step?
Comment #68
Antti J. Salminen CreditAttribution: Antti J. Salminen as a volunteer commented#66: I can't think of those examples and I tend to agree with you, but I've seen one example where the base hook isn't the last. The first test in #2111079: Add @code sample and test coverage per hook_theme_suggestions[_HOOK]() and hook_theme_suggestions[_HOOK]_alter() does that. Maybe @Cottser could comment on if that's based on a real-world use case?
#67: I agree changing the label could be a good idea, not sure what the replacement would be...
Comment #69
star-szr@Antti J. Salminen I think I just did that test wrong, when you pass an array of hooks it will stop at the first one it finds so that should be reversed in the test.
Comment #70
star-szrOr in other words more specific should come first.
Comment #71
flocondetoilePatch #62 has worked fine on Drupal 8.0.1. But applying patch after upgraded to 8.0.2 add the generic suggestion (block.html.twig, node.html.twig, etc.) in first position and so is always matched.
Comment #72
lauriiiRerolled the patch
Comment #73
joelpittetUnless anybody has an issue with this solution it seems to fix things up and provides tests.
Screenshots.
Before
After
@akalata could you open up a follow-up to change the label and discuss what it should say so we can fix this separate. I also think it may alleviate confusion but since theme functions are deprecated or being deprecated it may be only good for D7.
Comment #74
alexpottI'm not sure about this change - doesn't it mean we're doing a lot working stuff out in the ThemeManager that we will only use if twig debug is on?
Comment #75
dawehner@alexpott What is the problem with that? Its not like dramatically more work and it actually fixes a bug
Comment #76
alexpottI think this can just be !$hook_found now... which helps with working out how all this hangs together.
This comment needs work. I suggest something along the lines of:
Get the generic fallbacks. If a valid hook implementation has not been found, check each fallback until one is.
The
If there's still no implementation, log an error and return an empty string.
is redundant and wrong - we return FALSE. I was going to suggest moving to above the relevantif
but doing that made me realise this.Comment #77
Antti J. Salminen CreditAttribution: Antti J. Salminen as a volunteer and commented@alexpott: I made the suggested changes, had to do a few more to let $hook_found be always defined. Does this look better?
Comment #78
Antti J. Salminen CreditAttribution: Antti J. Salminen as a volunteer and commentedForgot to set needs review to see if it passes tests.
Comment #80
Antti J. Salminen CreditAttribution: Antti J. Salminen as a volunteer and commentedOops, helps to have the right branch. This should apply cleanly I hope...
Comment #82
lauriiiSimple reroll
Comment #84
lauriiiThis probably helps a bit
Comment #87
lauriiiThis bit of code is very complex and for that reason hard to understand. I tried to add few lines of docs to make it more clear what happens there and for what reason. Let's see whats test bots response for this.
There was also problem where we were setting log messages in unnecessary cases.
Comment #89
lauriiiComment #90
star-szrI think the wording might be a bit confusing here. Maybe either no comment at all (or consider a slightly more descriptive var name like original_hook_is_array) or something like "All theme hooks are converted into an array, so we create $hook_is_array so that it can be checked later."
Can we check $hook_is_array here before doing the foreach? Otherwise I think we do unnecessary processing when $hook is a string. What do you think?
Comment #91
greggmarshall#89 solved an issue I was having with templates for certain entity references inside the paragraphs module, thanks.
Comment #93
Antti J. Salminen CreditAttribution: Antti J. Salminen as a volunteer and commentedHow about this for avoiding the is_array(). Also tried to clarify the comment accompanying the rather weird behavior related to the watchdog messages vs. arrays of hooks.
Comment #94
star-szrI think this fix could be included in 8.1.x.
Comment #95
clemens.tolboomPlease add this to 8.1.x :-)
I needed this info for 8.0.5 so added kint($hook) in Drupal/Core/Theme/ThemeManager.php:136 render method like
Comment #96
dawehnerNice, we moved logic from the template engine more towards the theme layer. This is great, given that the theme engine should not have to implement this complex logic.
We also extended the test coverage for the new capability to see views template suggestions ...
The changes to the
ThemeManager
look sane as well.out of scope of this issue: On the longrun I'm wondering whether we could move the logic to retrieve theme registry entries to the theme registry and let the theme manager deal with the rest of the problem space
Comment #97
star-szrSince this changes so much I don't think we should commit it to 8.1.x at this time (too risky), but we can get it into 8.2.x I think :)
Comment #98
catchI don't understand 'their every element is allowed to be unimplemented' - is this 'every element of the array'?
Comment #99
Antti J. Salminen CreditAttribution: Antti J. Salminen as a volunteer and commented@catch: Yeah, that's what the comment is trying to say.
Comment #100
star-szr@lauriii and I pair reviewed this patch for a while a couple days ago and I spent several hours today going over everything and trying some real world examples to see if I could break it.
Summary of what I'm asking for after this review, detailed thoughts below the horizontal rule:
$variables['theme_hook_suggestions']
.Drupal\Core\Theme\ThemeManager::render()
that we are changing here - deriving theme suggestions especially.$variables['theme_hook_suggestions']
.For the "pattern" used to derive more generic suggestions, I think there is a behaviour change and perhaps a bit of an assumption that the last item in the array of theme hooks is "the original hook" - there is a comment that says:
Before: First theme hook in the array that is implemented, OR if none are implemented, the last theme hook.
For this last part I'm not sure if it's a bug or by design. I'm talking specifically about the
$hook = $candidate;
afterforeach ($hook as $candidate) {
. When nothing is found $hook will be the last candidate that was checked.After: Always the last theme hook in the array regardless of implementations (via array_pop).
Below are a couple example render arrays you can use to test this behaviour with and without the patch (the behaviour in terms of which templates are picked up seems to stay the same).
With and without this patch, using the first render array a template of item-list--stuff.html.twig won't get picked up. I don't think any of the current web tests cover that behaviour either, so we should add that too IMO.
Based on the logic from twig.engine we are porting in here I think it might make more sense to use
$original_hook
rather thanarray_pop($theme_hooks);
.I think it would be less confusing to use $hook here rather than $candidate because we know $hook is a string.
This logic could use a comment to say that it's appending the base hook or something along those lines.
Comment #101
Antti J. Salminen CreditAttribution: Antti J. Salminen as a volunteer and commented@Cottser:
Thanks for taking the time to thoroughly review!
Regarding some of the items:
2.
If the hook is implemented the currently committed version doesn't use the pattern so it doesn't really matter. The only case where this would make a difference as far as I know is the case that I mentioned in #41, #56 and tried to describe in #65.
The reason was that If alternate base hooks (with or without a suggestion) are permitted in elements besides the last one, the list of possible templates to implement won't be just a single list of template names. It means that more suggestions are possibly inserted into the list by hook_theme_suggestions_HOOK() in the case that they have an implementation in the registry.
At least this is what I believe the implications are, it's been a year since I really looked at this in depth. My assumption was that this is not something that was ever intended as a real use case, I've found nothing implying so in code or ancient issue discussions and it certainly makes the way the hooks are selected more complicated.
3.
As outlined above, If this was changed to $original_hook, the list of hooks that twig_debug outputs would change depending on what you have implemented and would not show the full list of possible template/theme function names.
4.
Don't you think the explanation is the part that is actually useful about the comment? Without it, it just states the exact same thing you can see from the lines it's commenting on. I can't really tell what @catch's stance on this was from his comment.
The reason for the behavior isn't obvious to me from the code and I added the explanation because I was confused enough that I didn't properly address it in a version of this patch.
Comment #103
kclarkson CreditAttribution: kclarkson commentedWould love to see this move forward.
Comment #104
Antti J. Salminen CreditAttribution: Antti J. Salminen as a volunteer and commentedI looked at the current functionality and the patch to carefully look at the possible concerns in detail and I noticed there is one issue that needs addressing related to what @Cottser wrote previously: If there is a hook derived from a base hook in the array that does not follow the naming pattern of suggestions (name doesn't start with the base hook and have the suggestion(s) separated with '__'), the displayed base hook for the Twig debug output will not be correct.
Other than that I believe what I wrote before is correct provided that the assumption I have mentioned previously, namely that all the hooks in the passed theme hook array *must* have the same base hook holds true. I hope that I properly addressed the other concerns in @Cottser's review there in that previous comment. I think coming up with a fix for the remaining problem and adding some tests that expose the assumptions/behavior for added clarity and coverage as he mentioned is a good way forward. I've also searched all of contrib for usages where the assumption regarding the base hook would be a problem, I'll report my findings once that's finished.
Comment #105
lauriii@alexpott, @Cottser, @joelpittet, @xjm, and I discussed this and agreed that this is a major bug because of the widespread TX issues this causes. This mostly effects theming Views, which is a common use case. There are no known workarounds for this.
This is also a regression from Drupal 7 where views used to provide this information in the Views UI, and now it is not available anywhere.
Comment #107
B-Prod CreditAttribution: B-Prod commentedThis patch applies specifically to Drupal 8.3.0, to be used in a Composer file.
Comment #108
mikeker CreditAttribution: mikeker as a volunteer commentedI'm working (off and on) to incorporate @Cottser's feedback incorporated into a new patch during the DC Baltimore sprints.
Comment #109
mikeker CreditAttribution: mikeker as a volunteer commentedPosting my work in progress. Changes in this patch:
$original_hook
variable as it is not neededI still need to address #100.2.
Comment #110
mikeker CreditAttribution: mikeker as a volunteer commentedThe testbot is happy, but back to Needs Work until #100.2 is addressed. Also realized we're missing tests for when there is an array of options that aren't implemented but one of the derived options is.
Comment #111
clemens.tolboomIs the _original theme hook_ or the _candidate_ supposed to be saved? Either adjust the comment or revert $variables.
Comment #112
mikeker CreditAttribution: mikeker as a volunteer commented@clemens.tolboom, thank you for the review.
They are the same. Earlier, we set a variable named
$original_hook
that was only used to set$variables['original_hook']
. That seemed extraneous to me, but I suppose it did help with readability...Comment #113
mikeker CreditAttribution: mikeker as a volunteer commentedI believe that #2752443: Incorrect order and duplicate theme hook suggestions is the appropriate place to handle the logic side of this issue, namely the order of suggestions and whether derivative suggestions are used for both string and array
#theme
items. I *think* this addresses @Cottser's concerns in #100.2 so I'm removing the Needs followup tag.The tests I considered missing in #110 should probably be handled in #2752443: Incorrect order and duplicate theme hook suggestions as well, so I'm removing the "Needs tests" tag. I believe we've got the tests we need to cover the fact the debug info doesn't appear. Plus some extras that would be better handled in the followup (see the interdiff).
Comment #114
mikeker CreditAttribution: mikeker as a volunteer commentedAdded a draft CR.
Comment #115
hanoiiI just tried this and it works very nice and for twig debugging views is almost a must.
Comment #116
lauriiiThanks for your help trying to get this issue landed. I'm starting to be pretty confident about this, but there are a few things I think we should still try to solve here.
We will have to change this since the $hook is now always an array. We probably should also add test coverage for this since the tests are passing and it seems like this was broken.
I don't fully understand why is the original theme hook now determined. Probably we should find this value in another way than we do currently or at least document how it is determined.
Comment #117
mikeker CreditAttribution: mikeker as a volunteer commented@lauriii, thank you for the review!
re: #116:
I think there's still plenty of places we call
render
with a string instead of an array for $hook. And we don't force $hook to be an array in the code so I don't think this is correct.Agreed, though, I couldn't find a test for the warning when an invalid single template string is passed.
That makes two of us... :)
Honestly, that was in the original code so I think the hope was to change as little as possible. There is also a lot of confusion on how theme suggestions should be ordered (least-to-most specific or the opposite) and how derived suggestions are to be included in the mix. Is there a source of truth for how this should be handled?
Comment #118
clemens.tolboomNeeds work?
Comment #119
mikeker CreditAttribution: mikeker as a volunteer commentedRerolled. Tests moved to core/modules/views/tests/src/Functional/ViewsThemeIntegrationTest.php.
Comment #120
mlncn CreditAttribution: mlncn at Agaric for Drutopia commentedYay!!! Template suggestions for Views! Fantastic work everyone.
Working great.
Comment #122
bobthebuilder CreditAttribution: bobthebuilder commentedThe patch, #119, fixed the problem for me as well. Thanks.
Comment #123
star-szrThanks, everyone! Happy to see this RTBC. I got part way through a review and this is looking very good overall.
The change record should probably also indicate that
$variables['theme_hook_suggestions']
will be changing in the scenario in question here (array of theme hooks passed to #theme).Some minor feedback:
Minor: These should all use the short array syntax (
[]
) per https://www.drupal.org/docs/develop/standards/coding-standards#array.This inline comment looks like it could use an update since the code is now doing something more generic.
I think this part could still use a comment, mentioned in #100.6. I don't think it's obvious what's happening here.
Comment #124
mikeker CreditAttribution: mikeker as a volunteer commented@Cottser, thank you for the review!
I've replaced all long-syntax array declarations with short-syntax array declarations, some of which were handled in other bug fixes and I think I may found another couple along the way. Anyhow,
So I think that should cover it...
Updated the inline comments to (hopefully) better reflect what's happening in the code.
Comment #125
mikeker CreditAttribution: mikeker as a volunteer commentedOops.
Comment #126
bapi_22 CreditAttribution: bapi_22 at Cybage Software Pvt Ltd. commentedThese are the list of theme hook suggestions for views module which has not included in suggestions. So it will not display while twig debugging.
views-view--foobar--page.html.twig
views-view--page.html.twig
views-view--foobar.html.twig
views-view.html.twig
views-view-unformatted--foobar--page.html.twig
views-view-unformatted--page.html.twig
views-view-unformatted--foobar.html.twig
views-view-unformatted.html.twig
views-view-fields--foobar--page.html.twig
views-view-fields--page.html.twig
views-view-fields--foobar.html.twig
views-view-fields.html.twig
Comment #127
bapi_22 CreditAttribution: bapi_22 at Cybage Software Pvt Ltd. commentedHi,
This patch will create possible template suggestions for views template.
In same way we can create suggestion for views display and views field templates.
Comment #128
markhalliwell@bapi_22, please don't hijack an existing issue. The patch in #124 is the correct one for this issue.
Comment #129
bapi_22 CreditAttribution: bapi_22 at Cybage Software Pvt Ltd. commentedYes markcarver,
Its great job. Its a complete suggestion for views as well as its child elements.
Comment #131
Antti J. Salminen CreditAttribution: Antti J. Salminen as a volunteer and commentedThanks to everyone who's been working on this. Great to see it moving forward!
I don't understand why this got changed and I'd still like to see something closer to the older comment that actually tried to explain why this exception exists. I believe comments should primarily be used for answering why the code does what it does and only repeat what it does when that is non-obvious to a less-experienced reader (for example summarising larger block of code or some unfamiliar idiom). See also: http://wiki.c2.com/?CodeComments
If you don't think the explanation is needed here just removing the comment altogether would be better because the code is already very simple and clear. In general I think comments shouldn't be used too sparingly but the current version just doesn't add much.
Comment #132
frobWe could almost just concatenate the two comments. The wording is still a little awkward.
What if we changed it to:
How does that sound?
Comment #133
star-szrI like the direction you're both going, here's a slightly reworded suggestion based on #132:
Comment #134
mikeker CreditAttribution: mikeker as a volunteer commentedI went back and forth between which sounds better:
or
I went with @Cottser's suggestion because he has core commit permissions and I don't. :) But I'm happy to switch it if folks feel the second is more clear than the first.
Also, pushing this back to 8.4.x in hopes of getting it in the next release. As per the allowed changes, I feel this falls under non-disruptive bugs where the disruption (very, very low) outweighs the benefit (we can see Views' theme suggestions!).
Comment #135
star-szr@mikeker thanks :) I thought that wording was awkward as well but just left it as is (it's from HEAD).
What about something along the lines of:
Could probably be merged into one sentence but not sure how to do that smoothly at the moment.
Zooming out for a second, maybe it's a bit weird that we support this, in the form example it seems like you'd want to at least have a fallback of
form
in case no specific suggestions are implemented but that also seems out of scope for this issue! :)+1 to hopefully getting this into 8.4.x.
Comment #136
mikeker CreditAttribution: mikeker as a volunteer commentedYes, I like the version in #135 much better.
Out with the old, in with the new! :)
Comment #137
mikeker CreditAttribution: mikeker as a volunteer commentedMaybe handled in #2752443: Incorrect order and duplicate theme hook suggestions? But, agreed, definitely outside the scope of this issue...
Comment #138
star-szrThanks @mikeker :)
Seems like we're just down to nitpicks at this point :) I've been poking at this patch as much as I can recently and as much as I try I can't find anything functionally wrong with it.
I will say, one thing that still feels a bit weird is the logic for the
THEME HOOK:
part in Twig debug. However, I also think that is OK to be out of scope here, what we have here is such a massive improvement and I don't want to delay things any further. I created a new issue to discuss what to do with that: #2901599: Decide what to display for 'THEME HOOK' in Twig debug for special cases such as arrays of theme hooksMinor but going through this again and this chunk of docs only refers to hook singular where it could be either singular or plural at this point. Some suggested wording:
This should be
hook_theme_suggestions_HOOK()
(suggestions plural, add parens).Comment #139
mikeker CreditAttribution: mikeker as a volunteer commented@Cottser, thanks, as always, for the review!
Nits have been picked.
I completely agree. Thank you for opening the followup for that.
Comment #140
joelpittetI'm a bit concerned about this change:
That changes the meaning of that variable, doesn't it? It was the incoming
$hook
variable before and now it's the last found$candidate
.In most cases it would be the first one in the array, but the way it's written if it doesn't find it, it will keep moving through candidates.
Comment #141
star-szrThanks for taking another look @joelpittet :) The logic looks a little bit different but to my eyes ends up the same.
Before:
After:
In both cases, the value that ends up as theme_hook_original (original_hook/candidate) is either:
break
in both cases.Before, the code will break out of the loop and hit the
$hook = $candidate;
line outside of the loop. After, it's set before thebreak
.$hook = $candidate;
line (or the original $hook if it's a string passed in). After, it's the fact that we're using the loop variable of$candidate
to set theme_hook_original. In both instances, the variable used to populate theme_hook_original will be the last (or only) theme hook passed in.Comment #142
star-szrComment #143
Anonymous (not verified) CreditAttribution: Anonymous commented#139 patch is very useful! Now information about the suggestions displayed much better. It really saves time. Many thanks!
Comment #144
joelpittetOh weird,
theme_hook_original
really isn't that in some edge cases(it's mostly that, and sometimes last found hook)... thanks for double checking that @Cottser, we are good here then:) No harm, no foul.Comment #146
mikeker CreditAttribution: mikeker as a volunteer commentedLooks like an unrelated test failure, requeuing.
Comment #147
mikeker CreditAttribution: mikeker as a volunteer commentedTest is back to green. Setting the status to where it was.
Comment #148
xjmAdding to a queue of issues for the frontend framework managers. Thanks!
Comment #149
dkre CreditAttribution: dkre commentedThis is a massive quality of life and time saver for theming - Thank you!
This applies cleanly on 8.3.x
Comment #150
mikeker CreditAttribution: mikeker as a volunteer commentedMinor IS updates.
Comment #152
mikeker CreditAttribution: mikeker as a volunteer commentedAgain, the test failures seem unrelated. Re-queuing.
Comment #153
mikeker CreditAttribution: mikeker as a volunteer commentedTestbot came back green so I'm resetting to the status in #144.
Comment #154
Anonymous (not verified) CreditAttribution: Anonymous commentedLooks like @Cottser (Framework Manager - Frontend maintainer) already implicitly approved the last patch in #141. And all his points are addressed.
Also we have RTBC (#144) from @joelpittet (Theme API maintainer).
Comment #156
frobLooks like the test failing is systemic to an issue with the testbot and not to do with with anything related to the patch.
Comment #157
Anonymous (not verified) CreditAttribution: Anonymous commented#139 contains 3 fail cases:
Issue with map of random fails: #2829040: [meta] Known intermittent, random, and environment-specific test failures
If we always will qualify random faults (mentioning them in the related issues, or creating new issues), then we will deal with them much more quickly.
I already did all this, just wanted to remind about good practices for random fails.
Comment #158
frobI was unaware of that protocol. thank you.
Comment #159
shaalI tested https://www.drupal.org/node/2118743#comment-12218334 patch with 8.4.0, and it works great!
Comment #160
Anonymous (not verified) CreditAttribution: Anonymous commented#158: no problem, and thanks for understanding!
#159: good point! I also tested the last patch (#139) on 8.4.2 and it works like a charm!
Comment #161
xjmThanks everyone for your ongoing work on this issue!
Since this issue has been at RTBC awhile, @lauriii, @catch, @effulgentsia, and I discussed this issue today to figure out how to move it forward. There are a couple of reasons we have hesitated on it:
Given these two things, we discussed whether it might be better to commit this issue very early in the development cycle for a minor release (for example, right away when we open 8.6.x in January, rather than now just two months before the alpha). Sometimes for things like this we postpone the issue and schedule it for a certain date, but we want to be ready to commit it other than that before we do so (in case there's other points that come up during review that need to be addressed).
Other than that, the main thing that's blocking this is just committers having enough time to give it the careful review it needs. In the meanwhile, though, there are a couple things that contributors can do to help give us confidence in the patch.
I'm going to keep the issue at RTBC for now since there aren't any specific blockers currently. @lauriii might have additional feedback on the issue as well. Thanks everyone.
Comment #162
hanoiiI can add a bit to 1. but isn't this patch something that affects only twig debug, or does it affect anything else?
In any case, I am running several productions sites, both 8.4.x and 8.3.x with #119 without any issue so far, and this patch has proven very useful for local envs.
Comment #163
pingers CreditAttribution: pingers as a volunteer and at University of Adelaide commented1. Are you using this patch on a production site? If so how is it working, and what side effects if any have you noticed?
- We've been tracking the patch in production on about 20 sites (same install profile) since May 2017, without any noticeable side-effects.
The install profile uses paragraphs module with about 15 unique paragraph types on a custom, style guide driven theme.
Comment #164
markhalliwellThis issue is of very little importance.
It only really focuses on the debug output concerning like 0.01% of code that actually implements the rarely used "theme hook array" (a.k.a. views).
This is a legacy "feature" that really should be converted to use the, now, proper hook: hook_theme_suggestions_HOOK().
If anything, I would say that the real "major bug" here lies within Views and its need to use the proper hooks now. Thus, I'm downgrading this issue to "Normal".
The related issue I'm attaching now is a real major bug in theme hook suggestions that affect which suggestion is actually used and should probably be fixed before this one. It may even semi-fix this one.
FTR, Bootstrap does little to nothing with the debug output of theme hook suggestions. That being said, given that this patch is altering the underlying suggestions array, I wouldn't be surprised if this patch actually improves the base theme in some way, regarding Views anyway.
Normal string theme hooks still seem to function the same, from what I can tell by reviewing the patch.
---
Initial quick review of latest patch:
I'm really not a fan of typecasting this. It just makes this more unnecessarily complicated.
As I mentioned above, this "theme hook array" feature is rarely used. If anything, I think this feature should be removed entirely in the future.
For BC reasons, we can keep this "array theme hook" detection (
is_array
) and just log a warning that this will be removed in the future and to use the proper hooks for theme hook suggestions.Changing the overall structure to "just deal with arrays" is what has gotten the theme system in the "Render arrays of doom" mess we're in now.
Perpetuating this mentality in code is wrong.
Especially in the theme system.
I don't like this unnecessary change.
It may seem trivial, but given how complex this method/topic is, keeping semantic variable names is important for future work.
Comment #165
markhalliwellA little history:
In D6, #141730: Allow theming system to use wildcards introduced this "theme hook array" feature. It was, essentially, the first iteration of "theme hook suggestions" in core.
In D7, #653622: Make the '__' pattern for theme suggestions easier to use and #678714: Unify use of theme hook / template suggestions, fix clobbering problems, and improve suggestion discovery performance moved to a more "internal" way of providing theme hook suggestions via
$variables['theme_hook_suggestions']
in the preprocess phase.In D8, dedicated hooks were introduced for theme hook suggestions. These hooks removed the internal preprocess suggestions, but never removed the original "theme hook array" way of providing suggestions.
It's wildly redundant, unnecessary, should be deprecated and then ultimately removed.
Comment #166
dkre CreditAttribution: dkre commented@markcarver I agree with the logic but this is a further dialution of Drupal's usability for non-developers.
Basically theming shouldn't be so layered and complex. Out of the box a CMS should be able to create logic, clean markup which can be altered without too much fuss. By removing the debugging output it requires a further step to markup alterations.
The hook system isn't overally intuitive due to Drupal's endless nesting of elements which isn't always easy to discover or interpret. Worse it requires an understanding of the underlying Drupalisms. Often I find myself poking away to see what effects the markup at the right point in the render chain.
My biggest concern with only using suggestion hooks is that it seems like an impossibility to document it effectively to allow new Drupal users to make frontend alterations as you would expect in a modern CMS.
Comment #167
markhalliwellNo, it's not. It's putting the blame where it belongs: with Views. If Views used D8's new theme suggestion hooks, then non-developers would be able to see the proper theme hook suggestions in the debug output. I think you are conflating what I have said above with an assumption that this is somehow shifting the focus from "fixing a problem" to "telling non-developers they have to code". That isn't true. What I am proposing would actually help everyone, even non-developers.
It's a nice sentiment and I generally agree. However, Drupal has over a decade of its own history. How a page is ultimately "themed" in Drupal has evolved over time (rather slowly I might add). There are years of technical debt and antiquated techniques still being used to this day. As I outlined above in #165, this issue is a direct symptom of using said outdated theming practice (from D6).
No one is "removing" the debugging output. What I have suggested will actually make the debugging output work properly, because it wouldn't have to deal with "arrays of theme hooks".
As someone who has spent their entire career in understanding the fundamentals of Drupal's theming layer, I couldn't agree more. However, you are again assuming that I am pushing the responsibility of fixing this onto non-developers. That is not the case. Views is in core. Developed by and maintained by core developers. They are the ones that should use these hooks, not non-developers.
The documentation would be the (proper) debug output. I still think you are confusing what I am saying here. I will try to break this down and explain in greater detail:
ViewExecutable::buildThemeFunctions is responsible for generating the necessary theme hooks.
As you can see, this is creating an array of theme hooks (not theme hook suggestions) that will be passed directly to
#theme
(typically, this is just a single string).This technique is very old, outdated and harkens back to the days of D6. merlinofchaos, who also developed Views, created this "array of theme hooks" technique (#141730: Allow theming system to use wildcards) that was added to core (likely a direct correlation to Views). In the 11+ years that this "array of theme hooks" feature has been in core, almost nothing has really used it except Views.
Even though Views is now in core, this specific aspect of Views (providing an "array of theme hooks") really hasn't changed all that much. It is severe technical debt that was simply inherited.
Early on in D7's development (#653622: Make the '__' pattern for theme suggestions easier to use), it was realized that passing
array()
each time and duplicating the same prefix in hooks was entirely unnecessary and simply added more unnecessary technical debt:This is when the "array of theme hooks" technique should have been deprecated and Views' code adjusted to using proper preprocess alterations to the
$variables['theme_hook_suggestions']
. However, given that Views was still contrib and my own personal experience with trying to fix bugs in Views was always met with hostility and a superiority complex, I can see how that never happened.Now that, in D8, there are dedicated hooks for providing and alter actual theme hook suggestions, Views' code should be fixed (moved) to utilize them.
This simple change would just mean that instead of providing an "array of theme hooks" to
#theme
, it would provide a single theme hook and then these "arrays of theme hooks" would then be provided as proper suggestions later when they're actually needed.This would make everything "just work", even for non-developers.
Comment #168
markhalliwellComment #169
dawehner@markcarver Thank you for the great suggestion. Let's see whether its possible to implement that at least for views: #2923634: [PP-1] Use hook_theme_suggestions in views
Comment #170
markhalliwellComment #171
frobSo what is the actual problem with the patch? This is an RTBC patch that fixes an issue with DX.
Can we just revert this patch after the underlaying issue with views is fixed? I just don't understand why this was changed from RTBC to needs work. What work needs to be done?
Comment #172
markhalliwellSee my review in #164.
Comment #173
frobI'm sorry, its been a long week already. I completely missed the later half of your comment. I read the whole rest of the thread though. Sorry.
Comment #174
markhalliwellI hear that, no worries :D
Since the "array of theme hooks" feature has been in core since 6.x, this is still technically a bug and needs to be fixed. So no, I don't think it should be reverted after Views gets fixed. It should, however, be removed entirely once all deprecated code is removed in the future.
I just don't think that focusing on this particular issue is all that important right now considering that it's just a symptom due to outdated theming techniques.
The end goal of the OP (Views theme hook suggestions) can be fixed using the proper hooks first, without fiddling around with the internals of an already extremely complicated theme "system".
In my mind, the priority of importance is as follows:
#2923634: [PP-1] Use hook_theme_suggestions in views
#2752443: Incorrect order and duplicate theme hook suggestions
#2923506: Deprecate and discourage use of the "array of theme hooks" feature
... then this issue.
How the above issues are tackled will likely affect how this issue implements it's "fix" for displaying "suggestions" for an "array of theme hooks". Given that, I would almost say this issue should probably be postponed until then.
Comment #176
gambryI agree with #174. The work done here is as huge as important, but this fix may create more problems than the ones is trying to fix if we don't do #2923634: [PP-1] Use hook_theme_suggestions in views, #2752443: Incorrect order and duplicate theme hook suggestions and #2923506: Deprecate and discourage use of the "array of theme hooks" feature before.
So let's postpone this in favour of those three.
I'm also updating the IS to reflect these thoughts.
Comment #178
shaalIgnore these patch files (they include by mistake a /web directory)
Use #179, works like a charm
Comment #179
shaalUntil a better solution for Views suggestions is found, this patch is awesome!
I re-applied manually @mikeker patch #139
It is now working with Drupal 8.6.1
Comment #180
Ben Buske CreditAttribution: Ben Buske at werk21 commentedreroll of #179 for 8.8.x
Comment #181
shaalI rerolled #179 for 8.8.x, but kept code changes that were introduced in 8.8.x and seemed to be removed in #180
(you can see these details in the interdiff)
Comment #183
shaalRerolled #181 for 8.9 and 9.0
Comment #184
shaalHopefully #bugsmash initiative can help getting this resolved.
Comment #186
JohnAlbinIt's a major bug when the twig debugging comments that are meant to show all of Drupal's theme suggestions don't display all of Drupal's theme suggestions. In this case, the mechanism by which the feature fails to work (because of an array is used for
#theme
) is important for how to fix the bug, but not for determining its severity, IMO. These twig debugging comments are an important feature in discoverability of Twig templates.And postponing this bug for a more important refactoring was a mistake. This issue has remained untouched and unfixed for an additional 3 years because of that. Both the refactoring and this bug fix could have been done in parallel.
Having read all the previous comments, I think xjm's comment is most pertinent for finding the most expedient way to fix this bug:
She suggests a way to commit the patch in #139 to a Drupal branch before any releases are tagged. Currently, that means applying it to 9.2.x.
Alternatively, we could fix the bug by only altering the Twig debug comments and not the underlying
#theme
-handling code. Any code that looks like it needs refactoring (there's lots!) should be done in a separate issue.Comment #188
JohnAlbinI've created a merge request that uses this new approach. It adds two new read-only variables when rendering a template:
template_suggestions
: containing the full list of possible theme hook suggestionstemplate_suggestion
: containing the actual theme hook of the matched templateBecause it is only adding 2 new variables, it only adds additional lines to
core/lib/Drupal/Core/Theme/ThemeManager.php
and does not modify any of the existing lines of code. It also removes all the template suggestion-generating code intwig_render_template()
since it was buggy and all that function needs now is to just append the.html.twig
suffix to each entry in the newtemplate_suggestions
variable.This should be a completely safe patch to backport. (Though we'll need to change the PHP 7.3-specific
array_key_last()
toarray_pop()
in Drupal versions that support PHP 7.2.)The MR still needs tests. I can possibly re-use some of the test from the previous patches.
Comment #189
JohnAlbinThe fact that none of Drupal's existing tests break with this change is a good sign!
I'll see about adding in the tests from earlier patches into the MR. Also, this MR probably fixes some of #2752443: Incorrect order and duplicate theme hook suggestions too, so I could also use some of the proposed tests in that issue's patches.
Comment #190
joseph.olstadarray_key_last is fine for 9.2.x
Great work on the merge request, wow, huge improvement!
I noticed the use of array_key_last , it is (PHP 7 >= 7.3.0) only, so I switched to php 7.3.x for my test run and applied your patch on 8.9.9 , works as anticipated!
Huge improvement.
Before patch:
no views-view-field suggestions,
after patch, all the twig suggestions that we expect to see are there.
I haven't done extensive testing on this patch but it did not cause any issues in my test environment. Thanks so much for this!
Attaching a new version of the patch with changes based on the failure suggestions.
see interdiff for changes.
Comment #191
joseph.olstad**EDIT**
Still testing and reviewing, but so far it looks ok.
**EDIT**
Comment #193
joseph.olstadinitial testing is promising showing expected theming suggestions for views fields , views lists, unordered lists, and other twig theming components, however lots of warnings and notices from the twig engine, excessively (at least in my environment). 8.9.9 with php 7.3.x , bootstrap theme.
still some refinement to do and one test failure.
Comment #194
JohnAlbin@joseph.olstad I haven't gotten to try this yet, but multiple people can push to the issue fork. You can either add to the existing MR's branch or create a new branch. https://www.drupal.org/drupalorg/docs/gitlab-integration/issue-forks-mer...
I've not seen any warnings when testing on Drupal 9.2.x. What specific warnings are you seeing?
Since my last comment, I've added the tests from patch #139 and have just now fixed the broken test. The tests aren't a perfect match though because they were testing the approach in #139 and my approach is completely different. I need to add some tests that actually cover all the possible paths through the new code. So, definitely, the MR needs work.
But the MR needs review like the one Joseph started, so I'm putting this back at "needs review".
Comment #195
JohnAlbinWhile working on more specific tests for this MR, I discovered a bug in the code. (Yay, tests!) My code was inserting all suggestions returned from
hook_theme_suggestions_HOOK
andhook_theme_suggestions_HOOK_alter
in place of the one matched hook in the#theme
array, but while that follows the flow of the code in Drupal, it misrepresents how Drupal behaves when new templates are created.Here's the example:
Let's assume these templates exist:
base--from-hook-alter.html.twig
,base--suggestion1.html.twig
, and, of course,base.html.twig
.Since
base--suggestion1.html.twig
is the first match, thehook_theme_suggestions_HOOK
hook is called and will result in this array:Drupal will note that the matched hook was
base__suggestion1
and will add it as the most specific spot in the$suggestions
list, giving us:Then
hook_theme_suggestions_HOOK_alter
is called and can add stuff and move stuff around like so:My previous code was taking this entire
$suggestions
list and inserting into the theme#theme
list, like so:Since
base--from-hook-alter.html.twig
exists, themers would see the above list and think that abase--suggestion2.html.twig
template would override it, but THEY WOULD BE WRONG. Why? Because ifbase--suggestion2.html.twig
exists, then the entire$suggestions
list is inserted into the theme#theme
list, like so:As you can see the themer added
base--suggestion2.html.twig
, butbase--from-hook-alter.html.twig
is still being used since it is the most specific suggestion.That means my code is not creating a list that reflects how Drupal is behaving.
The solution is to split the
$suggestions
list into 2 parts, those suggestions before the original matched $hook (base__suggestion1 in the first part of the example above) and those suggestions after that $hook.In other words, split:
into:
Then take those 2 lists and insert the most specific suggestion before the first hook in
#theme
with the same base and the least specific suggestions before the last hook in#theme
with the same base. e.g.Take these 3 arrays:
and combine them like this:
which is what the new MR does. Oof. Yes, there's lots more lines of code, but it still doesn't modify any existing code, it just adds lines to support the 2 new variables.
I still need to finish refactoring the tests. Right now one of them still fails, but I need some sleep. I'm pushing the WIP tests to the MR.
Comment #196
andypost@JohnAlbin great research! Just not clear how to document expected behavior to make everyone understand and prevent misuse
PS: patches are hidden to point that WIP in MR now https://git.drupalcode.org/project/drupal/-/merge_requests/49#note_5162
Comment #197
JohnAlbinI found a minor bug in my code when ordering a base hook suggestion relative to those from hook_theme_suggestions_HOOK(). Again, I found this due to tests I'm writing. (yay!)
Comment #198
joseph.olstadGreat find @JohnAlbin, thanks again for your hard work, I'm really looking forward to this one, very important improvement IMHO.
Comment #199
JohnAlbinOkay, I've refactored and expanded the theme suggestions tests. They now mostly use the data provider interface of PHPUnit to make them easier to understand. I got rid of all the controller paths as they made the tests really hard to understand; and it's much simpler to take a render array and render it to markup directly rather than pull an entire Drupal page.
As for the actual changes to
Drupal\Core\Theme\ThemeManager::render()
, the MR changes no existing lines of code; it just adds 60 lines of PHP to calculate the list of templates to print out in Twig debug comments. It also adds about 60 lines of code comments. And I went ahead and fixed all the phpcs code formatting warnings in that file.The most controversial part of the MR changes is that I was forced to call theme suggestions alter hooks a second time so that I can accurately figure out how to split the
$suggestions
list created by hook_theme_suggestions_XXX functions into 2 different lists. Why? Here's how I describe it in the code comment:The reason why I ordered the list of suggestions like above is due to this: the call to hook_theme_suggestions_HOOK() returns $suggestions and Drupal appends the matched $hook (from #theme), but only if $hook is a hook suggestion and not a simple base hook. This affectively splits the $suggestions into 2 parts, any suggestions before $hook will NEVER be used because $hook already matches an implementation and only those suggestions after it can override it.
The code implementation of how to split the $suggestions into two parts is complex because Drupal only conditionally appends the matched $hook. If the matched $hook was a simple base hook, we still need to track where it would have been in $suggestions because a themer may later to decide to implement one of the more-specific suggestions from #theme. I tried figuring out how to do that without actually inserting a separator into $suggestions (to mock how an appended $hook would work), but it's literally impossible to do:
If hook_theme_suggestions_HOOK() creates this list: A - B - C
We need to track the end of that list: A - B - C - d (I'm using "d" to represent where $hook would have been added if needed)
A hook_theme_suggestions_HOOK_alter hook might return: A - B - Z - C
But where is "d" in that list? Impossible to tell. Here's 2 possible answers:
Z is inserted into the middle of the list: A - B - Z - C - d
Z is appended and then C is moved from the middle to the end: A - B - d - Z - C
As you can see both of the above lists are identical EXCEPT where d is located. Both of those answers represent completely valid steps that an alter hook may have done and the only way to tell the difference between the two is to insert an actual item on to the end of $suggestions before the alter hooks are run.
To be clear, the best solution is to insert all the #theme suggestions on to the end of hook_theme_suggestions_HOOK()'s list before letting alter hooks modify that list. IMO, it's a bug that theme suggestions alter hooks aren't actually given all the possible suggestions that Drupal knows about. However, that's a BIG change and might break have unintended consequences for contrib modules. I'd argue it can only be done for Drupal 10. I'll search the issue queue to see if this bug report already exists and make a new issue if it does not.
Which means, the safe (but verbose way) to fix this bug now is to insert a fake separator into a fake list and re-run the alter hooks. This fake list is only used by the Twig debug comments, so it doesn't affect how the rest of
Drupal\Core\Theme\ThemeManager::render()
works. And it is totally safe to run alter hooks as many times as we want (on different data each time), so we won't cause any problems for contrib modules. Is it possible that some weird contrib module will mess up the data when it sees this new fake separator? I'll be honest; the answer is: Yes, never underestimate the insane things contrib modules will do. :-D However, it would be very rare and it would only mess up the fake list of suggestions used by the Twig comments and have no side effects anywhere else in Drupal's code.Reviews, please! :-)
Comment #200
JohnAlbinI've opened a bug report for this at #3187010: Not all suggestions are passed to theme suggestions alter hooks.
While working on a MR for that issue I found another edge case where this issue's MR would fail. I've just pushed an updated test that should fail. And I'll add the fix after the testbot runs.
Comment #201
JohnAlbinI've added comments directly to the MR to point out the controversial part of this code change (discussed in detail in #199).
This MR is 100% safe and 100% ugly. It adds ~48 lines of code to fix this bug. If we were to fix #3187010: Not all suggestions are passed to theme suggestions alter hooks first in 9.2.x, then we would only need ~2 lines of code to fix this bug.
I really, really, really want to see this bug fixed in Drupal 9. I believe #3187010: Not all suggestions are passed to theme suggestions alter hooks is the better fix, but I would love to get some people's opinions on whether we can make that change to 9.2.x or if we'd need to wait until 10.0.x.
Comment #203
star-szr@JohnAlbin I don't have much to contribute at this time but I just want to sincerely thank you for working on this issue again! If I get time I will try to contribute a review (here or on #3187010: Not all suggestions are passed to theme suggestions alter hooks).
Comment #204
JohnAlbinSo to reiterate:
core/themes/engines/twig/twig.engine
'stwig_render_template()
(where Drupal currently calculates the list of suggestions) because some of the valid theme suggestions are never passed totwig_render_template()
and are only known inside\Drupal\Core\Theme\ThemeManager::render()
.\Drupal\Core\Theme\ThemeManager::render()
without altering any of the existing logic used; this would 100% guarantee that no other parts of core or contrib would be affected by the bugfix. HOWEVER, this bug fix requires adding 123 lines of code and explanatory comments, including making duplicate calls to the theme_suggestions alter hooks. And looks like this https://git.drupalcode.org/project/drupal/-/merge_requests/49/diffs?diff...Since no one else has reviewed the code in option #2, I'm going to, even though I wrote it.
My conclusion: the bugfix using option #2 is way too ugly and hacky to add to core. There is no cleaner way to fix the bug "without altering any of the existing logic used" in
\Drupal\Core\Theme\ThemeManager::render()
. Which means, if we want clean, acceptable code (and core always wants this), the bugfix will have to alter the existing lines of code and be really damn careful that we don't alter the basic logic of how we load templates.I think option #3 is our best solution. I'll update the MR momentarily.
Comment #205
JohnAlbinComment #206
JohnAlbinI've updated the issue summary to show the rationale for rejecting the previous proposed solution.
FYI, I'm using this patch on production using the latest 9.2.8 official release (minus the changes to the tests since they don't apply cleanly.)
Comment #207
JohnAlbinComment #209
pivica CreditAttribution: pivica at MD Systems GmbH commentedJust to note that we have a similar order problem in ThemeManager in #3073053: Theme library override can fail in when you have multiple parent themes with wrong order of libraries-override and alter hooks calls.
Didn't check what this patch is exactly fixing but this patch and patch from 3073053 are fixing different parts of code and order problems. Do we have some overlaps here in fix approach?
Comment #211
mherchelCross referencing #3301373: Create twig |add_suggestion filter, which is affected by this issue.
Comment #212
JohnAlbinI'll try to get this rebased on 10.1.x during Drupalcon Prague this week.
Comment #214
JohnAlbinLet's see what the testbot says…
Comment #215
joseph.olstad@JohnAlbin
great work, thanks! Looking forward to getting this improvement, hope it makes it into 9.5.x / 9.4.x also
Comment #216
jwilson3Just a quick side note here that the current state where the D9 MR !49 was left is not generating the template suggestions in the correct order.
This is breaking contrib module responsive_preview, which depends on the custom-defined suggestion and template provided by that module for proper theming display.
Comment #217
greggmarshallIt would appear this patch/diff and the latest one from #2752443: Incorrect order and duplicate theme hook suggestions conflict with each other.
Comment #218
nod_The 10.1.x MR doesn't apply anymore unfortunately
Comment #219
Bhanu951 CreditAttribution: Bhanu951 as a volunteer commentedComment #220
Bhanu951 CreditAttribution: Bhanu951 as a volunteer commentedRe Rolled to 10.1.x branch latest commits.
Comment #221
nod_Thanks, a little lint error left :)
Removing old tag
Comment #225
JohnAlbinIt looks like all the new code handling for #3343198: Improve documentation of hook_theme_suggestions_HOOK_alter() has been reverted during a rebase and force pushed to the "-d10" GitLab branch.
I'll work on properly porting this to the main (11.x) branch.
Comment #230
JohnAlbinStill working on this. The solutions in #3343198: Improve documentation of hook_theme_suggestions_HOOK_alter() and #2953921: Refactor out theme hook suggestion building from ThemeManager::render() into a separate function. need to be refactored so they can work with this issue.
Comment #234
Giuseppe87 CreditAttribution: Giuseppe87 commented@JohnAlbin about #225: I tried to rebase the MR according the documentation, I'm sorry if I messed something up.
I needed to reroll the 10.x patch again, this time against 10.2.x, as it doesn't work anymore for that version.
In order to avoid to mess again, I'm attaching as .patch file, if it is good, should I rebase\or push on the MR request?
Comment #237
vasikeSome updates on MR to make it "green"
- "Refactor" for related refactor https://www.drupal.org/project/drupal/issues/2953921 (i think also tried on #234 patch)
- Updates for the related refactor "regression" https://www.drupal.org/project/drupal/issues/3409982
- Some updates for "invalid suggestions"
- Updates (forced sometimes) for tests to pass
In conclusion ...the MR got dirty (in its "moments") ... commits and merges
And, i think, it needs more than review ... maybe a "resume" ... and make it happen!!!
For now "Needs review" ... but it's "Needs work" (imho) ,,, still
Comment #238
joseph.olstadThis is a 10 year old issue that will improve DX / TX by a large margin. I'm favoring a community RTBC on this as we'll need theming system manager review on this and from there lets try to get this to the finish line and into the end zone ( a release ) while we have some momentum here.
Comment #241
alexpottNeeds to merge 11.x and fix conflicts due to #3420709: Make it more obvious that a Twig template is overridden
Comment #243
vasikeMR green again
Most of errors were from https://www.drupal.org/project/drupal/issues/3420709 update .. mentioned in #241.
So updated the MR - put back things and fix tests
There also others changes missed in latest merges and rebases. I hope I covered them all.
Let's review again
Comment #244
joseph.olstadComment #245
needs-review-queue-bot CreditAttribution: needs-review-queue-bot as a volunteer commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #246
vasikeMR updated - solved conflicts
so RTBC ... once more
Comment #247
alexpottI think we need a separate CR detailing the new variables that are going to be available ie.
And specifically how they related to any existing variables, ie. theme_hook_original.
Also this is adding an
info
variable everywhere - this feels very generic and likely to be used elsewhere - is this necessary?