Follow up to #2047633: Move definition of menu links to hook_menu_link_defaults(), decouple key name from path, and make 'parent' explicit. That issue introduced a hook_menu_link_defaults() as the D8 replacement for the menu link portion of D7's hook_menu(). However, several alternate ideas were presented on that issue and punted to here:
- Use YAML + optional YAML-specified callable + ALTER event, to match how routes are declared/altered.
- Leave the array based hook, but rename it to hook_menu_link_info().
- Use the Entity API exclusively: i.e., create the menu link entities directly in hook_install(), and update them in hook_update_N().
- Since the vast majority of the time, there's only 0 or 1 menu links per route, add menu link definitions to routing.yml, and then introduce some dedicated hook or event to handle the extreme edge cases.
- See #47 redefine menu links as plugins with YAML discover plus custom menu links with associated content entity in analogy to block + custom_block
Remaining tasks: decide on one of the above and implement it, or decide to leave hook_menu_link_defaults() as-is.
Comment | File | Size | Author |
---|---|---|---|
#14 | menu_link-direct-2178723-14.patch | 3.37 KB | effulgentsia |
Comments
Comment #1
catchThoughts on all these;
#1. While it 'matches' routes, routes/local tasks etc. aren't entities, and we don't have a UI for creating/updating them. Sometimes it's better to be inconsistent, than misleadingly consistent.
#2 would be leaving the status quo of the current patch that's RTBC. I don't think anyone thinks that's great, but it's a relatively simple port from 7.x if we decide none of the other options are better.
#3 I really like. chx suggested that in 2010 and it seemed a good idea then too "Menu links should be simply saved from install files and not generated from router": #653784: Separate out menu links from hook_menu. We'll lose some features (like magic updating of menu items, this is only really useful for the admin menu structure, don't think I'd miss it for anything else), but those are features that are quite minor compared to the effort maintaining them.
#4 please no.
Comment #2
dawehnerIf we do this, we would have to figure out how to properly "update" menu links when a view config is synched. Something like storing the used UUID somewhere could be one approach.
Comment #3
pwolanin CreditAttribution: pwolanin commentedSo one of the questions is if you update do you have to check if the link is customized? How would all this work with translations?
Also, as much as I think the port in #2047633 is the correct next step and needs to go in ASAP, It would be nice to find a way to improve the DX for the very common use case of creating a module admin page that should show up in the various admin listings.
Comment #4
catchSeems like that'd be optional, and depend on what the change is.
With translations, seems like if fields aren't translatable there's no problem. If they are it'd need to update the English version, then the others would be marked as outdated?
Comment #5
sunPlease note that 1-3 are attacking technical issues only, and out of those, (3) would certainly be most logical (although the still required auto-re-parenting logic might block it).
(4), however, addresses the DX/UX of defining menu links in code for the primary use-case of associating exactly 1 link with a route, which requires you to figure out 3 independent IDs:
Because I believe it would be a big win for DX, I'd like to better understand the reservations of others against this.
From my perspective, I'd understand the concerns, if we hadn't cleanly separated the internal subsystems yet. But given that this is the case already, it all boils down to finding the best DX for developers only.
Perhaps as a concrete example:
In words:
The data is still cleanly separated. Their definition is merely moved to the most sensible place to achieve the best DX for module authors.
To produce the above example, I had to open exactly two files: node.routing.yml and system.routing.yml. In HEAD, that's at least four files (+ node.module + system.module).
Comment #6
Damien Tournoud CreditAttribution: Damien Tournoud commentedI agree with @sun here. Automatic definition of menu links from the router just makes sense. That's precisely why we have had that all those years.
(But of course, it's way better to use schema-less YAML files to define them then to use an anonymous arrays.)
The DX of #3 would be horrible for simple tasks, but another problem with it is that it solidly anchors menu links in the realm of content, instead of the one of configuration. In my experience, menu links tend to fall more frequently in "configuration" rather then "data", so this feels very backward.
Comment #7
Gábor HojtsySo #2047633: Move definition of menu links to hook_menu_link_defaults(), decouple key name from path, and make 'parent' explicit wanted to remove the runtime t()-ing of strings for items provided by modules, but it does not do that in its current (still uncommitted) form. If/once that is removed, the translations open up to be solved for the built-in items as well. That requires having menu titles translatable (or incurring *serious* regressions). I'm not fully up to date on the effects of #2047633: Move definition of menu links to hook_menu_link_defaults(), decouple key name from path, and make 'parent' explicit for translatability. The format to be used to set default menu items needs to be defined in a way to support translatability-integration as well especially when item updates are concerned.
Comment #8
effulgentsia CreditAttribution: effulgentsia commentedI have mixed feelings on this option (#4 from the issue summary). I like the DX of it with respect to developers who only develop for Drupal, and not also for other Symfony or MVC systems. However, for people who cross operate with those systems, my reservation is that it's Drupal hijacking a file type that isn't ours. Symfony defines the concept of a *.routing.yml file and the top-level properties of a route (path, defaults, requirements). Us bolting on a "link" property (in the YAML) in order to add information that has absolutely nothing to do with routing (routing is what you do when a request to a URL hits the server, not how you display links to that URL from other pages) just feels a bit wrong.
With or without #3, I think for better or worse, menu links are already solidly anchored to content rather than config. That will especially become true once #1842858: [PP-1] Convert menu links to the new Entity Field API is done, and I don't see a viable suggestion to not do that issue, or to change course and convert menu links to config entities.
As to the DX of #3, are we sure it'll be horrible for simple tasks? Would it make sense to prototype / mock up some examples to evaluate?
Comment #9
catchFor me the 'simple tasks' is configuration pages under /admin, because we still have this hard-coded administration interface in system module, which in turn is based entirely on menu links.
ConfigFormBase and entity administration links gets us closer to a situation where administrative pages could be derived from information other than which routes/links happen to live under admin/* and its top-level directories, but we're obviously not there yet.
Comment #10
pwolanin CreditAttribution: pwolanin commentedWell, if we are committed to using routes in Drupal 8, the PATH pattern admin/* shouldn't be used to determine anything, and in general paths are not canonical. So you always need both the route for the link and the machine name for the parent link.
re: #8 we do also need to start thinking of menu links as content - maybe more like book module than whatever we thought of them before.
So, possibly we should not be using this system to define the administrative interface at all?
If we want to "automate" it for the simplest cases, maybe the .info.yml file? Modules are already putting some links there using configure, e.g.:
Comment #11
moshe weitzman CreditAttribution: moshe weitzman commentedI too am in favor of #3, for the reasons that catch alluded to in #1 - "We'll lose some features (like magic updating of menu items, this is only really useful for the admin menu structure, don't think I'd miss it for anything else), but those are features that are quite minor compared to the effort maintaining them.".
Menu link rebuilding is so painful, we had to invent a locking system to pause all requests until rebuild is done (more or less). I love #3 because it bypasses all that complexity and poor performance.
Comment #12
jhodgdonCan we see an example of what proposal #3 would actually look like? How much trouble is this going to be for developers of contrib modules that want to have something listed on admin/config or admin/structure?
Comment #13
xjmYep, @effulgentsia said today that he will post a mockup of what proposal #3 would look like for a module implementing it.
The issue @DamZ raises with menu links being content (versus configuration) is a relevant concern, but I think it's out of scope for this issue. It also affects other content entities that might also be more like configuration in some circumstances; for example, taxonomy terms. So far we've chosen to make both entity types content, and the scope of CMI has been that we will leave it to contrib to solve the issue of content deployment or managing situations where something we have classified as content should be treated like configuration.
Having default menu items as configuration entities instead of content would still require something like #3 anyway. The difference would be that one could also provide the menu entities in the default configuration folder to be installed automatically, rather than a
hook_install()
. The problem of updating from modified default configuration on a module update is still unsolved for configuration as well. Conceptually, we would make a lot of the same changes as for #3 if they were configuration entities instead.So, I'm also in favor of the proposal in #3.
Comment #14
effulgentsia CreditAttribution: effulgentsia commentedHow's this?
Comment #15
sunThis discussion still focuses exclusively on (1) API usage and (2) maintenance, but (3) ignores DX entirely.
Perhaps my brain is simply not able to make the logical connection, but:
How can there be an agreement on #2178725: Make all core menu link machine names and the corresponding route names match to synchronize route names and menu link machine names, but at the same time, the exact proposal that would truly ensure synchronization without human interaction in #5 is dismissed?
That proposal exclusively talks about (3) DX. Because (2) the topic of whether to maintain that info is a decision internal to the system, and (1) API usage is [mostly] abstracted away.
Can you enlighten me, please?
My fundamental stance is still the same: Humans are not able to reliably synchronize any data set that is larger than 3 items. — Do NOT rely on humans for any task like that.
PS: It also helps to clarify that people are in support of option 3) in the issue summary (as opposed to comment #3) when referring to #3...
Comment #16
jhodgdonRE comment #14, the proposal for how modules would create menu links seems OK to me. As compared to hook_menu_link_defaults(), it is pretty similar and only a line or two more of code (because you have to put in the module and machine name into the function call, and call save() afterwards).
Some improvements:
- It would be really helpful if this function menu_link_create() -- which many many module developers are going to need to use, and they probably have zero use for really digging into the menu link entity otherwise -- had some documentation on which array elements in $values are actually required to create a menu link. And as you can see from the code, there are some special things like $values['description'] and $values['parent'], which are not part of the menu link entity at all. So, docs are necessary.
- If this menu_link_create() function is primarily for use in the hook_install() for modules to create their menu links, why not let it go ahead and call ->save() inside, rather than forcing developers to do that everywhere? Are there really use cases where someone would call this function and not immediately call ->save()?
RE comment #15... It seems like the main concern you are raising (if I am understanding correctly?) is that this proposal does not encourage/force people to use the same machine name for the link as the route, although we would prefer that people do that. So... Would it be possible for the new menu_link_create() function to do something like this:
It really seems like most modules that create menu links are just trying to get something displayed on the Configuration or Structure page, and they don't care about what the machine name is for the link. At all. Why not make it easy on them and assign it automatically? There are already helpers in there for description and parent... why not one more?
Also, regarding the patch in #14 and concerns in #15, it looks like this proposed specific example of a menu link for Actions does not follow the convention of "route machine name and link machine name match"... ???
Comment #17
pwolanin CreditAttribution: pwolanin commentedSince multiple links (in the same or different menus) can point to the same route (with the same or different route parameters) forcing the machine name to be the same as the route name is fundamentally a flawed proposal.
I also fear that doing this at install time open a real can or worms around correctly building the correct hierarchy. We depend on finding all links as part of the current process, while install-time means (I think?) that you only get the data for the one module being installed at a time?
In terms of performance, I think we should figure out a way to run this hook only when a module is enabled or uninstalled? Modules that want to programmatically create/remove links (e.g. Views) should be using the entity API.
Comment #18
jhodgdonThat is a really good point about the route parameters, and I don't think anyone is advocating forcing all menu entries' machine names to match their route machine names. But I think most modules that would want to declare menu entries would be so they can get listed in the whatever-it-is-now, Tools menu? The one that generates the Structure and Configuration admin landing pages. And in most cases there, they probably should use the same machine name for their route and menu entry. So why not have the API function they have to use have this default?
Comment #19
effulgentsia CreditAttribution: effulgentsia commentedYes, see #2178725: Make all core menu link machine names and the corresponding route names match. There's not consensus on that issue yet, so more opinions could be helpful.
Comment #20
effulgentsia CreditAttribution: effulgentsia commentedI tried to express my reservations about comment #5 in comment #8; here's a summary/update:
Meanwhile, we don't need true, 100% guaranteed, synchronization. Across the entirety of link generation systems, we can't have that anyway: for example, our *.local_tasks.yml are both human fallible and require default subtabs to have a different ID than their parent tab even though both link to the same route. Even if we only consider menu link entities, if due to human fallibility, D8 core or a contrib module is released with one or two mismatches, it's not the end of the world. And once released, neither route IDs nor menu link IDs should be changed, so maintaining synchronization over time (within a major version) is a non-issue.
Per #8, I'm not completely against issue summary option #4 / comment #5. If we can't do issue summary option #3, then that might be the next best thing despite my concerns about hijacking the routing file. But if we can do #3, then I prefer we just directly do it, and not add a potentially confusing layer over it.
Comment #21
effulgentsia CreditAttribution: effulgentsia commentedIf a module's hook_install() calls menu_link_create() with a particular 'parent', that usually means the module has a dependency on the module that provides that parent. Which means its install hook ran first and menu_link_create() has the data it needs in the {menu_link} table (or alternate storage backend). If it's an optional dependency, then the hook_install() needs to do a module_exists() call and conditionally set 'parent' to something that exists, same as comment_menu_link_defaults() does currently in HEAD. So, where is the potential for there being a problem with #14?
Comment #22
jhodgdonSo... Thinking about the proposal (3) from the issue summary, as in the patch in comment #14, am once again confused about the DX of how this would work during the module development process.
So if I am developing a module, let's say I put some stuff in my routing.yml file to define where my settings page is, and I implement hook_install() to define my admin menu link to that page to be on Configure.
Then I (oops!) realize that I had a typo in the route machine name. So I edit my routing.yml file and the hook_install() and clear the cache. Would I need to uninstall/reinstall the module in order for the menu link to be recreated correctly? It seems like I would... but actually, I guess my bad link would still be there after uninstalling -- do modules also need to implement hook_uninstall() and remove the link, or would links to non-existent routes automatically go away? Or would I need to do a hook_update_N() during my development process (ugh)?
None of this seems too great for the DX of a module developer who's early in the development process and editing things right and left, if I'm understanding correctly. Is there one of the proposals that would work better for this? And does the current system of hook_menu_link_defaults() work better for DX?
Comment #23
effulgentsia CreditAttribution: effulgentsia commented#14 contains an action_uninstall(). We could potentially implement this in a single place on behalf of all modules as a hook_modules_uninstalled() implementation (e.g., menu_link_modules_uninstalled()). However, I don't know yet if we want to standardize/force a particular content removal decision on all modules like that. We do that for config, but we haven't really discussed yet whether to do that for content.
Both HEAD and all of the other proposals retain the concept of menu link rebuilding after cache clear, which makes things easier during early module development for cases where you're making frequent changes to your module-defined menu links. However, with all module-defined config, as well as with all other module-defined content, you need to uninstall/reinstall or implement hook_update_N(). So why should we continue to treat menu links as different than that? Perhaps it's because routes and local tasks *are* rebuilt every cache clear, but routes and local tasks are neither config nor content -- they're completely unchangeable via (core) UI.
Comment #24
pwolanin CreditAttribution: pwolanin commentedRight, so here's my proposal:
Comment #25
jhodgdonWow, that proposal in comment #24 sounds almost sane! :)
Comment #26
catch@effulgentsia
It'd be straightforward enough to remove links for which there's no corresponding route, that'd take care of admin routes easily enough.
Where it gets tricky is if a module adds a link for the route of another module. In that case I don't see any particular reason to remove (nor for config entities where there's no actual dependency). For those you can always just go in and delete. Annoying if you're trying out a module, less so if your site is actually using stuff it provides and you didn't know where it came from.
@pwolanin
Moving the hook to .install is interesting - worth adding that as a new option to the issue summary?
Comment #27
sunHow does the explicit install/update/uninstall approach work for menu links that depend on another module, which wasn't installed yet when my module was installed, but gets installed later?
Will I have to implement
hook_modules_installed()
to check every module installation for whether that module has been installed, in order to create my menu links...?Comment #28
jhodgdonRE comment #27 - how can you install a module before one that it depends on? That isn't supposed to happen?
Comment #29
effulgentsia CreditAttribution: effulgentsia commentedYou can't, but #27 is referring to more of a soft dependency. For example, module A does not strictly depend on module B, but if module B exists, then module A adds some additional menu links. With issue summary option #3, yes, that would require a hook_modules_installed(), which I don't think is so bad, as the case isn't that common. With comment #24, I guess that would be handled automatically if the installation/uninstallation of any module triggers a full link rebuild.
If we go with #24, then why keep it a hook? Seems like then we're back to also needing to consider issue summary options #1 and #4. From comment #1:
I don't see how the presence or lack of UI is all that relevant. For example, contrib can create a UI for editing route information, but that wouldn't invalidate the concept of a routing.yml file. What matters more is architecture, and so long as we have a rebuild step on cache clear, manual triggered, or whenever, then what a *.menu_link.yml (or other name TBD) would define isn't menu link entities themselves, but module-defined declarative information that the menu system then uses to create entities based on its logic.
Care to elaborate? I've shared my concerns in #20, but as I mentioned there, those concerns aren't necessarily hard blockers for me.
Comment #30
catchThis is a problem for soft dependencies in config entities as well:
#2082117: Install default config only when the owner and provider modules are both enabled
#1776830: [META-1] Installation and uninstallation of configuration provided by a module that belongs to another module's API
and etc.
On option #4 it's the mixing of information in the routing file, especially after so much effort has been expended to clearly separate all of this out.
Comment #31
catchThe config entity issue I was actually thinking of: #2090115: Don't install a module when its default configuration has unmet dependencies
Comment #32
pwolanin CreditAttribution: pwolanin commentedWe left this as a hook because we didn't have a better idea for default content.
I feel like it's fine to ship as-is and we are wasting effort on a non-problem, versus actually dealing with all the entity API conversions and other things that need to happen.
If it's contributing to excessively slow rebuilds, then my suggestion in #24 would help alleviate that.
Comment #33
jhodgdonOK, can we add this as proposal #5 in the issue summary, to take into account that Module A might want to define different menu links based on whether module B is enabled or not?
5. Leave it as a hook, but put it into the .install file. Call the hook on all newly and previously enabled modules when any module is either installed or uninstalled, to rebuild the menu links. Have a button someplace (e.g., menu ui module?) that does a manual rebuild, and ask drush to add a menu links rebuild command.
Comment #34
jhodgdonActually, maybe the best spot for the button would be next to the "clear cache" button on Performance, if "clear cache" would not also rebuild the menus?
Comment #35
pwolanin CreditAttribution: pwolanin commentedI would see this new button as 95%+ a developer tool - it's not really performance related.
Comment #36
jhodgdonClearing the cache is also 95% a developer tool. :) Agreed that rebuilding the menu is not performance-related at all; however, if you need to rebuild the menu, it's probably also true 95% of the time that you need to clear the cache (and at least sometimes, vice versa). For DX, I think it would be nice if they were available on the same page, for situations where you happen to be working in the UI (testing your new site feature etc.) rather than using Drush.
Or maybe the "rebuild the menu" button could just have another button next to it that is "rebuild the menu and clear the cache"?
Comment #37
tim.plunkettThat's why it's under admin/config/development...
Comment #38
effulgentsia CreditAttribution: effulgentsia commentedThis is the premise that I object to. Here's why. D8 core has no existing API or pattern for declarative module-provided content entities. For config, we have YAML files in a config directory, but for content, if your module wants to programmatically add nodes, taxonomy terms, etc., it needs to do it imperatively (in hook_install() or elsewhere) rather than declaratively. However, if we were to create a generalized declarative API for content entities, I do not think we would do it as a hook; I would imagine we would do it as YAML files in a "content" directory to mirror config. I'm not suggesting we add such an API to D8 core (it's probably too late for that), but if a contrib project pops up to do so (and I hope it does, cause that would be awesome for distribution builders), I would hope it would do it that way, and not as a hook. And, I think that having an example of a one-off hook_ENTITY_TYPE_defaults() in core sends the wrong message for how contrib should tackle that problem space.
It would be fine in the sense of working and people could get used to it without too much trouble, but I think it's indefensible as a correct API. In terms of applying D8 patterns consistently, I see two sensible approaches:
I can see the difficulty in us reaching consensus on one of the above two, but I think it would be a shame if our failure to do that results in us sticking with the hook, which, IMO, is the worst of all options.
That's a valid concern to raise. Certainly there are plenty of functional bugs and other DX issues to spend time on. @catch, @webchick, others: what do you think?
Comment #39
jhodgdonRE #38 - well there's the root of the problem: Someone apparently made a decision at some point that menu items are Content.
It seems to me that providing a link to a module's main config page, from either Structure or Config or whatever, is really a very basic thing that pretty much every module needs to do -- and "The location of the main config page" and "whether this page logically belongs in Structure or Config and in what category" also seem like very basic module information. So telling Drupal this information should be straightforward, and it isn't really very logically like "I want my module to create a content item and then make sure it goes away when my module is uninstalled". In other words, I don't think it's "Content". I'm not even sure it's "Configuration" really... is that really something you want people to change?
So... I realize it is most likely way too late to change the fact that (apparently?) menu items are Content and not Config, and it is also probably too late to change the fact that (apparently?) the Config and Structure pages are generated from Menu content items... but these decisions seem to be problematic for the very basic need of nearly all modules to have a straightforward way to tell Drupal where their config page belongs.
Comment #40
effulgentsia CreditAttribution: effulgentsia commentedYep. Damien pointed out the same in #6. Where I think that was decided was in #916388-48: Convert menu links into entities and the patch that followed. Though I didn't read all the earlier comments to see what factors led to that decision, my incomplete understanding is something like this:
If we really believe that menu links being content entities is an unfortunate WTF that we have to live with, but would prefer to not have to force the consequences of that onto every module developer, then I think we're left with either issue summary option #1, or keeping the hook_menu_link_defaults() hook but just moving it to the .install file. I've made my preference on that clear in #38, but we're obviously far from consensus on that.
Comment #41
catcheffulgentsia's summary of the reasons is pretty good. I'd add that they are often programmatically created along with content entities (i.e. core did this until the book patch landed a couple of days ago), and that would get very messy with syncing configuration. Similarly per-user shortcuts were using menu links until fairly recently too - don't want those staged either.
We have a similar conceptual/implementation problem with path aliases, except we didn't even try to resolve that one at all yet and left them as a 100% special case.
I also think a bad implementation of content entity defaults in YAML would be worse than the current implementation in a hook. With the hook we can hold our hands up and say sorry we didn't have any better ideas. With YAML it's not going to be a proper default content API at all, but it might superficially look like an attempt to create one. And if we bake it into the router files then come up with something better for 9.x, we'll need to go into all the routing YAML and remove it again, whereas the hook is self contained.
If we had declarative admin navigation not using menu links, then just let people create menu links in hook_install() for other stuff, that would solve some of the issues here. While no-one is working on this at all, I do not think it's necessarily too late to change that - after all we're discussing changing this API and 99% of the difficulties are due to administrative link handling.
#1316692: Convert hook_admin_paths() into declarative properties on routes is relevant here, but we'd need to add descriptions and top level categories /somewhere/ to re-implement what we currently have at a minimum.
Comment #42
Gábor HojtsyYeah adding to what catch said, menu items are often tied to nodes too like related taxonomy terms and uploaded files, and therefore make more sense to be considered content attached to other content (like terms and files). Admin menu links are different but I've only seen proposed recently that they should use their own system instead of "the menu system". That would be interesting but way too late now?! I think.
Comment #43
Dries CreditAttribution: Dries commentedGiven that the menu system is something that almost all module developers are being exposed to, I would really like to see us move to YML files for this, for consistency with the rest of the system. Whether that means changing admin menu links from content entities to something else or not, I leave to you all to hash out. I think fixing this API is more important than fixing other API that are more 'under the hood' and only used by a select few.
Comment #44
jhodgdonRE #42, catch said in #41 that it is "not necessarily too late" to change admin navigation to not use menu links.
So, if I can summarize recent discussions:
a) There are good reasons for having menu links be Content Entities, in general (see comment #40 for a nice summary).
b) There aren't really compelling reasons for having admin menu navigation be Menu Link entities, because they're just created by modules, and it's not really necessary to have the ability to edit in the UI and add fields to etc. The reasons that they are menu item entities are: (1) they always have been menu links in the past and (2) they do have a hierarchical structure, so they share some structure with menu links.
c) Nearly every module has at least one admin navigation item to declare, so it would be good DX if declaring admin navigation items is easy to do (and a major DX regression if it is difficult to do). Because they are closely tied to routes, YML is probably preferable to a hook.
d) Creating Menu Item content entities and making sure they are updated/deleted appropriately would be a pain for a module developer (bad DX).
e) catch has stated that it may not be too late to change how the admin navigation works.
====> Could we (in a reasonable amount of time):
Devise a YML-file-based system for modules to declare their admin navigation items (including a way to declare the categories, such as Structure, Configuration, Configuration/Development, etc.), and caching/storing the admin navigation efficiently, with a "Rebuild" button somewhere (and otherwise assuming that the only time we need to rebuild the admin nav is when a module is installed/uninstalled).
Comment #45
dawehnerIf we do make the admin navigation a 100% different subsystem, we will see problems when people use tools like panels/views
to build their own custom admin pages. Admin navigation is certainly not that static as you think.
The problem comes down that config entities "dynamically" create content entities. The current hook_menu_link_defaults rebuilding
handles that pretty nice automatically, so I would like the first alternative.
Comment #46
Gábor Hojtsy@dawehner: well, then if views would need to define an admin menu item, it would need to create a config entity then I guess? The problem is Views, etc. would need to have duplicate solutions, if the person wants to place it in /admin or /somethingelse, different code paths would run and different setup would be needed for items.
BTW a static YAML approach would work really well for translation, since we don't need to be bothered with updating the items. Especially if using config entities for that, then translation is already solved :)
Comment #47
pwolanin CreditAttribution: pwolanin commentedThere is really a deep split in the desired behavior of menu links. Some of them are very close to configuration or plugin definitions, and are rarely modified by site admins, while others are clearly content, and we want them to behave like translatable entities.
Discussing in person with xjm, dawehner, and Gabor, here's a slightly radical proposal, but which might actually satisfy the competing concerns here:
Taking a lead from the block system (and local tasks and local actions) let's make the base implementation of menu links be a plugin using YAML discovery. The custom menu links would be plugin derivates each of which has an associated content entity (like custom_block).
Some further thought/details:
Comment #48
xjm@dawehner also made the suggestion that we could break these changes into more manageable chunks by converting to the yaml definitions first, so that the public API is complete for contributed module developers, and then continue architecting it properly internally once that change is made.
Comment #49
dawehnerTo clarify this bit: For non default yml file ones we can extend the class and not call t(), for the ones defines in static yml files we will call t() and this is it.
+1 for the summary
Comment #50
Gábor Hojtsy@dawehner: agree with that translation direction.
Comment #51
jhodgdon+1 for the proposal in #47. Maybe it should be added to the issue summary?
Comment #52
pwolanin CreditAttribution: pwolanin commentedadded to the issue summary
Comment #53
jhodgdonUm. I think it should have been added to the end of the list, because previous comments already were referring to "proposal 1 from the issue summary" etc.
Comment #54
jhodgdonMoved it, it's now proposal #5.
Comment #55
effulgentsia CreditAttribution: effulgentsia commentedI like #47 a lot. Question about it:
Does this apply to parent and weight as well as to title? I would imagine so. Is that acceptable in terms of feature/UX? Also, if you then have a menu with a mix of custom and noncustom links, is it possible to have drag-and-drop reordering if custom ones can have editable weight, but noncustom can't?
Comment #56
pwolanin CreditAttribution: pwolanin commentedDiscussion last night seemed to have some reasonable comfort going forward with #47, though amateescu expressed some misgivings and desire to continue down the path purely of content entities.
An amendment to the plan concerns customizing the module-provided menu links. For those that are customized we could create a config entity per link only when it's customized OR (likely more performant) have a single config for all admin links. In either case this config would essentially record the different compared to the default for parent (maybe? using a uuid?), weight and enabled/disable. We would still block title editing so as to preserve localization. This could allow changes to the admin section to be deployable.
All the menu link plugins would have a way to invoke methods on save that allow custom data to be persisted.
In the case of module-provided links, this would involve updating the config. For custom menu links, updating the content entity. for e.g. views-provided links, updating the associated view config entity.
Comment #57
effulgentsia CreditAttribution: effulgentsia commentedSingle config seems like a great idea for that.
I think it's okay to disallow title editing if we need to, but why is it necessary if we have a config file for recording customizations? Config files can be localized.
Comment #58
pwolanin CreditAttribution: pwolanin commentedUser entered strings in config should not be localized (passed though t()). However, we should get other people to weigh in on this part.
Comment #59
effulgentsia CreditAttribution: effulgentsia commentedYes. Absolutely. I meant, config files can have translations. I.e., Views titles and Site name can be translated, so why can't customized menu link titles?
Comment #60
dawehnerHere is step one: use the yml files: #2226903: Step 1: Move static menu links to yml files
Comment #61
pwolanin CreditAttribution: pwolanin commented@effulgentsia - I think one of the most serious Drupal WTF moments (though one people rarely discover) is that admin links are localized normally, but if you edit them, might suddenly stop being localized. I think that violates the principle of least surprise :)
Comment #62
dawehnerProposed the second step: #2227179: Step 2: Move the menu tree storage to a separate service. which will potentially allow us to work on parallel on some components.
Comment #63
pwolanin CreditAttribution: pwolanin commentedCan we call this fixed? Do we need a new Meta issue?
Comment #64
Andre-B@pwolanin meta issue would be great, so once everything is done contrib maintainers can adjust the recent changes (again).
Comment #65
xjmLet's use this as a meta. I nearly had a heart attack when I saw this marked fixed. ;)
Comment #66
xjmComment #67
Crell CreditAttribution: Crell commentedI'm just now catching up here. So that's what broke my demo module right before MidCamp... :-)
I am actually extremely wary here. We've already put a ton of work into making links entities, because entities are designed to scale. The old menu links system supported tens of thousands of links without falling over.
I am fairly confident that a content entity can scale to "tens of thousands". If it can't, then we're kinda hosed anyway because there are Drupal sites with millions of nodes.
I am not at all confident (due to lack of data) that config entities or plugins can scale to thousands. Blocks are scaling to dozens, not thousands. Are we talking about having *tens of thousands* of menu link derivative plugins? Can the system even handle that? I don't know, but we've never pushed it that far and I am very very worried that going down this route will come back to haunt us for years to come.
Comment #68
amateescu CreditAttribution: amateescu commentedThat's exactly the concern I raised when this plan was proposed/discussed in a call during Dev Days.
Comment #69
effulgentsia CreditAttribution: effulgentsia commentedThe current proposal does not involve config entities, so we just need to address the plugin scaling question. First of all, even with content entities, there are ways to use the entity APIs that would lead to scaling problems. For example,
entity_load_multiple_by_properties('node', array('type'=>'article'))
would run out of memory if there are more article nodes than can fit into memory. Similarly, having a derivative plugin for every custom menu link would be a problem if there's calling code that invokes$menu_link_manager->getDefinitions()
. So code that works with a menu link manager would need to not do that, just as application code needs to be careful when calling entity_load_multiple*(). Instead, we'll need to add methods to MenuLinkManager for getting the subset of definitions that are needed (e.g., only those that are in a certain menu, or have a certain parent, etc.). Also, we won't be able to use the default plugin manager caching strategy of caching all definitions in a single cache entry; instead, we'll probably only want to cache the base definitions, and then add in the derivatives at runtime (which should be fast as the underlying content entity will have its own cache once #597236: Add entity caching to core lands). Other than that, I don't see where the plugin approach will run into scaling issues. But I do appreciate the concern about this being new territory for us, possibly filled with problems we won't anticipate until we hit them.Comment #70
catchAgreed with #69, there's potentially new problems with this, but some of them are obvious and can be ameliorated.
Note I would be fine with forcing static/config entity links for admin menus, and content entity menu links anywhere else (i.e. split the system in two, avoiding the need for content menu link plugin derivatives), but I think I'm the only person that thought that was an entirely OK restriction when this came up.
Comment #71
amateescu CreditAttribution: amateescu commentedYou weren't the only one ;)
Comment #72
catch@amateescu, that's good to know. So I think it's OK to proceed with static + config entities as menu link plugins. Then when we get to fitting content entities to work with that, we should be prepared for the possibility that it just won't, in which case splitting it is still possible at that point. Or maybe we should re-open that discussion in a dedicated issue now?
Comment #73
webchickFor whatever it's worth, the distinction between "module-provided thingies" and "custom UI-created thingies" makes sense to me, too. We have already established this pattern with blocks, so it's something both developers and end-users will have some familiarity with.
The only thing I'd ask is that we take care to make the UI for "using" the thingies look and work the same regardless of what the underlying implementation looks like. In other words, we have one "place blocks" UI, and it lists both module-provided/custom-created blocks. The end user shouldn't need to know the origin of these menu links when building out their menus; the UI should work the same for both.
Comment #74
Gábor HojtsyI'd like to reiterate what @effulgentsia said in #69 above, "The current proposal does not involve config entities", the proposed static YAML solution for admin menu items would be similar to the current local tasks and local actions solution, not as a config entity.
Comment #75
catch@Gabor, in Szeged we discussed that it would be necessary to use configuration entities (or a state() entry but that sounded bad to me) in the case that a module-provided link is overridden (title/parent/weight/disabled). Has that plan changed?
If we're not going to allow admin menu links to be overridden by the UI, then I don't see any reason for those to be treated as menu links at all - they could then use exactly the same approach to tasks/actions - just parse the YAML and create a UI from that. No editing at all.
Comment #76
effulgentsia CreditAttribution: effulgentsia commented#56 says:
The second part of that OR statement sounds way more sane.
Comment #77
Gábor HojtsyYeah it makes sense to somehow store the customisations in config yup.
Comment #78
pwolanin CreditAttribution: pwolanin commentedI've posted a new meta issue for those who wish to discuss the plan we've developed to use plugins #2256497: [meta] Menu Links - New Plan for the Homestretch
I propose to mark this issue fixed if that plan is acceptable.
Comment #79
xjmRemoving beta blocker tag in favor of #2256497: [meta] Menu Links - New Plan for the Homestretch since this is a meta.
Comment #80
effulgentsia CreditAttribution: effulgentsia commentedIf #79 is ok to do, then I think closing this as a duplicate is even better. If that plan isn't accepted, then we can reopen this as a beta blocker.
In some ways, I actually see this issue as "fixed" in that "declaring menu links" has been finalized as of #2226903: Step 1: Move static menu links to yml files. However, #2256497: [meta] Menu Links - New Plan for the Homestretch or similar is needed to make the underlying system coherent with those declarations.
Comment #81
pwolanin CreditAttribution: pwolanin commented