1

I would like my local tasks (tabs), defined in mymodule.links.task.yml, to appear only if a certain module is enabled. If a certain module is disabled, I want the tab to be hidden.

mymodule.alpha:
  route_name: mymodule.alpha
  base_route: mymodule.alpha
  title: 'Alpha'

mymodule.bravo:
  route_name: mymodule.bravo
  base_route: mymodule.alpha
  title: 'Bravo'

My understanding is that this can be acheived by adding a _module_dependencies key to my routing.yml file.

_module_dependencies: Optionally use this key to specify one or more modules that are required to serve this path. You can combine module names with a + (plus) for an AND relationship or , (comma) for an OR relationship. For example, _module_dependencies: 'node + search' means both node and search are needed, _module_dependencies: 'node, search' means either node or search are needed. If your module already depends on other modules for its behavior (via info.yml dependencies), there is, of course, no need for specifying the dependency here as well; however, for optional dependencies, where routes are provided only if those optional dependent modules are also enabled, this is a useful option.

https://www.drupal.org/docs/8/api/routing-system/structure-of-routes

mymodule.alpha:
  path: '/admin/reports/mymodule/alpha'
  defaults:
    _controller: Drupal\mymodule\Controller\AlphaController::render
    _title: 'Alpha'
  requirements:
    _permission: 'access site reports'
    _module_dependencies: 'comment' # this is module is enabled, this line does not cause a white screen on cache rebuild

mymodule.bravo:
  path: '/admin/reports/mymodule/bravo'
  defaults:
    _controller: Drupal\mymodule\Controller\BravoController::render
    _title: 'Bravo'
  requirements:
    _permission: 'access site reports'
    _module_dependencies: 'statistics' # this is module is disabled, this line does cause a white screen on cache rebuild

However, if a module is disabled, I get this error:

Symfony\Component\Routing\Exception\RouteNotFoundException: Route "mymodule.bravo" does not exist. in Drupal\Core\Routing\RouteProvider->getRouteByName() (line 187 of /var/www/mysite/core/lib/Drupal/Core/Routing/RouteProvider.php).

The error is thrown even when I visit: /admin/reports/mymodule/alpha.

  • This is a head scratcher, Did you clear the cache? Looks like you might have to use _custom_access: and do the module check logic in here... – No Sssweat Apr 21 '17 at 7:10
  • 1
    When developing, always enable verbose error logging, then you will get an error with the actual problem instead of useless wsod. One reason for that could be that the controller extends or uses something that statistics.module provides, AFAIK the controller class is still loaded, as the route still exists, it will just deny access at runtime. The safest option is to define the route dynamically. – Berdir Apr 21 '17 at 17:40
  • 1
    To add to @Berdir comment, You can enable verbose error logging by adding $config['system.logging']['error_level'] = 'verbose'; to your development environment settings.php file. – No Sssweat Apr 23 '17 at 8:12
2

I believe my problem was that if the module was disabled then the route would not exist - and this would have my links.task.yml trying to create a local task from a non existent route.

My solution was to generate a dynamic local task instead, so that the local task is only created, if the module is enabled:

mymodule.links.task.yml

mymodule.bravo:
  deriver: 'Drupal\mymodule\Plugin\Derivative\BravoLocalTasks'

src/Plugin/Derivative/BravoLocalTasks.php

namespace Drupal\mymodule\Plugin\Derivative;

use Drupal\Component\Plugin\Derivative\DeriverBase;

/**
 * Generates local tasks.
 */
class BravoLocalTasks extends DeriverBase {

  /**
   * {@inheritdoc}
   */
  public function getDerivativeDefinitions($base_plugin_definition) {
    $moduleHandler = \Drupal::service('module_handler');
    if ($moduleHandler->moduleExists('statistics')) {
      $this->derivatives['mymodule.bravo'] = $base_plugin_definition;
      $this->derivatives['mymodule.bravo']['route_name'] = 'mymodule.bravo';
      $this->derivatives['mymodule.bravo']['base_route'] = 'mymodule.alpha';
      $this->derivatives['mymodule.bravo']['title'] = 'Bravo';
    }
    return $this->derivatives;
  }
}

I believe I will have to create a distinct class for every route that requires dynamic local tasks.

If you have a better solution, please post.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.