Three suggested steps to clean up (and speed up) _menu_navigation_links_rebuild():
- Support a parent_path setting in hook_menu(), to determine the parent.
- Do NOT allow changing of plid, menu_name (and thus, depth) in hook_menu_link_alter(). At the time this hook fires, we should already know the position in the tree. The ticket is bought, there is no trip cancellation insurance. See below why.
- Rewrite _menu_navigation_links_rebuild() accordingly.
Benefits:
- faster rebuild.
- more robust and predictable rebuild.
How much of that is going to survive in Symfony land, or backported to D7?
I don't know. I want to get this in before hook_menu() officially dies.
I am working on a patch.
But it would be great to have some feedback on the suggested API change.
From #1830234: commerce_kickstart_menus_menu_link_alter() should not change the plid of customized items. (adapted):
Longer explanation:
I came through this while trying to rewrite _menu_navigation_links_rebuild() for performance sake.
#1010480-64: Optimize _menu_navigation_links_rebuild()During this work I bumped into hook_menu_link_alter().
This is a beast of a hook. I will explain why.Where is hook_menu_link_alter() invoked?
http://drupalcontrib.org/api/drupal/drupal%21modules%21system%21system.a...
The hook is fired during menu_link_save(). This is called in multiple occasions:
- During _menu_navigation_links_rebuild(), for "non-customized" (*) links that are being reset to the newest state from menu_router.
- During _menu_navigation_links_rebuild(), for "customized" links that are moved around because the parent has changed or disappeared.
- On form submission, for then "customized" links, when the user wants to update this link.
- On form submission, for any links, when the user has modified a parent, and the children need to be moved around.
Maybe I missed some.
(*): A "customized" link is something a user has edited. During _menu_navigation_links_rebuild(), non-customized links are updated with data from menu_router: They get an updated title, etc. Customized links are not updated from menu_router, except for the router path.
What happens in hook_menu_link_alter() implementations?
I would classify the implementations of hook_menu_link_alter() into these two classes:
- Implementations that look at plid, depth and menu_name, and adjust other values.
- Implementations that change the plid and menu_name (and thus the depth):
commerce_kickstart_menus
page_manager (that's disabled and tagged as "concept code")
user (setting the plid to 0 - only for non-customized items).Having both of these in one hook makes for a perfect chicken-and-egg problem.
(This alone could be solved by proper sorting of those hook implementations.)Besides, this makes it really hard to work on _menu_navigation_links_rebuild():
- It is hard to predict where a menu item will end up in the tree, if in the last moment some module can send it somewhere else.
- We don't want modules to do select queries on a half-broken or out-of-date menu_links table.If I were to decide that for core:
- hook_menu_link_alter() should be forbidden to set plid.
- Instead, core _menu_navigation_links_rebuild() should respect a 'parent_path' setting in menu_router. This can be set to FALSE, to set the plid to 0.
Comments
Comment #0.0
donquixote commentedUpdated issue summary.
Comment #0.1
donquixote commentedUpdated issue summary.
Comment #1
donquixote commentedThere are two major API changes, and the title should reflect that. Even at the cost of a longer title..
Comment #2
donquixote commentedIssue title:
It is about the plid, not the mlid.
--------------
"Preview" patch:
Every time I git-pull the latest version of D8, nothing works anymore.
I promise this patch did "work" (*) before I pulled.
(*) What did work?
- The same links ended up in the menu_links table as with the original _menu_navigation_links_rebuild().
- The rewrite did bring a speed improvement between 2x and 3x.
- I saw no immediate wsod or nasty errors.
What did I not test?
- I did not run any unit or web tests locally.
- I did not edit any menu items (I was going to do it, but now my D8 is broken once again).
- I did not enable and disable any modules, or changed any hook_menu() definitions.
Known issues:
Yes, this patch is full of debug statements. And the code is quite rough.
It's a preview, nothing more.
Comment #3
donquixote commentedI would be surprised if testbot likes this..
Comment #4
donquixote commentedWrong direction.
Comment #6
donquixote commentedThis might not be enough, we might also need to change the way that menu_link_save() works.
Comment #8
donquixote commentedI realize I am too fast here. Should do it piecemeal instead.
1) Just support the additional setting in hook_menu(). Don't do anything else.
I hope this is possible via _menu_link_find_parent().
2) Some effort to ignore or punish the parent change in hook_menu_link_alter()
3) The rewrite can happen in a follow-up. The issue title is long enough already!
Comment #9
donquixote commentedComment #10
donquixote commentedOops
Comment #12
donquixote commentedSome ridiculous bugs.
Also added a test. This could be a bit naive, improvements are welcome.
Comment #13
donquixote commentedAwesome!
Things to consider:
For D8 we might totally leave hook_menu() and move to something else. Although, noone really knows yet (afaik) how modules should register menu links, if not with hook_menu().
For D7, we need to be careful with other modules that might already use the 'parent_path' key for something else.
This would require some further research.
Comment #14
donquixote commentedLet's add some explosion and flames for modules that don't follow the new rules.
(that's step (2) of #8)
Note: If this fails the test, then #12 is still valid!
Okok, I re-add #12, to keep the light green.
Comment #15
donquixote commentedAfter step (1) and (2) of #8, now here is step (3) of #8:
The rewrite of _menu_navigation_links_rebuild().
EDIT:
This will fail. Quite likely.
Comment #17
donquixote commentedThis one is quite promising!
Note: The patches in #14 is still valid.
This one is just another attempt for the full rewrite as in #15.
Comment #19
donquixote commentedThe failing user account test has a hardcoded mlid=2 for the 'My account' link, this is why it fails.
I changed that test locally to use a dynamically determined mlid, and this change makes it succeed.
The failing shortcut test shows that I totally ignore the $link['module'] setting on menu links, and instead only look at the "customized" setting. That's a big blunder, but probably easy to fix now that I know it.
Comment #20
donquixote commentedIntroducing a touchable/untouchable metaphor for "customized" vs "module".
An item is touchable, if it is uncustomized and owned by 'system' module.
Comment #21
donquixote commentedAnd as always, I forget the "needs review" setting.
Comment #23
fabianx commentedAdding Performance tag
Comment #23.0
fabianx commentedUpdated issue summary.
Comment #24
berdirMenu link handling in 8.x is so different that I doubt anything here is still relevant, parents are explicitly defined. and the rebuild function is gone, although it remains to be seen how performant the new system will be. But that will need new issues...