1

Since the Drupal sniffer whines at you if you call \Drupal::service() in your code, I have been trying to figure out how to use dependency injection on a widget I’ve been working on. Here’s what I’ve currently arrived at:

<?php

use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\xois\SicsService;
use Symfony\Component\DependencyInjection\ContainerInterface;

class XoisSicSelect extends WidgetBase {

  /**
  * SICs service.
  *
  * @var \Drupal\xois\SicsService
  */
  protected $sicsService;

  /**
  * {@inheritdoc}
  */
  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, SicsService $sicsService) {
    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
    $this->sicsService = $sicsService;
  }

  /**
  * {@inheritdoc}
  */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('xois.sics')
    );
  }

}

Pretty much the standard DI pattern, no? Only difference is that WidgetBase takes a whole load of construct parameters already.
Problem is, it doesn’t work. When run, it gives this delightful error:

The website encountered an unexpected error. Please try again later.
Recoverable fatal error: Argument 6 passed to Drupal\xois\Plugin\Field\FieldWidget\XoisSicSelect::__construct() must be an instance of Drupal\xois\SicsService, none given, called in /var/www/docroot/core/lib/Drupal/Core/Field/WidgetPluginManager.php on line 125 and defined in Drupal\xois\Plugin\Field\FieldWidget\XoisSicSelect->__construct() (line 35 of modules/xois/src/Plugin/Field/FieldWidget/XoisSicSelect.php). 

Anyone got an idea on how to accomplish this?

4

You will need to implement ContainerFactoryPluginInterface in order to use dependency injection in your class.

Include use Drupal\Core\Plugin\ContainerFactoryPluginInterface; and implement the interface so it looks like:

class XoisSicSelect extends WidgetBase implements ContainerFactoryPluginInterface {

    ....

}

You will also need to make sure your __construct() and create() methods contain the correct parameters.

If you look at ContainerFactoryPluginInterface.php, you will see the parameters the create() method takes in:

public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition);

Thus, your final code should look like:

use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\xois\SicsService;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;

class XoisSicSelect extends WidgetBase implements ContainerFactoryPluginInterface {
  /**
   * SICs service.
   *
   * @var \Drupal\xois\SicsService
   */
  protected $sicsService;

  /**
   * {@inheritdoc}
   */
  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, SicsService $sicsService) {
    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
    $this->sicsService = $sicsService;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $plugin_id,
      $plugin_definition,
      $configuration['field_definition'],
      $configuration['settings'],
      $configuration['third_party_settings'],
      $container->get('xois.sics')
    );
  }
}
0
1

This was a difficult one for myself as well. How I figured out to resolve it was to go to the documentation page for the class (ex WidgetBase), and look for the __construct() method (ex WidgetBase::__construct()). Click on that, and you'll see a section for "X methods override CLASSNAME::__construct()". For example, DateRangeDefaultWidget::__construct overrides WidgetBase::__construct(). Click on the override, and you'll an example of what the __construct() override should look like:

public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityStorageInterface $date_storage) {
  parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);

  $this->dateStorage = $date_storage;
}

And if you look at the class page (ex DateRangeDefaultWidget), you can find what the create() method should look like: DateRangeDefaultWidget::create):

public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
  return new static(
  $plugin_id, 
  $plugin_definition, 
  $configuration['field_definition'], 
  $configuration['settings'], 
  $configuration['third_party_settings'], 
  $container->get('entity_type.manager')->getStorage('date_format')
  );
}

So it takes a bit of detective work, but once you figure out the method of detecting, it's actually fairly straightforward for any classes.

3
  • Your IDE/code editor should be able to help with this. For example, when a method is being overridden Netbeans looks up the parent and displays a clickable marker next to the line number. Clicking on the marker will open the file with the parent method and take you to it.
    – imclean
    Apr 11 '17 at 0:05
  • Depends on the editor. Mine doesn't do this.
    – Jaypan
    Apr 11 '17 at 0:21
  • It's a handy feature. Obviously the whole Drupal codebase needs to be part of the same project for this to work.
    – imclean
    Apr 11 '17 at 6:32
1

You need to copy the create() method and add your argument, not replace it.

There's no magic involved, it's just a plain static PHP method that contructs an object, including all its arguments.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.