Score:-1

Dependency injection in create() method of block plugin with derivatives

us flag

I am trying to extend the menu_block module to allow it to work with domains (via the domain module). In order to do this I am extending the MenuBlock class, and trying to access the domain.negotiator service from within getDerivativeID(). I am using the create() method to inject the service and store it to a $domainNegotiator property as follows:

  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
    $instance->domainNegotiator = $container->get('domain.negotiator');
    return $instance;
  }

When I attempt to call $this->domainNegotiator from within getDerivativeID() I get an error stating the property doesn't exist and I therefore cannot access methods on it. In debugging I was able to trace the issue to MenuBlock::defaultConfiguration() calling $this->getDerivativeID().

Default configuration is set in __construct() inside BlockPluginTrait (called in BlockBase), so ultimately it boils up to being called in my class's create() method.

I am able to work around this issue by using a non-injected version of the service in my getDerivativeID() method, however this isn't a great practice:

$active_domain = \Drupal::service('domain.negotiator')->getActiveDomain();

Is there a way to inject dependencies so they are available before calling parent::create() in my class's create() method?

cn flag
Simplest would probably be to do what core does when it's not sure if a service has been injected, e.g. [`ControllerBase::entityTypeManager()`](https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Controller%21ControllerBase.php/function/ControllerBase%3A%3AentityTypeManager/9.3.x)
Score:3
cn flag

The Drupal plugin system has four levels. Plugin definition, derivatives, configuration and finally the run-time instance.

As you can see in your error message you configure a specific plugin definition or a derivative of the plugin definition. This definition is static and you can't change it after you have configured it.

Only the run-time instance of the plugin can react on dynamic conditions like the active domain.

Score:1
us flag

I was unable to figure out how to extend MenuBlock using only create(). I had to change to a __construct() and create() system, and returned static() from within create() while manually injecting the dependencies from both the MenuBlock and SystemMenuBlock classes. This is a bit less robust in that it will break if the MenuBlock class ever changes dependencies, however it still accomplishes the intended behavior of using an injected dependency within getDerivitiveId():

  public function __construct(array $configuration, $plugin_id, $plugin_definition, MenuLinkTreeInterface $menu_tree, MenuActiveTrailInterface $menu_active_trail, MenuParentFormSelectorInterface $menu_parent_form_selector, EntityTypeManagerInterface $entity_type_manager, DomainNegotiatorInterface $domain_negotiator) {
    $this->domainNegotiator = $domain_negotiator;
    $this->menuParentFormSelector = $menu_parent_form_selector;
    $this->entityTypeManager = $entity_type_manager;
    parent::__construct($configuration, $plugin_id, $plugin_definition, $menu_tree, $menu_active_trail);
  }

  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('menu.link_tree'),
      $container->get('menu.active_trail'),
      $container->get('menu.parent_form_selector'),
      $container->get('entity_type.manager'),
      $container->get('domain.negotiator'));
  }
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.