Plugins
A plugin is a class that adds one more variant of something to the system: one more block, one more way to output a field, one more text filter. The block from the previous lesson is a plugin. All plugins are built the same way, so once you understand one kind, the others are easy to write.
How Drupal finds plugins
Every kind of plugin has three things:
- a folder inside
src/Pluginwhere the classes live; - an attribute with the description: machine name, label;
- a base class with the methods to implement.
A plugin does not have to be registered in YAML files. On drush cr Drupal scans the src/Plugin folders of all modules and remembers the classes with a fitting attribute.
A field formatter
A formatter decides how a field value is output. Let us write a formatter that shows the reading time instead of the article text. Its folder is src/Plugin/Field/FieldFormatter. Create it and the file by hand:
// web/modules/custom/blog/src/Plugin/Field/FieldFormatter/ReadingTimeFormatter.php
namespace Drupal\blog\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\Attribute\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\StringTranslation\TranslatableMarkup;
#[FieldFormatter(
id: 'blog_reading_time',
label: new TranslatableMarkup('Reading time'),
field_types: ['text_with_summary', 'text_long'],
)]
class ReadingTimeFormatter extends FormatterBase
{
public function viewElements(FieldItemListInterface $items, $langcode): array
{
$elements = [];
foreach ($items as $delta => $item) {
$minutes = \Drupal::service('blog.reading_time')->minutes((string) $item->value);
$elements[$delta] = [
'#markup' => $this->t('Reading time: @min min.', ['@min' => $minutes]),
];
}
return $elements;
}
}The field_types entry of the attribute lists the field types the formatter suits: long text and text with a summary. The viewElements() method receives all values of the field and returns a render array for each. The time is calculated by the blog.reading_time service from the lesson about services. For brevity it is taken statically; in real code it is passed through create(), as in the block.
Using it
Run drush cr and open Structure → Content types → Article → Manage display. In the Body row the Format list now has a "Reading time" option. Choose it for the Teaser view mode and save — in the /blog list a line like "Reading time: 3 min." appears under the article title instead of the text.
Attributes and annotations
Before Drupal 10.2 plugins were described not with attributes but with annotations — special comments above the class:
// web/modules/custom/blog/src/Plugin/Field/FieldFormatter/ReadingTimeFormatter.php
/**
* @FieldFormatter(
* id = "blog_reading_time",
* label = @Translation("Reading time"),
* field_types = {"text_with_summary", "text_long"}
* )
*/
class ReadingTimeFormatter extends FormatterBase
{
}This entry does the same as the attribute above. Older examples and modules use exactly this form. In Drupal 10 and 11 annotations still work, but new code is written with attributes.
Kinds of plugins
| Plugin | Folder in src/Plugin | Purpose |
|---|---|---|
Block | Block | Blocks |
FieldFormatter | Field/FieldFormatter | Output of a field value |
FieldWidget | Field/FieldWidget | The input in the edit form |
FieldType | Field/FieldType | A new field type |
Filter | Filter | A text format filter |
QueueWorker | QueueWorker | A task queue worker |
Condition | Condition | A condition for showing blocks |
Drush creates a skeleton for any of them: drush generate shows the list, for example drush generate plugin:field:formatter.
Common mistake
A plugin is not found when the file path does not match the plugin kind: a formatter put into src/Plugin/FieldFormatter or src/Field/FieldFormatter is invisible to Drupal — it must be exactly src/Plugin/Field/FieldFormatter. The second cause is a duplicate id: two plugins with the same machine name overwrite each other. Start the id with the module name.