5

Is it possible to let a plugin depend on a module (other than the declaring one)?

Use case: I want to create a field widget plugin that should depend on the 'date_all_day' contrib module, but I don't want the entire declaring module depend on this.

Is it possible to declare this in the annotation?

E.g.

/**
 * @FieldWidget(
 *   id = "my_daterange",
 *   label = @Translation("Date and time range + All day"),
 *   field_types = {"daterange"},
 *   dependencies = {"date_all_day"}
 * )
 */
class MyPlugin {...}

The plugin should only become available if both the declaring module and the dependency module are enabled.

2 Answers 2

7

Yes, this is possible.

You may declare a dependency on a module by using the "provider" key in the annotation. For your example, this would look like:

/**
 * @FieldWidget(
 *   id = "my_daterange",
 *   label = @Translation("Date and time range + All day"),
 *   field_types = {
 *     "daterange"
 *   },
 *   provider = "date_all_day"
 * )
 */
class MyPlugin {...}

Note that you can only list ONE provider, but of course that provider module can depend on other modules ...

This is a feature added to the DefaultPluginManager in order to allow plugins to declare a dependency on one (and only one) module. This only works for plugins that use a plugin manager derived from DefaultPluginManager.

Specifically, @FieldWidget plugins are managed by the WidgetPluginManager, which DOES subclass DefaultPluginManager, so this will work. (Most, if not all, core plugin managers subclass DefaultPluginManager.)

7

First, it's probably preferable to use the provider option if you can.

However, Field widgets and formatters also make use of an isApplicable() method. You can use this for any additional logic to determine whether your widget should be available for a field, a field in the subset of fields defined in your annotation.

You can use this method to limit the widget to just a datetime field with a particular machine name, or only datetime fields on nodes, etc.

You can also use it for your case, to check if other modules are enabled.

4
  • Nice! If I could I would accept both answers because this is an essential part of the picture.
    – donquixote
    Dec 23, 2020 at 15:36
  • In practice, checking on moduleExists() isn't very useful for plugins because it can only be called after the plugin is discovered and made available as a selection in the UI. If the plugin depends on another module through inheritance or implementation, or by injecting a service provided by that other module, etc, then moduleExists() won't help. You have to prevent the plugin from being discovered in the first place or the code will fail. Even in the best case, a moduleExists() check will still allow your plugin to be selected and used - it will just prevent the plugin code from functioning.
    – anonymous
    Dec 23, 2020 at 19:59
  • Obviously, it's preferable to use the provider option if you can. While you are correct that, with this approach,you cannot use dependency injection to inject services from dependencies you're checking in isApplicable(), you could just access them from the service container via Drupal::service('etc'). Additionally, if a field formatter or field widget's isApplicable() method returns false then the widget can't be selected in the interface - it would stop people from selecting it.
    – sonfd
    Dec 29, 2020 at 20:22
  • As a possible alternative, is there an alter hook that fires in the discovery phase?
    – donquixote
    Dec 30, 2020 at 0:03

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.