Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
related to #1908756: Separate Action Links (MENU_LOCAL_ACTION) from hook_menu()
For tabs we need to have hierarchy which distinguishes them from local actions.
A knotty problem to be solved is also the _to_arg functionality which is used in tracker module and other places. The pattern for solving this would probably apply to both tabs and local actions. Basically we need to be able to call a function to populate a value when constructing a synthetic path to make a request object.
Follow ups:
Comment | File | Size | Author |
---|---|---|---|
#55 | local-task-2004334-55.patch | 41.37 KB | pwolanin |
#48 | local-task-2004334-48.patch | 41.31 KB | dawehner |
#48 | interdiff.txt | 3.26 KB | dawehner |
#46 | local_task-2004334-46.patch | 41.25 KB | dawehner |
#37 | 2004334-37.patch | 41.1 KB | fubhy |
Comments
Comment #1
tim.plunkettTagging
Comment #2
pwolanin CreditAttribution: pwolanin commentedfollowing on from the work in #1981644: Figure out how to deal with 'title/title callback', it would make snese to switch to plugins rather than a hook.
Comment #3
pwolanin CreditAttribution: pwolanin commentedThis is a starting patch - doesn't actually sort the hierarchy yet, etc. Just to show the direction.
Comment #4
pwolanin CreditAttribution: pwolanin commentedOk, this is generally working right - posting so we can get a test run.
Comment #5
pwolanin CreditAttribution: pwolanin commentedIncludes fixes for the active class on the tab LI and added tests from dawehner
Comment #7
pwolanin CreditAttribution: pwolanin commentedWith more tests/fixes from dawehner
Comment #9
pwolanin CreditAttribution: pwolanin commentedSimple fix for test - set a weight so the tabs are in the expected order.
Comment #11
pwolanin CreditAttribution: pwolanin commentedAh, same problem - we need to be explicit about weight for this tab.
Comment #12
sdboyer CreditAttribution: sdboyer commentedgenerally speaking, this looks sane. i'm still a touch weirded out by the use of plugins for this, but that's ok.
the thing that's keeping me from RTBCing this is how the logic has been split.
MenuLocalTaskManager::getTasksBuild()
is the only public method that's ever actually called on this thing, and it seems rather singly-focused on doing the exact same thing as the rest of the logic inmenu_local_tasks()
. if there isn't another use case for calling that method, then i think that logic should be moved back in tomenu_local_tasks()
, at which point the other public methods onMenuLocalTaskManager
will suddenly have a reason to be public. otherwise, we're effectively just dividing one logical task into two separate places - and to find the second place frommenu_local_tasks()
, you have to check system.services.yml to find the class that actually comes back fromplugin.manager.system.menu_local_task
.from what i can tell, the only thing that would really need to change in moving it back out to the function is asking the container for the route provider directly. if that's the case, we can hit
\Drupal
for that.i imagine that your response to this will be, "we have it this way so people can swap the logic," and/or "we're intending to get rid of the rest of
menu_local_tasks()
and have it just be this." if that's the case, i could probably be convinced to RTBC as-is, but for the former i'd like more details on whether or not such a use case is realistic, and for the latter i'd like to see a followup issue for that removal :)Comment #13
pwolanin CreditAttribution: pwolanin commentedper Tim, needs work - need to restore and fake up hook_menu entries so breadcrumbs don't break until they are really fixed.
Comment #14
pwolanin CreditAttribution: pwolanin commented@sdboyer - yes, basically all the rest of the code in menu_local_tasks() dealing with tabs and actions should go away.
Comment #15
dawehnerAdd a first basic unit test for the local task manager.
Comment #16
dawehnerAdded some of the followups to the summary.
Comment #17
pwolanin CreditAttribution: pwolanin commentedbreadcrumb fixes by adding back minimal hook_menu entries, added comments, and added unit test from dawahner
Comment #17.0
pwolanin CreditAttribution: pwolanin commentedAdded follow ups
Comment #18
sdboyer CreditAttribution: sdboyer commented@pwolanin added comments and opened a followup for removing the rest of
menu_local_tasks()
at #2032587: Clear menu_router code out of function menu_local_tasks() when all actions and tabs are converted to plugins. that meets my criteria. tim can knock it back if he's not OK :)Comment #19
tim.plunkettRTBC +1, this works for me.
Comment #20
YesCT CreditAttribution: YesCT commentedThis issue was RTBC and passing tests on July 1, the beginning of API freeze.
Comment #21
fubhy CreditAttribution: fubhy commentedFixing some minors... Also, same thing as over at #2031473: Convert menu local actions to plugins so that we can generate dynamic titles and paths: We should not put the controller resolver resolved methods in the interface.
Also, setters should always return $this imho unless they can return something else that makes sense. The rest are just name changes, 180 character limit fixes and fixing copy&paste issues (local actions/local tasks).
Comment #22
dawehnerPersonally I prefer the interface one, as we do the same with for form interface as well and an interface is a nicer documentation then an exception but I won't block that.
Comment #23
fubhy CreditAttribution: fubhy commentedForms are weird and special because of FormAPI and the form state/form array we got there. I'd prefer properly calling the form builder methods there at some point too though. But currently, as you will see by looking at HtmlFormController we do a lot of weird things with $form and $form_state and the request at that point. We may be able to fix that later. I hope so at least.
Comment #25
tim.plunkettNo no no no no.
I pushed back on the other issue about exceptions vs interfaces, lets not have this debate again. We agreed to use interfaces, and the patch was green and RTBC.
Comment #26
fubhy CreditAttribution: fubhy commentedThe unit tests were stile trying to invoke the getTitle() / getPath() method on the interface mocks which don't have that anymore. Mocking the base class instead and testing for \BadMethodException when the method is missing.
Comment #27
dawehnerThese unit tests addition looks great!
Comment #28
fubhy CreditAttribution: fubhy commentedDaniel, calm down. Every time you see a new unit test you completely freak out :D.
dawehner++
Comment #29
tim.plunkettI withdraw my RTBC. This goes against the consensus we had.
Comment #30
fubhy CreditAttribution: fubhy commentedOkay, we agreed on IRC that we will do the interface/no interface thing in a follow-up. Daniel is going to come back with a re-roll (I gotta run in a few, office closes).
Comment #31
dawehnerThis adds #26 + #21 without the code changes.
Comment #32
dawehnerFollow up: #2035105: Figure out how to inject abritrary arguments into controllers/local action/local tasks whatever else plugins
Comment #34
tim.plunkett#31: drupal-2004334-30.patch queued for re-testing.
Comment #36
dawehnerDamn.
Comment #37
fubhy CreditAttribution: fubhy commentedGoing back to mocking the interface instead of the base class now that we got the methods on the interface again.
Comment #38
dawehnerThis changes looks great, but yeah they don't matter that much.
Comment #39
YesCT CreditAttribution: YesCT commentedwth just happened since #20? can you say in englishish?
Comment #40
tim.plunkettSince #20? @fubhy rewrote the patch with a new approach, added better docs, and the we agreed to return to the old approach.
So it should be almost the same as #20, but with better docs.
Comment #41
tim.plunkettSomeday we'll fix that bug.
Comment #42
fubhy CreditAttribution: fubhy commentedWe do not have the perfect solution for the getTitle/getPath methods yet and neither the patch from #17 nor the patch from #21 should be considered a final solution. It's not an easy problem for which we don't have a solution yet, so Daniel opened a follow-up to discuss that and we agreed to revert back to #17 as that's what we previously had consensus about.
In short, the question is: How do we 'inject' contexts into plugins. The problem basically is that due to these dynamic contexts the methods signature would be different for each plugin (if we assume that we want to forward the contexts as method arguments, which is what this patch does) which makes using an interface rather hard/weird because in order to match the interface all the additional, dynamic arguments of the method have to be declared as optional by setting a default value ($foo = NULL).
Here is the follow-up: #2035105: Figure out how to inject abritrary arguments into controllers/local action/local tasks whatever else plugins
Comment #43
fubhy CreditAttribution: fubhy commentedc'mon d.o ...
Comment #44
tim.plunkettThis needs a reroll in the theme of #2031473-28: Convert menu local actions to plugins so that we can generate dynamic titles and paths, including the removal of ContainerFactoryPluginBase (#2033447: Remove obsolete ContainerFactoryPluginBase).
I'll try to get to this during lunch or this evening, but if someone wants to jump in before me, feel free.
Comment #45
dawehnerAssign to myself.
Comment #46
dawehnerComment #47
tim.plunkettIn the other, we had plugin.manager.menu.local_action, with the extra .menu.
Is this really needed? I can't think of another time we've done this.
@var array|null (optional)
This could probably have a default of 0 just for consistency.
@var int|null (optional)
@var array (optional)
Missing @return
no . at the end of @see
Comment #48
dawehnerThank you for the review.
There is a logic in the localtaskBase which sets the weight to -10, if the tab is basically the default tab. This is only applied if this value was not specified (null), but is not triggered when 0 is specified.
Comment #50
dawehner#48: local-task-2004334-48.patch queued for re-testing.
Comment #51
tim.plunkettThanks, the -10 part is a perfect explanation :)
Comment #52
Dries CreditAttribution: Dries commentedThis patch does not seem to apply. Asking for a re-test.
Comment #53
Dries CreditAttribution: Dries commented#48: local-task-2004334-48.patch queued for re-testing.
Comment #55
pwolanin CreditAttribution: pwolanin commentedrerolled for the expected services.yaml conflict.
Comment #56
tim.plunkettManually tested, and the patch is identical.
Comment #57
Dries CreditAttribution: Dries commentedCommitted to 8.x. Thanks!
Comment #58
cweagansThis needs a change notice.
Comment #59
jibranUpdating title.
Comment #60
catchWas this profiled?
Is there a follow-up for the @todo?
Comment #61
pwolanin CreditAttribution: pwolanin commented@catch - I'm going to try to make sure all the change notices and follow-ups are in place tonight or tomorrow- have been busy/traveling since this was committed.
Comment #62
pwolanin CreditAttribution: pwolanin commentedIn fact since we are now extending DefaultPluginManager, getDefinitions() is already cached - I think that changed in #1903346: Establish a new DefaultPluginManager to encapsulate best practices
We should just remove the @todo I think.
Comment #63
tim.plunkettDefaultPluginManager gives you static caching for free, you have to do some extra work in the constructor for persistent caching (which I don't know that we want anyway).
Comment #64
catchThat's why we should profile it ;)
Comment #65
pwolanin CreditAttribution: pwolanin commentedWe should either do persistent caching of the definitions, or for the set of local tasks found for a given route.
As the number of these plugins grows, I'd guess we'd want both? However, the trick will be to effectively clear this cache if e.g. there is a new plugin available (e.g. if Views adds a new derivative Plugin dynamically for a new tab).
Comment #67
tim.plunkett$manager->clearCachedDefinitions()
Right now that clears the static cache, if we add persistent caching it will handle that as well.
Comment #68
pwolanin CreditAttribution: pwolanin commentedchange notice at https://drupal.org/node/2044515
Comment #69
jibranDo we have an issue for this?
Comment #70
pwolanin CreditAttribution: pwolanin commentedShould probably at least add this to #2026075: [meta] Remove drupal_set_breadcrumb and LegacyBreadcrumbBuilder
And/or we should simple rip all breadcrumbs out of core and have a null service by default.
Comment #72
joachim CreditAttribution: joachim commentedReopening.
The change notice at https://drupal.org/node/2044515 is not clear at all.
Round about this point:
"These example tabs are simply using the base class - and all the needed definition information is encapsulated in the YAML:"
Each local task is represented by a plugin [how?]. Each plugin definition (found in the yml file [which yml file? routing.yml, or another one?]) contains three to five loal-task specific keys underneath the top-level key which is the unique plugin ID: [how?]
Comment #73
dawehnerA few improvements: https://drupal.org/node/2044515/revisions/view/2835083/2838581
Comment #74
dawehneroh tags ...
Comment #75
pwolanin CreditAttribution: pwolanin commentedI think the change notice is ok: https://drupal.org/node/2044515
Comment #76
YesCT CreditAttribution: YesCT commentedI think we might use this, or make a meta to go through each module and convert their use of the hook to plugins.
there could be a problem though, See config_translation issue #2044737: Provide local tasks as plugins and core issue #2095325: using plugins to add tabs is not as capable as hook_menu()
Comment #77
joachim CreditAttribution: joachim commentedThe change record doesn't explain what the plugins are like -- what class are they? What plugin type?
> Note that you have full control over this behavior by implementing your own plugin class for a specific tab.
How do you do that?
Comment #78
pwolanin CreditAttribution: pwolanin commentedJust added a line indicating the default class.
Comment #79.0
(not verified) CreditAttribution: commentedadd #2032587 to summary