Do you want to know how to Drupal?

Let's Drupal

Menu

How to add local tasks dynamically in Drupal 8?

In some cases we want to generate local task based on some data. In our example we want to provide custom settings per node type for it we will provide local tasks based on node types. 

As usual full example you can find here: https://github.com/LOBsTerr/drupal-modules-examples

First we need to define deriver plugin, which will expose the local tasks dynamically for us.

We will setup a route, which will accept a parameter "type" and we will just print it in order to see that it is a different page.

local_tasks.dynamic_tasks:
  path: '/dynamic-tasks/{type}'
  defaults:
    _controller: '\Drupal\local_tasks\Controller\LocalTasksController::dynamicTasks'
    _title: 'Dynamic tasks'
    type: ''
  requirements:
    _permission: 'access content'

and controller method

public function dynamicTasks($type = NULL) {
    return [
      '#markup' => $this->t('This is an example : @type', [
        '@type' => $type
      ]),
    ];
  }

Then we add in your file [your_module_name].links.task.yml (in our case local_tasks.links.task.yml), we set a deriver class

# Dynamically add local tasks.
local_tasks.dynamic_tasks:
  route_name: 'local_tasks.dynamic_tasks'
  title: 'Dynamic tasks'
  base_route: 'local_tasks.dynamic_tasks'
  deriver: Drupal\local_tasks\Plugin\Derivative\NodeTypeLocalTask # We set derive class, which will provide local tasks dynamically.

 

Now we can add a class Drupal\local_tasks\Plugin\Derivative\NodeTypeLocalTask.php

<?php

namespace Drupal\local_tasks\Plugin\Derivative;

use Drupal\Component\Plugin\Derivative\DeriverBase;

/**
 * Provides dynamic tabs based on node types.
 */
class NodeTypeLocalTask extends DeriverBase {

  /**
   * {@inheritdoc}
   */
  public function getDerivativeDefinitions($base_plugin_definition) {
    // Get node types.
    $node_types = \Drupal::entityTypeManager()
      ->getStorage('node_type')
      ->loadMultiple();

    foreach ($node_types as $node_type_name => $node_type) {
      $this->derivatives[$node_type_name] = $base_plugin_definition;
      $this->derivatives[$node_type_name]['title'] = $node_type->label();
      $this->derivatives[$node_type_name]['route_parameters'] = ['type' => $node_type_name];
    }

    return $this->derivatives;
  }

}

 

As you can see above, we get list of node types and pass title and route parameters. The route name comes from local_tasks.links.task.yml

The only thin left is to clean the cache and open /dynamic-tasks/, you should see the list of node types as tabs.

Like this you can easily add local tasks dynamically on your custom page applying more complicated logic in Drupal.

 


How to hide a local task (tab) in Drupal?

Quite often we want to have a parent tab to be selected, but the sublevel tasks are not displayed or we just want remove completely some tabs for user of specific roles. 

There are two ways to do it:

The first option is to use hook_menu_local_tasks_alter

function your_module_local_tasks_alter(&$local_tasks) {
  unset($local_tasks['id_of_local_tasks']);
}

This code will remove your tab completely from the system, it means it will not be displayed anywhere, but sometimes we want to hide the tab only on the specific pages

The second option is to use hook_menu_local_tasks_alter

function your_module_menu_local_tasks_alter(&$data, $route_name) {
  $routes = ['entity.node.canonical']; // add your routes to this array and your tab will be hidden for this routes
  if (in_array($route_name, $routes)) {
    unset($data['tabs'][0]['id_of_local_task']); 
  }
}

 


How to add a local task to custom page in Drupal?

In the previous post How to add a local task (tab) in Drupal?, we have reviewed how to add a static tab to the page with tabs, but what if we want to add a tab to our custom page?

Lets imagine we have a parent page and two child pages

E-shop Settings

  • Products settings
  • Categories settings

You can find the final result here: https://github.com/LOBsTerr/drupal-modules-examples/tree/master/local_tasks

First we will add custom pages and routes for them, for this we need to add to your controller (in my case LocalTasksController.php)

public function eshopSettings() {
    return [
      '#markup' => $this->t('This is a parent settings page. This page has two child pages: <a href="@products_settings_url">Products settings</a> and <a href="@categories_settings_url">Categories settings</a>', [
        '@products_settings_url' => Url::fromRoute('local_tasks.products_settings')->toString(),
        '@categories_settings_url' => Url::fromRoute('local_tasks.categories_settings')->toString(),
      ]),
    ];
  }

  public function eshopMetaSettings() {
    return [
      '#markup' => $this->t('This is a meta settings page'),
    ];
  }

  public function productsSettings() {
    return [
      '#markup' => $this->t('This is a products settings page'),
    ];
  }

  public function categoriesSettings() {
    return [
      '#markup' => $this->t('This is a categories settings page'),
    ];
  }

Then we will provide routes for them

# Routes for custom page (eshop) tabs.
local_tasks.eshop_settings:
  path: '/eshop-settings'
  defaults:
    _controller: '\Drupal\local_tasks\Controller\LocalTasksController::eshopSettings'
    _title: 'E-shop settings'
  requirements:
    _permission: 'access content'

local_tasks.eshop_meta_settings:
  path: '/eshop-meta-settings'
  defaults:
    _controller: '\Drupal\local_tasks\Controller\LocalTasksController::eshopMetaSettings'
    _title: 'E-shop meta settings'
  requirements:
    _permission: 'access content'

