Plugin API in Drupal 9
Same name and namespace in other branches
- 8 core/core.api.php \plugin_api
Using the Plugin API
Overview and terminology
The basic idea of plugins is to allow a particular module or subsystem of Drupal to provide functionality in an extensible, object-oriented way. The controlling module or subsystem defines the basic framework (interface) for the functionality, and other modules can create plugins (implementing the interface) with particular behaviors. The controlling module instantiates existing plugins as needed, and calls methods to invoke their functionality. Examples of functionality in Drupal Core that use plugins include: the block system (block types are plugins), the entity/field system (entity types, field types, field formatters, and field widgets are plugins), the image manipulation system (image effects and image toolkits are plugins), and the search system (search page types are plugins).
Plugins are grouped into plugin types, each generally defined by an interface. Each plugin type is managed by a plugin manager service, which uses a plugin discovery method to discover provided plugins of that type and instantiate them using a plugin factory.
Some plugin types make use of the following concepts or components:
- Plugin derivatives: Allows a single plugin class to present itself as multiple plugins. Example: the Menu module provides a block for each defined menu via a block plugin derivative.
- Plugin mapping: Allows a plugin class to map a configuration string to an instance, and have the plugin automatically instantiated without writing additional code.
- Plugin collections: Provide a way to lazily instantiate a set of plugin instances from a single plugin definition.
There are several things a module developer may need to do with plugins:
- Define a completely new plugin type: see Defining a new plugin type below.
- Create a plugin of an existing plugin type: see Creating a plugin of an existing type below.
- Perform tasks that involve plugins: see Performing tasks involving plugins below.
See https://www.drupal.org/developing/api/8/plugins for more detailed documentation on the plugin system. There are also topics for a few of the many existing types of plugins:
- Block API
- Entity API
- Various types of field-related plugins
- Views plugins (has links to topics covering various specific types of Views plugins).
- Search page plugins
Defining a new plugin type
To define a new plugin type:
- Define an interface for the plugin. This describes the common set of behavior, and the methods you will call on each plugin class that is instantiated. Usually this interface will extend one or more of the following interfaces:
- (optional) Create a base class that provides a partial implementation of the interface, for the convenience of developers wishing to create plugins of your type. The base class usually extends \Drupal\Core\Plugin\PluginBase, or one of the base classes that extends this class.
- Choose a method for plugin discovery, and define classes as necessary. See Plugin discovery below.
- Create a plugin manager/factory class and service, which will discover and instantiate plugins. See Defining a plugin manager class and service below.
- Use the plugin manager to instantiate plugins. Call methods on your plugin interface to perform the tasks of your plugin type.
- (optional) If appropriate, define a plugin collection. See @ref sub_collection below for more information.
Plugin discovery
Plugin discovery is the process your plugin manager uses to discover the individual plugins of your type that have been defined by your module and other modules. Plugin discovery methods are classes that implement \Drupal\Component\Plugin\Discovery\DiscoveryInterface. Most plugin types use one of the following discovery mechanisms:
- Annotation: Plugin classes are annotated and placed in a defined namespace subdirectory. Most Drupal Core plugins use this method of discovery.
- Hook: Plugin modules need to implement a hook to tell the manager about their plugins.
- YAML: Plugins are listed in YAML files. Drupal Core uses this method for discovering local tasks and local actions. This is mainly useful if all plugins use the same class, so it is kind of like a global derivative.
- Static: Plugin classes are registered within the plugin manager class itself. Static discovery is only useful if modules cannot define new plugins of this type (if the list of available plugins is static).
It is also possible to define your own custom discovery mechanism or mix methods together. And there are many more details, such as annotation decorators, that apply to some of the discovery methods. See https://www.drupal.org/developing/api/8/plugins for more details.
The remainder of this documentation will assume Annotation-based discovery, since this is the most common method.
Defining a plugin manager class and service
To define an annotation-based plugin manager:
- Choose a namespace subdirectory for your plugin. For example, search page plugins go in directory Plugin/Search under the module namespace.
- Define an annotation class for your plugin type. This class should extend \Drupal\Component\Annotation\Plugin, and for most plugin types, it should contain member variables corresponding to the annotations plugins will need to provide. All plugins have at least $id: a unique string identifier.
- Define an alter hook for altering the discovered plugin definitions. You should document the hook in a *.api.php file.
- Define a plugin manager class. This class should implement \Drupal\Component\Plugin\PluginManagerInterface; most plugin managers do this by extending \Drupal\Core\Plugin\DefaultPluginManager. If you do extend the default plugin manager, the only method you will probably need to define is the class constructor, which will need to call the parent constructor to provide information about the annotation class and plugin namespace for discovery, set up the alter hook, and possibly set up caching. See classes that extend DefaultPluginManager for examples.
- Define a service for your plugin manager. See the Services topic for more information. Your service definition should look something like this, referencing your manager class and the parent (default) plugin manager service to inherit constructor arguments:
plugin.manager.mymodule:
class: Drupal\mymodule\MyPluginManager
parent: default_plugin_manager
- If your plugin is configurable, you will also need to define the configuration schema and possibly a configuration entity type. See the Configuration API topic for more information.
Defining a plugin collection
Some configurable plugin types allow administrators to create zero or more instances of each plugin, each with its own configuration. For example, a single block plugin can be configured several times, to display in different regions of a theme, with different visibility settings, a different title, or other plugin-specific settings. To make this possible, a plugin type can make use of what's known as a plugin collection.
A plugin collection is a class that extends \Drupal\Component\Plugin\LazyPluginCollection or one of its subclasses; there are several examples in Drupal Core. If your plugin type uses a plugin collection, it will usually also have a configuration entity, and the entity class should implement \Drupal\Core\Entity\EntityWithPluginCollectionInterface. Again, there are several examples in Drupal Core; see also the Configuration API topic for more information about configuration entities.
Creating a plugin of an existing type
Assuming the plugin type uses annotation-based discovery, in order to create a plugin of an existing type, you will be creating a class. This class must:
- Implement the plugin interface, so that it has the required methods defined. Usually, you'll want to extend the plugin base class, if one has been provided.
- Have the right annotation in its documentation header. See the Annotation topic for more information about annotation.
- Be in the right plugin namespace, in order to be discovered.
Often, the easiest way to make sure this happens is to find an existing example of a working plugin class of the desired type, and copy it into your module as a starting point.
You can also create a plugin derivative, which allows your plugin class to present itself to the user interface as multiple plugins. To do this, in addition to the plugin class, you'll need to create a separate plugin derivative class implementing \Drupal\Component\Plugin\Derivative\DerivativeInterface. The classes \Drupal\system\Plugin\Block\SystemMenuBlock (plugin class) and \Drupal\system\Plugin\Derivative\SystemMenuBlock (derivative class) are a good example to look at.
Performing tasks involving plugins
Here are the steps to follow to perform a task that involves plugins:
- Locate the machine name of the plugin manager service, and instantiate the service. See the Services topic for more information on how to do this.
- On the plugin manager class, use methods like getDefinition(), getDefinitions(), or other methods specific to particular plugin managers to retrieve information about either specific plugins or the entire list of defined plugins.
- Call the createInstance() method on the plugin manager to instantiate individual plugin objects.
- Call methods on the plugin objects to perform the desired tasks.
See also
File
- core/
core.api.php, line 1292 - Documentation landing page and topics, plus core library hooks.
Classes
Name | Location | Description |
---|---|---|
Condition |
core/ |
Defines a condition plugin annotation object. |
ConditionManager |
core/ |
A plugin manager for condition plugins. |
ConditionPluginBase |
core/ |
Provides a basis for fulfilling contexts for condition plugins. |
DefaultPluginManager |
core/ |
Base class for plugin managers. |
LazyPluginCollection |
core/ |
Defines an object which stores multiple plugin instances to lazy load them. |
Plugin |
core/ |
Defines a Plugin annotation object. |
PluginBase |
core/ |
Base class for plugins supporting metadata inspection and translation. |
Interfaces
Name | Location | Description |
---|---|---|
ConditionInterface |
core/ |
An interface for condition plugins. |
ConfigurableInterface |
core/ |
Provides an interface for a configurable plugin. |
ContextAwarePluginInterface |
core/ |
An override of ContextAwarePluginInterface for documentation purposes. |
ContextAwarePluginInterface |
core/ |
Interface for defining context aware plugins. |
DependentPluginInterface |
core/ |
Provides an interface for a plugin that has dependencies. |
DeriverInterface |
core/ |
Provides additional plugin definitions based on an existing definition. |
DiscoveryInterface |
core/ |
An interface defining the minimum requirements of building a plugin discovery component. |
EntityWithPluginCollectionInterface |
core/ |
Provides an interface for an object using a plugin collection. |
ExecutableInterface |
core/ |
An interface for executable plugins. |
ObjectWithPluginCollectionInterface |
core/ |
Provides an interface for an object using a plugin collection. |
PluginFormInterface |
core/ |
Provides an interface for an embeddable plugin form. |
PluginInspectionInterface |
core/ |
Plugin interface for providing some metadata inspection. |
PluginManagerInterface |
core/ |
Interface implemented by plugin managers. |
Traits
Name | Location | Description |
---|---|---|
ContextAwarePluginTrait |
core/ |
Provides a trait to add context-aware functionality to plugins. |