Image
Two plugs in a white electrical outlet, one black and one white.

drush generate plugin:manager walk-through

When creating a custom Drupal plugin manager the "drush generate plugin:manager" command is helpful in creating correctly structured boilerplate code for you.

This continues from last week's article, How to create a custom Drupal plugin. I recommend reading that first to understand the purpose of the files that will be generated. I will also be making reference back to the example module and files in those code snippets.

Setup

Obviously you will need to have Drush installed on your Drupal website to use this command, in most cases all you need is to run composer require drush/drush but otherwise I suggest reading Installing and Upgrading on drush.org.

Then simply run drush generate plugin:manager

The plugin-manager generator will then ask a number of questions to correctly name and locate the necessary boilerplate files additions and updates.

Module Machine Name

$ drush generate plugin:manager

 Welcome to plugin-manager generator!
––––––––––––––––––––––––––––––––––––––

 Module machine name:
  

As you can see above, the first question asks for the machine name of the module within which the plugin manager will be defined. The module must already exist and be enabled before you run the command, so perhaps look into using the "drush generate module" command if you require a new custom module. I have written a walk-through article for this command and cheat-sheet-style details can also be found in my new Reference section.

Note that this is the machine name of the module which might be different from the human-readable name. For example, if you were adding a plugin manager to the "Chaos Tools" module, the machine name would be "ctools". The generate command helpfully attempts to autocomplete the machine name so you may only need to type a few characters before pressing enter. In the code snippets of the previous article, the module machine name was "my_module".

Plugin Type

 Module machine name:
 ➤ my_module

 Plugin type [my_module]:
 ➤ 

Next you need to provide a name for the plugin type you are creating. The generator command offers the module name as a default that can be accepted by simply pressing Enter. Sometimes the module name is a suitable name for the plugin type (e.g. the Token Modifier module), but other times you may wish to change the name, especially if the module is likely to provide multiple plugin types.

Bear in mind that the name of the plugin type will be used in various file names. To match the code snippets of the previous article I will call my plugin type "my_plugin". This results in files being generated with names including:

  • MyPluginInterface.php
  • MyPluginPluginBase.php
  • MyPluginPluginManager

(with hindsight, perhaps it is better to not include the word "plugin" in the name of the plugin type)

Discovery Type

Module machine name:
 ➤ my_module

 Plugin type [my_module]:
 ➤ my_plugin

 Discovery type [Annotation]:
  [1] Annotation
  [2] Attribute
  [3] YAML
  [4] Hook

The third and final question is what type of discovery to use. Choose [2] Attribute, unless you have good reason not to, because since Drupal 10.2.0, plugins should be defined with PHP attributes (see Attribute-based plugins on drupal.org.

Files

Having answered all three questions you are presented with a list of the files that have been created (or updated):

 The following directories and files have been created or updated:
–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
  /web/modules/custom/my_module/my_module.services.yml
  /web/modules/custom/my_module/src/MyPluginInterface.php
  /web/modules/custom/my_module/src/MyPluginPluginBase.php
  /web/modules/custom/my_module/src/MyPluginPluginManager.php
  /web/modules/custom/my_module/src/Attribute/MyPlugin.php
  /web/modules/custom/my_module/src/Plugin/MyPlugin/Foo.php

For an in-depth guide to the files needed for a custom plugin type refer to last week's article, How to create a custom Drupal plugin. Here is a brief rundown including some simple changes you may want to make:

Services

services:
  plugin.manager.my_plugin:
    class: Drupal\my_module\MyPluginPluginManager
    parent: default_plugin_manager

If the .services.yml file for the module does not already exist then it is created. The plugin manager is registered as a service. There is no need to make any changes here for a simple plugin type.

Interface

interface MyPluginInterface {

  public function label(): string;

}

The plugin interface is created including an example method label(). I would recommend removing this method to restrict your plugin code to only necessary methods (unless you happen to need a label() method). It is highly likely that there are methods and properties that all instances of the plugin type should include - these should be defined here.

PluginBase

abstract class MyPluginPluginBase extends PluginBase implements MyPluginInterface {

  public function label(): string {
    return (string) $this->pluginDefinition['label'];
  }

}

The plugin base is created including and example method label(), in the same way as the interface. Remove this method unless you require it. This is where you define functionality that is common across all plugins of this type.

The label() method demonstrates a handy feature for accessing values in the plugin Attribute using $this->pluginDefinition['label'] where 'label' is the name of an attribute parameter.

Plugin Manager

All of the necessary code for the plugin manager is auto-generated; there is no need to make any changes here for a simple plugin type.

Attribute

The attribute file is created including the required $id parameter and a couple of suggested parameters: $label and $description. Do not remove the $id parameter, but the optional parameters can be removed as required. If you wish to include additional required parameters then replicate $id and if you wish to include additional optional parameters then replicate $description.

Example Plugin

#[MyPlugin(
  id: 'foo',
  label: new TranslatableMarkup('Foo'),
  description: new TranslatableMarkup('Foo description.'),
)]
final class Foo extends MyPluginPluginBase {

}

The plugin manager generator also creates an example plugin of this new type. This demonstrates the required file location of any plugins of this type and the required content, particularly the attribute.

It is highly unlikely that you want a plugin called "Foo" so rename this file and update the class name and attribute details accordingly.

Command Options

It is possible to use the command with three "--answer" options, now that we know the three inputs required for the plugin manager generator, as follows:

drush generate plugin:manager \
  --answer=my_module \
  --answer=my_plugin \
  --answer=Attribute

Reference

Details of this command can be found in my new Reference section.

 

Note that the details of this article are based on Drush 13.x (tested on Drush 13.6.2). Be aware that the questions (and their order) could differ in past and future versions of Drush.

Tags

If you have any comments or a particular topic in mind for a future article, or if you'd like to explore contracting possibilities, please get in touch.