Often we write and contribute module, but have you ever thought or considered how the module features can be extended? In Drupal 8, we can do so by using Plugin Manager that make our modules extendable. For this, first, you need to know what is Plugin, Plugin Type and how it works. Have a look.
So what is Plugin?
In short, Plugin is small pieces of swappable functionality.
What is Plugin Type?
Plugin type is categorization or grouping of Plugins, which perform similar functionality. Drupal 8 Plugin system has three base elements:
- Plugin Types
The central controlling class that defines the ways plugins of this type will be discovered, instantiated and purpose of all the plugins of that type. For example blocks, field formatter, field widget, etc.
- Plugin Discovery
It is a process of finding plugins which are of particular plugin types. There are four different core discovery types:
- StaticDiscovery
It allows for static registration of plugins within the discovery class. A protected variable ($definitions) in the discovery class holds all plugin definitions that are registered with it through the public method setDefinition().
- HookDiscovery
Drupal's hook_component_info() or hook_component_info_alter() can be used for plugin discovery. With this discovery, the plugin manager will invoke info hooks to retrieve a list of available plugins.
- AnnotatedClassDiscovery
Uses name of the annotations that contain the plugin definition such as @Plugin, @EntityType, in plugin docblocks to discover plugins.
- YamlDiscovery
YamlDiscovery allows plugins to be defined in yaml files. For example action.links.menu.yml & action.links.task.yml
- StaticDiscovery
- Plugin Factory
The Factory is responsible for instantiating the specific plugin(s) chosen for a given use case.
When to use Plugin or Service?
Plugin
We need Plugins to implement different behaviors via a common interface that means plugins will act in the same way on the same data but output varies. Use the plugin system if you need to expose a UI through which people can configure or select the implementation they want.
Service
Services provide the same functionality and are replaceable but internal implementation will differ.
Plugins vs. Hooks
Plugins are just object-oriented replacement for hooks. Plugins made possible to swap core or contrib module code or plugin.
Creating your own Plugin Manager
For better understanding, I will explain this section based on one of my contributed module dynamictagclouds.
Dynamictagclouds module basically provides tag cloud block, which displays all taxonomy vocabulary tags configured in block configuration. This modules goal is to provide end users an option to select what kind or style of tag cloud they need. Also, this style should be extendable that means other modules should be able to provide their own style for the tag cloud if needed.
Using following Drupal console commands, we can create scaffolding code for Plugin Manager.
- generate:plugin:type:annotation (gpta)
- generate:plugin:type:yaml (gpty)
Here I will walk you through generate:plugin:type:annotation. Leaving the other one for you. Executing the above Drupal console command by giving Plugin type class name as TagCloud, the following files will be created:
- dynamictagclouds/src/Annotation/TagCloud.php:
Defines Example item annotation object. Whenever Plugin of this type is been initiated this annotation object will be the plugin definition.
- dynamictagclouds/src/Plugin/TagCloudBase.php:
Base class for Example plugins. All plugins of this type should extend this base class. Add common methods and abstract methods for this plugin type here.
- dynamictagclouds/src/Plugin/TagCloudInterface.php:
Defines an interface for Example plugins. Add get/set methods for your plugin type here.
- dynamictagclouds/src/Plugin/TagCloudManager.php:
Provides the Example plugin manager. It's recommended to extend DefaultPluginManager, which defines annotation plugin discovery and defines plugin factory for instantiation.
- dynamictagclouds/dynamictagclouds.services.yml:
Plugin managers should be defined as services. It is considered best practice to prefix the service name with `plugin.manager`.
Using Plugin and Plugin type to extend module
- Create default tag cloud style as a plugin by executing the Drupal console command generate:plugin:skeleton (gps) provide Plugin Id as tag_cloud, which will create a Plugin class file for the specified plugin type. The plugin class will be something like below:
- Change the following things:
- Extend the DefaultTagCloud class from TagCloudBase instead of implementing TagCloudInterface. Where we can define common methods and properties for the TagCloud plugins.
- Implement your plugin logic in TagCloudBase build() method and call parent::build() in DefaultTagCloud.
- Optional, if needed define additional annotation parameters. For TagCloud, had defined annotation parameters libraries and template.
After all the above changes, TagCloudBase.php and DefaultTagCloud.php will be like:
- Expose or provide this default tag cloud style plugin in block configuration form. Below is the code to get all the available plugins of specific plugin type
- After this any module can create their own tag cloud style plugin and this will be available in block configuration form.
- Below code will render the user configured style of tag cloud in the block.
Create tag cloud style plugin in a custom module
- In your custom module, create a new plugin for TagCloud which inherits TagCloudBase class. Or copy paste DefaultTagCloud.php to your custom module and rename filename, namespace, and class.
- Change the following in plugin annotation:
- id - Plugin Id, this should be unique.
- label - Plugin style label.
- libraries - List of libraries name defined in your module libraries.yml file for your custom tag cloud style.
- template - Tag cloud twig template details:
- type - template provider module/theme. In your case, it would be 'module'.
- name - Module/Theme which defines the template. In your case, it would be your module name.
- directory - Directory path where twig template resides.
- file - Twig template name excluding '.html.twig'.
- Implement your logic in build() method.
Set newly created tag cloud style in tag cloud block configuration and you are done !!!
If you want to check the full working of this code, you can download dynamictagclouds module version >= 8.x-2.0. Hope this blog will be help you in extending module using Plugin Manager. Please comment below and let us know your thoughts on the same.