local_tasks.products_settings:
  path: '/eshop-settings/products-settings'
  defaults:
    _controller: '\Drupal\local_tasks\Controller\LocalTasksController::productsSettings'
    _title: 'Products settings'
  requirements:
    _permission: 'access content'

local_tasks.categories_settings:
  path: '/eshop-settings/categories-settings'
  defaults:
    _controller: '\Drupal\local_tasks\Controller\LocalTasksController::categoriesSettings'
    _title: 'Categories settings'
  requirements:
    _permission: 'access content'

Once it is done, you can check these pages.

/eshop-settings

/eshop-meta-settings

/eshop-settings/products-settings

/eshop-settings/categories-settings

The next step to add tabs. First we define the default tab. For the default tab the base_route should be equal route_name 

# in this example we add tabs for custom page.
local_tasks.eshop_settings: # The ID of local task is the same as route, to make it easier to control.
  route_name: 'local_tasks.eshop_settings' # A route for e-shop settings, provided in local_tasks.routing.yml.
  title: 'Eshop settings' # This string will be displayed as a title of a tab.
  base_route: 'local_tasks.eshop_settings' # !!!important For default tab the base_route should be the same as route_name.
  weight: 10

local_tasks.eshop_meta_settings: # The ID of local task is the same as route, to make it easier to control.
  route_name: 'local_tasks.eshop_meta_settings' # A route for e-shop settings, provided in local_tasks.routing.yml.
  title: 'Eshop meta settings' # This string will be displayed as a title of a tab.
  base_route: 'local_tasks.eshop_settings' # This will allow to display it in the same level with "E-shop settings"
  weight: 20

local_tasks.products_settings: # The ID of local task is the same as route, to make it easier to control.
  route_name: 'local_tasks.products_settings' # A route for e-shop settings, provided in local_tasks.routing.yml.
  title: 'Products settings' # This string will be displayed as a title of a tab.
  base_route: 'local_tasks.eshop_settings' # Because we want to group tabs we should set to default one, in our case E-shop settings.
  parent_id: 'local_tasks.eshop_settings' # This will display item under "Eshop settings" tab.
  weight: 10

local_tasks.categories_settings: # The ID of local task is the same as route, to make it easier to control.
  route_name: 'local_tasks.categories_settings' # A route for e-shop settings, provided in local_tasks.routing.yml.
  title: 'Categories settings' # This string will be displayed as a title of a tab.
  base_route: 'local_tasks.eshop_settings' # Because we want to group tabs we should set to default one, in our case E-shop settings.
  parent_id: 'local_tasks.eshop_settings' # This will display item under "Eshop settings" tab.
  weight: 20

Clean the cache and open /eshop-settings, you will see your tabs. At that moment all tabs are in the same level, but we want them displayed as a sublevel. For these we need to define parent_id and set it to local_tasks.eshop_settings in order to display them as under "E-shop settings".

Keep in mind that there should be at least two tabs in the level. In other case the tabs will be hidden.

 

 


How to add a local task (tab) in Drupal?

In Drupal we can display secondary menu as tabs. If you don't know what it is open any content item (node) as administrator and on the top you can see:

  • View 
  • Edit
  • Delete

and so on

In this post we will review how to add a static local task

In order to have a new tab you need to create a new YML file in the root of your module. This file has a specific name your_module_name.links.task.yml. You have to replace your_module_name with the name of your module

Then you can define the local task you want to provide in the next format  

your_module.admin: # Unique id of your tasks normally prefixed with route name or entity name 
  route_name: your_module.custom_settings # Route which will be opened, when we click on the tab
  title: 'Settings' #Title of your tab
  base_route: entity.node.canonical #Parent route (the default tab) 
  parent_id: entity.node.canonical # Optional: We define the parent tab id here
  weight: 100 # Defines the order for the tab

Let's review a simple example. I have module local_tasks. You can find it here  https://github.com/LOBsTerr/drupal-modules-examples/tree/master/local_tasks or create your own.

I have a custom page, which will provide the extra settings for nodes, in fact it will just display the title of a node for a sake of simplicity. This a route for which we are going to add a tab. You need to add it to local_tasks.routing.yml

 

local_tasks.node_extra_settings:
  path: '/node/{node}/extra-settings'
  defaults:
    _controller: '\Drupal\local_tasks\Controller\LocalTasksController::nodeExtraSettings'
    _title: 'Node extra settings'
  requirements:
    _permission: 'access content'
  options:
    parameters:
      node:
        type: 'entity:node'

 

The definition of the tab you need to add to local_tasks.links.task.yml

# In this example we add an additional tab to the node page
local_tasks.node_extra_settings: # The ID of local task is the same as route, to make it easier to control.
  route_name: 'local_tasks.node_extra_settings' # The name of our route, provided in local_tasks.routing.yml.
  title: 'Extra settings' # This string will be displayed as a title of a tab.
  base_route: 'entity.node.canonical' # We use node entity in order to group tabs together.
  weight: 100

Now, you need install the module local_tasks or to clean the cache if you do it your own module.

Open any node (node/[NID]), you should see a new tab "Extra settings". Also, you can open directly the page node/[NID]/extra-settings

In the next posts, we will review how to add sublevels of tabs, to define the default local task and how to add local tasks dynamically