Plugin Constructor Factory provides a plugin factory allowing you to inject dependencies using the constructor.

Why?

By default, the only ways to inject dependencies, plugin inspection information and configuration into plugin classes are:

  • by using ContainerFactoryPluginInterface
  • by using the constructor, but only the plugin inspection information and configuration are passed as arguments

This imposes a couple of limitations:

  • Dependencies cannot be injected through the constructor. This makes it impossible to use autowiring, something
    which isn't possible by default in core, but which will inevitably be added in the future.
  • Injecting plugin inspection information and configuration is required, which makes using PluginBase as base
    class practically required.
    In practice, this information is rarely needed. Having it injected should be optional.

How does it work?

By default, this plugin does nothing. It only provides the classes necessary to implement constructor injection in your
plugins.

Configuring plugin managers to use the factory

To make a plugin manager use ConstructorFactory, you need to override the PluginManagerBase::getFactory
method. A trait is provided which does exactly this, PluginInspectionTrait.

Example

<?php

namespace Drupal\your_module;

use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceModifierInterface;
use Drupal\your_module\Plugin\Manager\ControllerPluginManager;

class YourModuleServiceProvider implements ServiceModifierInterface
{
    public function alter(ContainerBuilder $container): void
    {
        if ($container->hasDefinition('plugin.manager.wmcontroller.controller')) {
            $container->getDefinition('plugin.manager.wmcontroller.controller')
                ->setClass(ControllerPluginManager::class);
        }
    }
}
<?php

namespace Drupal\your_module\Plugin\Manager;

use Drupal\plugin_constructor_factory\ConstructorFactoryPluginManagerTrait;
use Drupal\wmcontroller\ControllerPluginManager as ControllerPluginManagerBase;

class ControllerPluginManager extends ControllerPluginManagerBase
{
    use ConstructorFactoryPluginManagerTrait;
}

Defining dependencies for your plugin classes

If you want to inject dependencies, add the plugin class to services.yml, add the service ID to the plugin annotation
and inject your dependencies through the constructor.

<?php

namespace Drupal\your_module\Controller\Node;

/**
 * @Controller(
 *     entity_type = "node",
 *     bundle = "homepage",
 *     service_id = "wmcustom.homepage",
 * )
 */
class HomepageController
{
}

If you have autowiring enabled, make sure there's a resource for the plugin namespace. Since the class name is the service ID, you don't need to add it to the annotation.

Accessing plugin inspection information

If you need access to the $pluginId or $pluginDefinition, your class should implement
PluginInspectionInterface. This is the same as the core
PluginInspectionInterface, but with added setters so that inspection information doesn't have to be injected through
the constructor. A common implementation for this interface is provided as PluginInspectionTrait.

Accessing plugin configuration

If you need access to the $configuration, your class should implement Drupal\Component\Plugin\ConfigurableInterface.
A common implementation for this interface is provided as PluginConfigurationTrait.

Limitations

ConstructorFactory can only be used as a replacement for
ContainerFactory. This means that if a plugin uses a custom factory, this will not work. Plugin managers extending
DefaultPluginManager should be fine.

Contributing

  • Tests are encouraged. This project doesn't have any test coverage yet, but contributions are welcome.
  • Keep the documentation up to date. Make sure README.md and other relevant documentation is kept up to date with your changes.
  • One pull request per feature. Try to keep your changes focused on solving a single problem. This will make it easier for us to review the change and easier for you to make sure you have updated the necessary tests and documentation.
Supporting organizations: 
Development time

Project information

Releases