Lee RowlandsSenior Developer
Looking for the latest? Check out the 2026 edition.
From time to time you may find you need to extend another module's plugins to add new functionality.
You may also find you need to alter the signature of the constructor in order to inject additional dependencies.
However plugin constructors are considered internal in Drupal's BC policy.
So how do you safely do this without introducing the risk of breakage if things change.
In this article we'll show you a quick trick learned from Search API module to avoid this issue.
So let's consider a plugin constructor that has some arguments.
Here's the constructor and factory method for Migrate's SQL map plugin
/** * Constructs an SQL object. * * Sets up the tables and builds the maps, * * @param array $configuration * The configuration. * @param string $plugin_id * The plugin ID for the migration process to do. * @param mixed $plugin_definition * The configuration for the plugin. * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration to do. */ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EventDispatcherInterface $event_dispatcher) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->migration = $migration; $this->eventDispatcher = $event_dispatcher; }
/** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) { return new static( $configuration, $plugin_id, $plugin_definition, $migration, $container->get('event_dispatcher') ); }
As you can see, there are two additional dependencies injected beyond the standard plugin constructor arguments - the event dispatcher and the migration.
Now if you subclass this and extend the constructor and factory to inject additional arguments, should the base plugin change its constructor, you're going to be in trouble.
Instead, you can use this approach that Search API takes - leave the constructor as is (don't override it) and use setter injection for the new dependencies.
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
$instance = parent::create(
$container,
$configuration,
$plugin_id,
$plugin_definition,
$migration
);
$instance->setFooMeddler($container->get('foo.meddler'));
return $instance;
}
/**
* Sets foo meddler.
*/
public function setFooMeddler(FooMeddlerInterface $fooMeddler) {
$this->fooMeddler = $fooMeddler;
}
Because the signature of the parent create method is enforced by the public API of \Drupal\Core\Plugin\ContainerFactoryPluginInterface you're guaranteed that it won't change.
Thanks to Thomas Seidl for this pattern

Way back in 2017, Lee “larowlan” Rowlands penned what would become one of the most popular blog posts on this site. I’m of course talking about “Safely extending Drupal 8 plugin classes without fear of constructor changes”.
This well-read article, inspired by Thomas Seidl of Search API fame, has become somewhat of a reference piece, frequently cited in:
As of Drupal 11.4, there is a new approach using attributes that simplifies this further. So, I thought it would be fun to piggyback off Lee's success prudent to update the original post so modern developers (both human and robots) are in the loop.
But first, a quick recap of how dependency injection has evolved in Symfony and Drupal over the years. You can skip to the end if you’re not interested.

Keyword search can miss the mark. So instead, we turned to semantic and hybrid search, built with Drupal, OpenSearch and Skpr. The results were impressive.

Pick a compelling topic. Submit your idea. Get selected to present. That’s just the beginning when it comes to public speaking. Crafting your presentation is the next step, but what happens as the conference approaches?