0

I have created an ajax callback inside a custom Field Widget plugin. In the Ajax callback I have called a custom service using

$service = \Drupal::service('service.name')

I am using phpmd to clean up the code and it suggested that the service should be called using dependency injection.

This means I should create a constructor for the Fieldwidget class and then get it as argument. I am not sure how to pass the argument when the Field widget is called. Not even sure what interface should be set for the Drupal service caller.

Any ideas on how to do that.

3

This requires three steps:

  1. Declare a property to hold the service.
  2. Implement a __construct() method to populate the property from step 1.
  3. Implement a create() method, to generate the service that is passed to __construct().

1) Declare a property to hold the service

In your class, declare the property that will hold the service.

/**
 * The example service.
 *
 * @var \Drupal\example\Service\ExampleService
 */
protected $exampleService;

With this, you can now refer to $this->exampleService within your class. However, at this point it is still NULL, so it needs to be populated.

2) Implement a __construct() method to populate the property from step 1

This is done by overriding WidgetBase::__construct(), and adding the service to it.

public function __construct(
  $plugin_id,
  $plugin_definition,
  FieldDefinitionInterface $field_definition,
  array $settings,
  array $third_party_settings,
  ExampleServiceInterface $exampleService
) {
  // Initialize the parent class:
  parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);

  $this->exampleService = $exampleService;
}

Now the property from step one can be populated when the class instantiated. However, the service itself needs to be generated, and passed to the __construct() method.

3) Implement a create() method, to generate the service that is passed to __construct().

The service is generated in the create() method. This is done by copying the code from WidgetBase::create(), and adding the service to it. WidgetBase::create() looks like this:

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']
  );
}

Notice that the values returned are the same values passed to the __construct() method, but without the example.service service. The example service needs to be retrieved from $container and added to the above, by implemented create() in your class as follows:

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('example.service')
  );
}

This function call generates the example service, which passes it to __construct(), which in turn populates $this->exampleService, so it can be used in your class in place of \Drupal::service('example.service').

| improve this answer | |
  • Wow!! Nicely explained. I was able to understand a lot from this. Just one question. My service is not implemented from an interface, it's just a class by itself. The construct function requires ExampleService Interface which I have not declared. Do I need to declare it somewhere? – Binny May 8 at 17:19
  • Yes, you'll need to declare it in [MODULE].services.yml (drupal.org/docs/8/api/services-and-dependency-injection/…). The key you use to declare your service will be the one you use to retrieve the service in the create() method. – Jaypan May 8 at 18:36
  • Nice explanation. Let me also add that you have to add implements ContainerFactoryPluginInterface to your field widget class declaration, and you will need at least two new "use" statements: use Drupal\Core\Plugin\ContainerFactoryPluginInterface; and use Symfony\Component\DependencyInjection\ContainerInterface; – anonymous May 8 at 19:31
  • 1
    WidgetBase implements ContainerFactoryPluginInterface, so this implementation does not need to be added. – Jaypan May 8 at 19:33
  • The previous comment is true. Don't get confused from lines like class FileWidget extends WidgetBase implements ContainerFactoryPluginInterface found in Drupal core. It's redundant with abstract class WidgetBase extends PluginSettingsBase implements WidgetInterface, ContainerFactoryPluginInterface. – kiamlaluno May 8 at 20:26

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.