Do you want to know how to Drupal?

Let's Drupal

Plugin

How to use dependency injection for Plugins in Drupal 9?

Services are very powerful way to make our code reusable. How can we pass service to our plugin in Drupal 9?

For this example we take code of simple plugin EntityLink. It doesn't actually it could be any plugin

use Drupal\views\Plugin\views\field\EntityLink;
use Drupal\views\ResultRow;

/**
 * Field handler to present a link to approval.
 *
 * @ingroup views_field_handlers
 *
 * @ViewsField("approve_link")
 */
class ApproveLInk extends EntityLink {

  /**
   * {@inheritdoc}
   */
  protected function getEntityLinkTemplate() {
    return 'approve';
  }

  /**
   * {@inheritdoc}
   */
  protected function renderLink(ResultRow $row) {
    $this->options['alter']['query'] = $this->getDestinationArray();
    return parent::renderLink($row);
  }

  /**
   * {@inheritdoc}
   */
  protected function getDefaultLabel() {
    return $this->t('Approve');
  }

}

We don't call constructor for our plugin. In order to pass our service. Let's take current_user service.

First we need implement 

class ApproveLInk extends EntityLink implements ContainerFactoryPluginInterface {

Add create method, where we pass our service

/**
   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
   * @param array $configuration
   * @param string $plugin_id
   * @param mixed $plugin_definition
   *
   * @return static
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('current_user')
    );
  }

After that add parameters to our constructor

/**
   * @var AccountInterface $account
   */
  protected $account;

  /**
   * @param array $configuration
   * @param string $plugin_id
   * @param mixed $plugin_definition
   * @param \Drupal\Core\Session\AccountInterface $account
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, AccountInterface $account) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->account = $account;
  }

 


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.