0

I want to provide a service in my module, but only if another module (say the core File module) is enabled. I don't want to require this module, it should be optional.

Something like this:

services:
  helper.file:
    class: Drupal\helper\File
    arguments: ['@entity_type.manager']
    requirements: ['file']

Is there a way to do this without needing to declare it as a dynamic service? If not, is there an example on how to declare a dynamic service, because https://www.drupal.org/node/2026959 is woefully lacking:

Finally, it is also possible to define the register() method to register services dynamically, however this should be very rare.

2
  • Take a look at How can I check if a module is enabled?
    – No Sssweat
    Commented Sep 12, 2016 at 4:41
  • I think there used to be a module requirements check once, but that doesn't work very well as it is just check when trying to access route. Especially when relying on services from a module, it will already fail at compile time when it is defined non-dynamically.
    – Berdir
    Commented Sep 12, 2016 at 19:18

2 Answers 2

6

Yep, you need a ServiceProvider that only registers the service if the module exists in the container.modules parameter

See https://github.com/md-systems/file_entity/blob/8.x-2.x/src/FileEntityServiceProvider.php#L25 for an example

1

Drupal core itself uses register(), in one of its modules. See LanguageServiceProvider::register().

  // The following services are needed only on multilingual sites.
  if ($this->isMultilingual()) {
    $container->register('language_request_subscriber', 'Drupal\language\EventSubscriber\LanguageRequestSubscriber')
      ->addTag('event_subscriber')
      ->addArgument(new Reference('language_manager'))
      ->addArgument(new Reference('language_negotiator'))
      ->addArgument(new Reference('string_translation'))
      ->addArgument(new Reference('current_user'));

    $container->register('path_processor_language', 'Drupal\language\HttpKernel\PathProcessorLanguage')
      ->addTag('path_processor_inbound', array('priority' => 300))
      ->addTag('path_processor_outbound', array('priority' => 100))
      ->addArgument(new Reference('config.factory'))
      ->addArgument(new Reference('language_manager'))
      ->addArgument(new Reference('language_negotiator'))
      ->addArgument(new Reference('current_user'))
      ->addArgument(new Reference('language.config_subscriber'))
      ->addMethodCall('initConfigSubscriber');
  }

The code is quite similar to the code you would use: It checks a condition is verified at run-time, and then adds the necessary services. In this case, the alternative would be having two services that check if the site is multi-lingual and do nothing in the case it is not.

I take the note you quoted as meaning don't implement the register() method if there are other alternative implementations for what you are trying to achieve. In your case, the alternative would be checking from the service if the other module is installed, which is what the service implemented by the Drupal core module tries to avoid, or making your module dependent from the other module, which is what I would avoid, since (from what I understand) the module is not dependent from the other one, and it would work even without it being installed.

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.