Problem/Motivation
template_preprocess_menu_tree irrevocably alters $variables['tree'] losing Information and preventing it from being shared between levels of a hierarchy. Further, template_preprocess_menu_tree gets called before YOURTHEMENAME_preprocess_menu_tree.
Proposed resolution
RTBC'd patch in #33 alters template_preprocess_menu_tree to make the tree data available as $variables['#tree'].
Approaches to resolving this have been:
1. saving a copy of the entire $variables['tree']. This results in excessive overhead (debunked in #15 and #35)
2. changing YOURTHEMENAME_theme_registry_alter to not call template_preprocess_menu_tree and then substituting that functionality into YOURTHEMENAME_preprocess_menu_tree. Once again, there are lots of arrays getting copied about.
Remaining tasks
None as of #38, other than some polishing of the change notice at #2827134: Drupal 7 adds menu tree render structure to (pre-)process hooks for theme_menu_tree().
User interface changes
None.
API changes
The Render API structure for menu tree data is now available in THEME_[pre]process_menu_tree / THEME_menu_tree as $variables['#tree']. The existing behavior of template_preprocess_menu_tree modifying $variables['tree'] is not changed.
| Comment | File | Size | Author |
|---|---|---|---|
| #33 | theme_menu_tree-767404-33.patch | 437 bytes | nikhilsukul |
| #29 | theme_menu_tree-767404-29.patch | 516 bytes | nitesh sethia |
| #27 | menu.inc_.160423.patch | 519 bytes | geru |
| #21 | 767404_21.patch | 492 bytes | Ketan Harit |
| #15 | menu_cold_cache.png | 26.06 KB | das-peter |
Comments
Comment #1
sunWe have http://api.drupal.org/api/function/template_preprocess_menu_tree/7 and http://api.drupal.org/api/function/theme_menu_tree/7 now, so this should be relatively easy to do.
Basically means that $variables['tree'] either needs a parent element (why are we removing it in the first place and only pass #children?) to assign a proper #type with #attributes, or we just want a #prefix and #suffix.
Can you roll a patch?
Comment #2
sunComment #3
Jeff Burnz commentedThis would be good (verbose subscribe...).
Comment #4
miroslav t commentedHello, I am new to Drupal and I have been studying it online for about 2 months. I have to migrate a site to Drupal, but I have to keep its design which is based on tables. This means that a lot of theming is needed and I already checked some of the core code to get a better idea what is actually hapenning down there... It's nice code, well done to the contributors!
So I tried to use menu_tree.tpl.php and THEME_preprocess_menu_tree(&$vars, $hook) in my theme and I came across the function menu.inc:template_preprocess_menu_tree(). Basically what it does is to hide a lot of information from the next preprocessor functions ($variables['tree'] = $variables['tree']['#children']; ???). I commented this row out and I could use $vars['tree']['#block'] in my preprocess function as desired.
Is this hack dangerous? :)
Comment #5
mcjim commentedUnless I'm missing something, I think we need to go back to menu_tree_output() to find something that we can use as a class (the menu_name).
Might be sensible to add an array of classes that can be imploded in template_preprocess_menu_tree().
Thoughts?
Comment #6
BenVercammen commentedI'm currently in the same situation:
- I want to add classes to the menu HTML output depending on the region they're in
- so I've implemented "THEME_preprocess_menu_tree()" but noticed that I don't get any info at all
- apparently "template_preprocess_menu_tree()" is called before, and indeed removes a lot of useful information
- so now I have no way of checking in which region the current menu_tree resides and it seems I'm either forced to hack the core or work around this through HTML and CSS changes...
Hence my question; why is "template_preprocess_menu_tree()" called before "THEME_preprocess_menu_tree()"? Shouldn't the THEME_preprocess_menu_tree() function replace / override the template_preprocess_menu_tree() one?
Edit: I decided to modify the core function as such:
Comment #7
Nexotap commentedI dont know exactly if this now belongs to 7.x or 8.x and maybe I'm already way too late.
Anyways, I had the same problem and I think, I've found a solution for that without overriding core function.
Since I'm working on 7.x, I can only tell you how it's working there. It may work on 8.x as well.
The whole magic lies in the hook_theme_registry_alter() function.
Within this function we search for that 'ugly' preprocess function which is defined in menu.inc and remove it from the list.
That made, my own preprocess function works without interruption and I can do whatever i want.
In the following case it just takes the delta value and puts it in $variables which you afterwards can use in hook_menu_tree().
Cheers
~Nexo
Comment #8
BenVercammen commentedThanks Nexotap! I've stumbled upon this issue again after updating core...
Your solution seems to work like a charm!
Comment #9
anandkp commentedThanks!
Almost a year later and Drupal version 7.15... Problem just throttled a fair hour or two out of me... LoL
Writing in to thank Nexotap for the solution posted. Worked like a charm indeed!
Hope this gets fixed in a future version of D7... Does anyone know if it will?
EDIT:
Just realised that my minor adjustment to Nexotap's code might be handy for someone else. Also want to mention that hook_theme_registry_alter(&$theme_registry) can be used in a Theme's template.php file as well - a custom module is not needed for it.
The following is my version... I'm just pulling out the menu name and adding it as a class. My use case was that I'm using Panels Everywhere and have my Menus in several Panes... and wanted an simpler way to differentiate between them other than the classic ul class="menu". I should mention that I've stripped out all the excess div elements that Panels inputs by default.
Hope this helps someone :o)
Comment #10
das-peter commentedI think the current version is plain wrong:
This plain removes the ability to (re-)use existing information in specialized processors or in the theme hook, which is absolutely silly.
For now I totally agree with the approach of BenVercammen in #6
The attached patches integrate the approach but with a slightly other naming.
This is not likely the final solution to make
theme_menu_tree()more themeable, but at least it's not impossible to reuse available meta-data for the theming.Comment #11
sunHold on, I think we want to postpone this on #891112: Replace theme_item_list()'s 'data' items with render elements and afterwards, revamp the entire menu tree rendering into a dead-simple item list.
Comment #12
das-peter commented@sun D7 too?! Because I just added the D8 patch to comply with the current set version here.
I'm more interested in fixing this in D7.
Comment #13
sunoh, hm. I guess we can do this as a bug fix to backport to D7, and handle the total menu tree theming revamp in a separate issue (which will potentially remove the additional data being added here for D8 then).
So for this patch, the only thing I'm concerned about is performance (specifically memory consumption) due to the additional copy of the entire menu tree array in the new #tree theme variable. I think we should bench/profile this.
Comment #14
das-peter commentedI agree that this will add some overhead. Unfortunately I can't think of another solution (at least none which doesn't include picking specific information).
The total memory consumption should increase since the full
$variables['tree']is kept longer and$variables['tree']['#children']is copied.If there are "parallel" uses of
template_preprocess_menu_tree()it's likely the total memory consumption increases quite a bit.So, what would be an acceptable increase and how shall I test it?
Is a simple D7/D8 fresh install with around 50 menu items enough?
Multiple menus?
How many hierarchy levels?
Comment #15
das-peter commentedTest:
50 menu links in the main-menu.
All menus on first level, this means all 50 links are displayed on one page.
2 menu blocks - makes 100 menu links on the same page.
Used profiler: xhprof
Results:
The CPU values differ heavily between each run, so no qualified result here.
Memory consumption after the change:
Following functions had 0.0% changes too:
bartik_menu_treemenu_treemenu_tree_outputComment #16
anrikun commented@Nexotap (#7): many thanks for this!
A small code improvement:
Instead of
Use:
Comment #17
Syd Barrett commentedThanks Nexotap and Anrikun,
how can I check the region in this case?
Comment #18
Rafayel commented#10: drupal-template_preprocess_menu_tree.d7.patch queued for re-testing.
Comment #19
Angry Dan commentedApologies, I haven't had time to read this entire thread but the simplest answer here has to be to rename template_preprocess_menu_tree to template_process_menu_tree doesn't it?
Then other preprocess functions will get a full render array for the tree?
Comment #20
jhedstromComment #21
Ketan Harit commentedComment #22
Ketan Harit commentedComment #23
alimac commentedRemoving location tag (and adding SprintWeekend2015) -- there was a discussion (see 2407325#comment-9513533 and decided to use one tag to keep the autocomplete list of tags short.
Comment #24
Jeff Burnz commentedI dont understand what this is supposed to be doing in Drupal 8 since
theme_menu_tree()doesn't exist in D8 anymore.I think this needs to go back to D7 because I can't see that its relevant anymore.
Comment #25
mgiffordNo longer in D8, so bringing back to D7:
https://api.drupal.org/api/drupal/8/search/theme_menu_tree
Comment #26
mgiffordComment #27
geru commentedI ran into this problem theming a hierarchical menu tree. If all that needs to be passed downward is the #attributes, then simply checking if #attributes have been set and then copying that is a relatively efficient way to resolve this problem.
I am submitting a patch to do just this, and can't see any drawbacks.
Comment #28
geru commentedI'm new to this patching thing. What does it take to get a little change like this reviewed?
Comment #29
nitesh sethia commentedFixed few of the indentation issues in the patch.
Comment #30
geru commentedBump. What does it take to get this proposed change reviewed / tested and incorporated into Drupal?
I have successfully gotten an appropriate modification merged into the main branch of Zurb Foundation (added a-tag class qualifier ":not(.follow)" to allow accordion a-tags to reference...)
That modification, coupled with this and some theme code allow accordions to be opened and closed and attribute information to be passed in the hierarchy of a menu tree without wholesale copying of entire menu tree branches.
I see that Nitesh Sethia has a link for "Add test". Are these tests something I can accomplish? If so, would someone please guide me in how to accomplish them?
Comment #31
fabianx commentedRTBC - I am generally fine with this change.
I am not sure we absolutely need tests for this functionality.
It would be better though.
--
Caveat: It might be that this still needs to be fixed in D8 first (even though the actual function does no longer exist.)
Comment #32
fabianx commentedActually I checked the whole issue again and what we really want is:
to make it available to other preprocessors down the road.
The memory usage is no concern as it is temporary (and the parent still has the data structure in memory anyway and arrays are shared in memory, not copied).
Can someone please roll a new patch based on #10 and update the issue summary?
Thanks!
Comment #33
nikhilsukul commentedHi @Fabianx,
Please find attached the patch based on #10.
Comment #34
chaseonthewebComment #35
geru commentedIf I understand #15 correctly, this approach causes a performance hit, because it is a shotgun blast solution to the problem. I hope the performance of this patch will be tested.
I think that if attributes are what need to be passed, then let's give the opportunity to pass attributes alone.
Comment #36
fabianx commented#35: There is no performance hit in memory for passing around a variable that is in memory anyway still (the caller still has the whole array in memory).
The whole tree is usually needed, attributes are just a special case. This is an unfortunate side effect of theme wrappers and how they work.
--
This still needs a change record and and IS update: Could someone try to write those please?
I am very happy to review the changes / change record.
Comment #37
fabianx commentedBump:
- Could a novice write a change record and update the issue summary, please?
Comment #38
chaseonthewebCreated a draft change notice https://www.drupal.org/node/2827134 and updated issue summary. Feedback on change notice welcome (could it use example code?).
Comment #39
fabianx commentedSplendid! Both work for me.
Some example code in the change notice would be great - yes!
Marking for commit.
Comment #41
stefan.r commentedChange notice looks good.
Committed and pushed to 7.x, thanks!
Comment #42
David_Rothstein commentedI went ahead and published the change record.