Plugin Reuse Patterns
When to Use
When you need extensible, discoverable, swappable components (blocks, field types, filters, conditions, actions), or when multiple plugins share common patterns.
Plugin Inheritance
| Pattern | Use When | Example |
|---|---|---|
| Extend core plugin base | Creating new plugin of existing type | MyBlock extends BlockBase |
| Extend another plugin | Creating variation of existing plugin | MyMenuBlock extends SystemMenuBlock |
| Create shared base for project | Multiple similar custom plugins | MyProjectBlockBase extends BlockBase |
| Use plugin derivatives | Generating multiple plugins from one class | Menu links, contextual links, local tasks |
Decision
| If you need... | Use... | Why |
|---|---|---|
| Single new block | Extend BlockBase |
Inherit caching, context, config scaffolding |
| Variation of core block | Extend specific core block class | Reuse logic, override specific methods |
| 5+ similar blocks with shared logic | Create MyProjectBlockBase extends BlockBase |
Eliminate duplication across project blocks |
| Dynamic plugins based on config | Plugin deriver | One class generates multiple plugin instances |
| Shared logic across plugin types | Service injected into plugins | Avoid duplicating logic in each plugin |
Pattern
// Plugin extending core base
namespace Drupal\mymodule\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
#[Block(
id: "my_custom_block",
admin_label: new TranslatableMarkup("My Custom Block"),
)]
class MyCustomBlock extends BlockBase implements ContainerFactoryPluginInterface {
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
protected MyService $myService,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('mymodule.my_service'),
);
}
public function build() {
return ['#markup' => $this->myService->getData()];
}
}
Plugin Derivatives Pattern
// Deriver generates multiple plugins from one class
namespace Drupal\mymodule\Plugin\Deriver;
use Drupal\Component\Plugin\Derivative\DeriverBase;
class MyBlockDeriver extends DeriverBase {
public function getDerivativeDefinitions($base_plugin_definition) {
$config_entities = \Drupal::entityTypeManager()
->getStorage('my_entity_type')
->loadMultiple();
foreach ($config_entities as $id => $entity) {
$this->derivatives[$id] = $base_plugin_definition;
$this->derivatives[$id]['admin_label'] = $entity->label();
}
return $this->derivatives;
}
}
Reference: core/lib/Drupal/Core/Block/BlockBase.php, core/lib/Drupal/Core/Plugin/PluginBase.php, core/modules/system/src/Plugin/Deriver/SystemMenuBlock.php
Common Mistakes
- Not calling parent::__construct() — Always call parent constructor in plugins to maintain plugin definition
- Overriding create() without matching constructor — Constructor parameters must match
create()factory method - Duplicating logic across plugins — Extract shared logic to injected service, not into each plugin
- Using derivatives when simple plugin would work — Only use derivatives for truly dynamic plugin generation
- Forgetting ContainerFactoryPluginInterface — Required for dependency injection in plugins