Plugins before event subscribers

by Adaddinsane   Last Updated December 19, 2018 12:07 PM - source

Not sure if there's a way out of this problem but...

I have a custom log module which listens for events that could come from multiple sources. All it does is listen for an event, and if it occurs logs an entry in the DB.

But there could be different recorded data coming from different modules and I want to separate concerns so there's no complex web of dependencies.

If Module L is the listener it might get EventA from module A and EventB from module B. The events have a common base but each module stores slightly different data in the log which needs to be set-up in each case.

Option 1) Module L could be set up to listen for "module_a.event" and "module_b.event" and process them separately but that is a "bad thing" because the listener is dependent on each of the other modules. The Listener shouldn't need to know how the data is stored, it doesn't care.

Option 2) Or Module L could have a specialised event "listener.event" that Module A and Module B calls having set up the data in the event correctly. But that means those modules have a dependency on the listener.

I don't like either option because of the dependency requirements in one direction or the other.

Bright idea: Create a plugin in Module L that builds the log structure for a particular module. So Module A has a plugin that handles its requirements, and Module B has another plugin that handles its needs.

There's no dependency in either direction because if Module L is not running then A and B don't care (the plugins will never be scanned or loaded). And if either or both of A and B are missing, Module L doesn't care.

And if I then need a Module C I just give it its own plugin.

But Module L still needs to listen for events from A, B and C (module_a.event, module_b.event and module_c.event). How does it know what events it should listen for?

Easy: Set a value in the annotation of each plugin giving the name of the event to listen for. We can load all the plugin definitions in the EventSubscriber and set it up. Fantastic. Problem solved. No dependency but every event type can be listened for and handled.

Except it doesn't work because: apparently event subscribers are initialised before plugin services.

My "module_l.services.yml" looks like this:

services:
  notifications_log.notifier:
    class: Drupal\notifications_log\EventSubscriber\NotifierSubscriber
    arguments: ['@plugin.manager.notifier', '@entity_type.manager',
                '@current_user', '@logger.factory']
    tags:
      - { name: event_subscriber }

  plugin.manager.notifier:
    class: Drupal\notifications_log\Plugin\NotifierManager
    parent: default_plugin_manager

Which seems fine (doesn't matter which way round they go BTW - I've tried) but we get a fail here, in the EventSubscriber:

  static function getSubscribedEvents() {
    /** @var NotifierManager $notifierManager */
    $notifierManager = \Drupal::service('plugin.manager.notifier');

    $events = [];
    foreach ($notifierManager->getNotifierEventNames() as $eventName => $plugin_id) {
      $events[$eventName] = ['onNotification', 0];
    }

    return $events;
  }

This is a static function so we have to fetch the plugin manager directly, and it can't find it.

Is there any way around this? I could find write some code to find all the plugins myself but that's a lot of faffing about.

What I really need is a way of dynamically adding to the Event subscribers, but I haven't found a way to do that.



Related Questions


Caching Event Subcriber

Updated March 13, 2016 08:03 AM


How can an event subscriber have access to entity type?

Updated November 17, 2017 23:07 PM