5

I am implementing custom fields in Drupal 8 (types, widgets, formatters).

@FieldType plugins are instantiated by their respective plugin manager (\Drupal\Core\Field\FieldTypePluginManager) which does not care about dependency injection. That is possible for both @FieldFormatter and @FieldWidget plugins if they only implement \Drupal\Core\Plugin\ContainerFactoryPluginInterface.

What would be the alternative way to inject a dependency into a @FieldType plugin?

Notes

4
  • I don't think it's supported. e.g. FieldItemBase::view() uses $view_builder = \Drupal::entityManager()->getViewBuilder($this->getEntity()->getEntityTypeId()) even though the entity manager is injectable. I wouldn't have thought that's a mistake/oversight
    – Clive
    Commented Dec 30, 2016 at 18:48
  • Right. I was wondering if the injection should happen (calling the \Drupal::service methods) in an overidden createInstance method. Commented Dec 30, 2016 at 18:53
  • It wouldn't really be injection any more if the called class is instantiating its own service objects rather than being provided with them...not trying to nitpick or anything but IoC isn't IoC unless that control stays inverted :) The createInstance method sounds like as good a place as any but I might be wrong
    – Clive
    Commented Dec 30, 2016 at 18:56
  • 2
    Thanks for not nitpicking :). I do agree however regarding IoC, hence came my question in the first place :-) Not having DI options for @FieldType seems odd, especially when writing unit tests. Commented Dec 30, 2016 at 19:12

2 Answers 2

9

An example for a field formatter plugin with dependency injections. This works differently from services. First parameter injected is the container to get all services needed.

Drupal\Core\Field\Plugin\Field\FieldFormatter

class StringFormatter extends FormatterBase implements ContainerFactoryPluginInterface {

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * Constructs a StringFormatter instance.
   *
   * @param string $plugin_id
   *   The plugin_id for the formatter.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
   *   The definition of the field to which the formatter is associated.
   * @param array $settings
   *   The formatter settings.
   * @param string $label
   *   The formatter label display setting.
   * @param string $view_mode
   *   The view mode.
   * @param array $third_party_settings
   *   Any third party settings settings.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   */
  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, EntityTypeManagerInterface $entity_type_manager) {
    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
    $this->entityTypeManager = $entity_type_manager;
  }

  /**
   * {@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['label'],
      $configuration['view_mode'],
      $configuration['third_party_settings'],
      $container->get('entity_type.manager')
    );
  }
15
  • 1
    Thanks @4k4, that is a good example. OP is about FieldTypes not FieldFormatters though. Commented Dec 30, 2016 at 19:08
  • ... implementing custom fields in Drupal 8 (types, widgets, formatters). But it's the same for all plugins.
    – 4uk4
    Commented Dec 30, 2016 at 19:09
  • 3
    Because FieldType plugins are essentially typed data plugins and are instantiated through that and typed data currently doesn't support this, it would need its own interface as it has special constructor arguments. See drupal.org/node/2053415
    – Berdir
    Commented Dec 30, 2016 at 22:17
  • 2
    No, it is not possible
    – Berdir
    Commented Oct 10, 2018 at 19:38
  • 1
    Yes, since my answer the core example changed in the same way, see git.drupalcode.org/project/drupal/commit/…
    – 4uk4
    Commented Nov 6, 2019 at 14:54
0

The accepted answer works for the field widget and field formatter classes. The actual @fieldType definition class constructor requires this signature:

public function __construct(
   \Drupal\Core\TypedData\ComplexDataDefinitionInterface $definition, 
   $name = NULL, 
   \Drupal\Core\TypedData\TypedDataInterface $parent = NULL
)

This works for Drupal 8, 9 and 10. For an example, refer to the FieldItemBase class.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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