Create your own custom annotation class
This documentation needs work. See "Help improve this page" in the sidebar.
New plugin types should always use a custom annotation class. This allows for documentation, and a consistent developer experience.
Let's look at an example in the wild, the plaintext field formatter which is located at text/src/Plugin/field/formatter/TextPlainFormatter.php.
It uses its own annotation class, FieldFormatter, which extends \Drupal\Component\Annotation\Plugin:
<?php
namespace Drupal\Core\Field\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a FieldFormatter annotation object.
*
* Additional annotation keys for formatters can be defined in
* hook_field_formatter_info_alter().
*
* @see \Drupal\Core\Field\FormatterPluginManager
* @see \Drupal\Core\Field\FormatterInterface
*
* @ingroup field_formatter
*
* @Annotation
*/
class FieldFormatter extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The human-readable name of the formatter type.
*
* @var \Drupal\Core\Annotation\Translation
* @ingroup plugin_translatable
*/
public $label;
/**
* A short description of the formatter type.
*
* @var \Drupal\Core\Annotation\Translation
* @ingroup plugin_translatable
*/
public $description;
/**
* The name of the field formatter class.
*
* This is not provided manually, it will be added by the discovery mechanism.
*
* @var string
*/
public $class;
/**
* An array of field types the formatter supports.
*
* @var array
*/
public $field_types = [];
/**
* An integer to determine the weight of this formatter.
*
* Optional. This is relative to other formatters in the Field UI
* when selecting a formatter for a given field instance.
*
* @var int
*/
public $weight = NULL;
}You can see that the field_types and weight keys have a default value assigned. In the plugins' annotation, using these keys is optional. See the following example of an annotation using our FieldFormatter class, which does provide the field_types key, but not the weight key.
/**
* Plugin implementation of the 'text_plain' formatter.
*
* @FieldFormatter(
* id = "text_plain",
* label = @Translation("Plain text"),
* field_types = {
* "text",
* "text_long",
* "text_with_summary",
* },
* edit = {
* "editor" = "direct",
* },
* )
*/
class TextPlainFormatter {
Assigning a default value to optional keys allows skipping irrelevant keys, it is particularly important for backwards-compatibility with contributed or custom plugins.
Phase in your own Annotation type
When you initially created your custom module, you might have used Drupal's default @Plugin annotation type in your plugins, although this is not recommended. Now that you want to add additional keys, set defaults or simply document the annotation keys you are using, you discover you can't. You want to switch to your own annotation class @MyPlugin, but can't do so either, at least not immediately, to avoid breaking backwards-compatibility for custom plugins your users might have created.
As long as all your (and your users') plugins reside in the same PSR-4 $subdir, there's however an easy way to allow your Plugin Manager discover plugins using both the original @Plugin type and your own @MyPlugin type annotations. Instead of passing your own $plugin_definition_annotation_name, pass the base class and then add the namespace of your own Annotation type definitions in $additional_annotation_namespaces.
So instead of constructing your Plugin Manager like this:
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct(
'Plugin/mymodule/MyPlugin',
$namespaces,
$module_handler,
'Drupal\mymodule\Plugin\MyPluginInterface',
// Your $plugin_definition_annotation_name:
'Drupal\mymodule\Annotation\MyPlugin'
);make your Plugin Manager act type-agnostic when discovering your plugins:
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct(
'Plugin/mymodule/MyPlugin',
$namespaces,
$module_handler,
'Drupal\mymodule\Plugin\MyPluginInterface',
// Drupal's base $plugin_definition_annotation_name:
'Drupal\Component\Annotation\Plugin',
// Your own $additional_annotation_namespaces:
['Drupal\mymodule\Annotation']
);If you're following best practice and your plugins reside in different subdirectories of your Plugin/$vendor PSR-4 directory (e.g. Plugin/mymodule/one_plugin_type and Plugin/mymodule/another_plugin_type), you can discover all of them by providing the common Plugin/mymodule directory:
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct(
// This will discover plugins in all subdirectories, too:
'Plugin/mymodule',
$namespaces,
$module_handler,
'Drupal\mymodule\Plugin\MyPluginInterface',
// Drupal's base $plugin_definition_annotation_name:
'Drupal\Component\Annotation\Plugin',
// Your own $additional_annotation_namespaces:
['Drupal\mymodule\Annotation']
);If the plugins however don't have a Plugin/$vendor directory in common, there is no easy way to discover all of them.
Extending an existing annotation class
When extending an existing plugin's annotation class, it's important to inject the new class into the plugin's plugin manager.
As an example of overriding the graphql module's DataProducer annotation, in my_module.services.yml supply the new annotation class:
plugin.manager.graphql.data_producer:
class: Drupal\graphql\Plugin\DataProducerPluginManager #Use the same class as the base module.
arguments:
- 'Plugin/GraphQL/DataProducer'
- '@container.namespaces'
- '@module_handler'
- '@cache.graphql.definitions'
- '@request_stack'
- '@cache_contexts_manager'
- '@cache.graphql.results'
- '\Drupal\graphql\Plugin\DataProducerPluginInterface'
- '\Drupal\my_module\Annotation\DataProducer' # Our custom DataProducer annotation class.
- '%graphql.config%'
Here we are simply passing our own annotation class into the plugin manager for graphql DataProducer plugins.
Help improve this page
You can:
- Log in, click Edit, and edit this page
- Log in, click Discuss, update the Page status value, and suggest an improvement
- Log in and create a Documentation issue with your suggestion
Still on Drupal 7? Security support for Drupal 7 ended on 5 January 2025. Please visit our Drupal 7 End of Life resources page to review all of your options.