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.
Problem/Motivation
When adding optional integrations with contrib modules, it is common to use the "_module_dependencies" requirement on a declared route.
For example, a Devel integration for commerce_store:
commerce.store_devel:
path: '/admin/commerce/config/store/{commerce_store}/devel'
defaults:
_content: '\Drupal\commerce\Controller\CommerceDevelController::storeLoad'
_title: 'Dump a store'
options:
_admin_route: TRUE
requirements:
_module_dependencies: 'devel'
_permission: 'access devel information'
Based on this route, a local task is defined as well.
Now, if Devel is disabled, the route is skipped during the rebuilding process, and it never reaches the database.
The local task is also not shown.
However, trying to access another local task on the same level (store "Edit", for example) causes a crash:
Symfony\Component\Routing\Exception\RouteNotFoundException: Route "commerce.store_type_devel" does not exist. in Drupal\Core\Routing\RouteProvider->getRouteByName() (line 150 of core/lib/Drupal/Core/Routing/RouteProvider.php).
Drupal\Core\Routing\RouteProvider->getRouteByName(commerce.store_type_devel)
Drupal\Core\Menu\LocalTaskDefault->getRouteParameters(Symfony\Component\HttpFoundation\Request)
Drupal\Core\Menu\LocalTaskManager->getTasksBuild(commerce.store_type_edit)
Proposed resolution
The LocalTaskManager should skip checking local tasks based on missing routes.
Comment | File | Size | Author |
---|---|---|---|
#76 | interdiff_72-76.txt | 11.24 KB | ravi.shankar |
#76 | 2315801-76.patch | 24.61 KB | ravi.shankar |
#72 | 2315801-72.patch | 24.92 KB | DieterHolvoet |
#68 | interdiff.txt | 9.47 KB | jurgenhaas |
#68 | 2315801-68.patch | 25.03 KB | jurgenhaas |
Comments
Comment #1
bojanz CreditAttribution: bojanz commentedHere's a patch with a proposed fix. The comment could be improved.
Needs a test, of course.
Something similar might be desirable for menu links as well, dblog needs to implement dblog_menu_links_discovered_alter() because one of its routes
depends on the search module. The situation is less urgent there because the alter hook provides an alternative, unlike with local tasks (where implementing hook_menu_local_tasks_alter() doesn't behave correctly).
Comment #2
bojanz CreditAttribution: bojanz commentedComment #3
bojanz CreditAttribution: bojanz commentedComment #4
bojanz CreditAttribution: bojanz commentedI've spoken to dawehner about introducing a dependency key on the menu link/task/action plugin definitions, and am working on that, but it makes sense to finish this patch as well, because I don't think the system should crash if the route is missing for any reason.
Comment #5
bojanz CreditAttribution: bojanz commentedAnd here's the alternative discussed with dawehner.
Comment #6
bojanz CreditAttribution: bojanz commentedNow with 100% more content.
Comment #7
bojanz CreditAttribution: bojanz commentedMoved the trait to the plugin namespace, reduced some boilerplate code (IRC review).
Comment #8
dawehnerDo we still need both? I guess you just forgot to drop that code.
Just put a newline between them.
Comment #11
jhodgdonComment #12
dawehnerYou could argue that 'dependencies][module' could be used as well, but this would imply that other dependencies are supported which is simply not the case. Let's be more parallel with routing instead.
Comment #13
mglamanReroll of #7 with updates from review in #8
Comment #15
dawehnerWorked a bit on this issue:
Just a general idea: we could pull in the provider filtering as well.
Comment #17
dawehnerI totally forgot that traits sucks.
Comment #18
jibranI was thinking how can we create a test only patch for this bug?
Comment #19
dawehnerWell, it should be for sure possible to create a menu link in some new test module which depends on the existance of some other module.
* Check that the link is not there is the other module exists
* Enable the other module, check that the link appears.
Comment #20
pcambraI've tried to do the test that @dawehner mentions in #19 and create a new module with the route dependent on another module (menu_ui in this case), then enable the second module, but the link seems to be there for both cases, I'll ask for feedback.
Comment #21
dawehnerMaybe also check the error in the IS which is executing the local task manager?
Comment #23
dawehnerMaybe more like this? Open for discussion.
Comment #26
jibranI am not sure about this change but other then this it is RTBC.
Comment #29
Ralt CreditAttribution: Ralt commentedRerolled #23 to have a non-conflicting class name.
Comment #30
bojanz CreditAttribution: bojanz commentedComment #32
dawehner#26 should be adressed afaik ... if possible it makes sense to define it both routing level ... if you don't control the route, maybe define it on the menu link level as well.
Comment #33
dawehnerAs talked with @pcambra: It would be cool if the property set on the route would be automatically inherited/reflected for the menu links as well.
Comment #34
pcambraEdit: This is not the patch you're looking for.
Comment #35
pcambraGenerated an interdiff with patchutils, it should be easier to review.
Comment #36
pcambraOk, so I had added an extra file totally unrelated, sorry about that. Here's the right patch with interdiff and all.
As discussed with @dawehner, adding the check on the routes as well as on the menu links, so if you add module_dependencies to either part, it would work.
Sorry for the horrible code in that part, but I'm not really sure how to get the routes in a clean way (I tried to inject the service but there's already a constructor in this part). Directions on how to improve that part are highly appreciated.
Also, I've noticed a mismatch when using YamlDiscovery, some bits like RouteBuilder use the component directly whereas other parts use the plugin. Is that expected?
Comment #37
dawehnerso a) we do we not just check for 'route_name' of the plugin definition in filterByModuleDependencies to copy it over? (Note: here you just use it for menu links, but don't you want to have the exact same behaviour for local tasks / actions ...
I don't see a problem with that :) Stuff which aren't plugins (routes) don't use the YamlDiscovery class from plugins, stuff which are plugins use the YamlDiscovery class
from plugins, which itself uses the other yaml discovery internally.
Comment #38
dawehner.
Comment #39
pcambraHere's a second try applying #37, I have add an additional check instead of merging the dependencies because it would be very hard to combine something like ['a','b','c'] with 'd','e' or 'f'+'g'
Comment #43
dawehnerGood question whether we have to merge, I would be fine with just overriding the value from the parent, if defined ... What do you think about it?
Comment #44
pcambraHere's another take, I think I had the wrong check.
What if there's a mismatch? if the plugin depends on the module but the route doesn't, isn't this forcing to have it defined in both places?
Comment #46
dawehnerHow can the menu link depend on it, but not the route? Maybe its just an example which is not obvious for me.
Comment #47
pcambraWhat about this then?
I've had to add unit test expectations about the getModuleDirectories method of the module handler that is mocked as the filterByModuleDependencies method is called from the DefaultPluginManager as well.
Comment #49
dawehnerwhat is with that?
Comment #50
pcambraIt was a commented line I leftover in #44, still dealing with some odd test failures
Comment #51
pcambraMissed one of the manager tests.
Comment #52
jibranLet's fix this.
Contains
Comment #53
pcambraHere we go.
Comment #54
bojanz CreditAttribution: bojanz commentedAnd back.
Comment #55
alexpottThe trait is being used in the DefaultPluginManager which is for all plugins but contains very specific assumptions that it is dealing with routes.
Comment #56
bojanz CreditAttribution: bojanz commentedAs we said on IRC, the naming and usage of the trait is weird now that it automatically uses the route's dependencies (which is definitely something we want to do).
So, we want to remove the trait from the DefaultPluginManager, ensure that the menu link, local action, local task managers use it.
We also need to rename it, perhaps to RouteModuleDependency? "Provides a trait for checking the module dependencies of a plugin's route." ?
EDIT: I was slower than Alex on the "needs work".
Comment #65
jurgenhaasRe-rolled the patch for 8.9.x-dev in order to continue working on this.
Only problem has been that I couldn't apply the last test any more:
There is no reference in the new code which would allow me to re-apply this change. Maybe somebody could help me with this?
I'm planning to work on comments from #55 next.
Comment #66
jurgenhaasI've now moved the ModuleDependencyTrait out of the default plugin manager over to the route related plugin managers in the Drupal\Core\Menu namespace.
Comment #67
ravi.shankar CreditAttribution: ravi.shankar at OpenSense Labs commentedPatch #66 didn't apply.
Comment #68
jurgenhaasSorry for that, for some reason my patch was created with a duplicated base path for each file. Not sure how that happened but it's fixed now.
Comment #70
ravi.shankar CreditAttribution: ravi.shankar at OpenSense Labs commentedComment #72
DieterHolvoet CreditAttribution: DieterHolvoet as a volunteer commentedI re-rolled the patch for 9.4.x.
Comment #73
DieterHolvoet CreditAttribution: DieterHolvoet as a volunteer commentedI encountered this issue using the Scheduler module: it adds a local task pointing to a module-provided view. When I disable this view, the site crashes because the route no longer exists. The current patch doesn't fix the issue, because Views doesn't add
module_dependencies
to its routes.Comment #74
DieterHolvoet CreditAttribution: DieterHolvoet as a volunteer commentedComment #76
ravi.shankar CreditAttribution: ravi.shankar at OpenSense Labs commentedFixed Drupal CS issues of patch #72.