BAOs - Allow BAOs to subscribe to hooks/events via `EventSubscriberInterface` or...
authorTim Otten <totten@civicrm.org>
Fri, 21 May 2021 18:51:32 +0000 (11:51 -0700)
committerTim Otten <totten@civicrm.org>
Wed, 26 May 2021 23:52:19 +0000 (16:52 -0700)
Before: There is no pleasant way for a BAO to register a listener for an
event.  True, one can use `Civi::dispatch()`, but (lacking a good
initialization point) it's hard to register reliably.  True, you can also
add new items to `\Civi\Core\Container::createEventDispatcher()`, but
this is rather out-of-the-way.

After: A BAO class may optionally implement `EventDispatcherInterface` or
`HookInterface`. Methods will be automatically registered. The list
of listeners will be cached in the container.

CRM/Core/DAO/AllCoreTables.php
Civi/Core/Container.php

index 0e4b38b848d9127187b449488430a3f096f472d6..04e4fe32dc81ecc7f3a232f237e6377b799c1df0 100644 (file)
@@ -276,6 +276,23 @@ class CRM_Core_DAO_AllCoreTables {
     return array_values(self::daoToClass());
   }
 
+  /**
+   * Get a list of all extant BAO classes.
+   *
+   * @return array
+   *   Ex: ['Contact' => 'CRM_Contact_BAO_Contact']
+   */
+  public static function getBaoClasses() {
+    $r = [];
+    foreach (\CRM_Core_DAO_AllCoreTables::daoToClass() as $entity => $daoClass) {
+      $baoClass = str_replace('_DAO_', '_BAO_', $daoClass);
+      if (class_exists($baoClass)) {
+        $r[$entity] = $baoClass;
+      }
+    }
+    return $r;
+  }
+
   /**
    * Get the classname for the table.
    *
index 947a2657d8d3e554f7f2e21732859317038e2332..fa723a77b34c209bad7028d46a618bdd5033e017 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 namespace Civi\Core;
 
+use Civi\Core\Event\EventScanner;
 use Civi\Core\Lock\LockManager;
 use Symfony\Component\Config\ConfigCache;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -338,6 +339,16 @@ class Container {
       ))->addTag('kernel.event_subscriber')->setPublic(TRUE);
     }
 
+    $dispatcherDefn = $container->getDefinition('dispatcher');
+    foreach (\CRM_Core_DAO_AllCoreTables::getBaoClasses() as $baoEntity => $baoClass) {
+      $listenerMap = EventScanner::findListeners($baoClass, $baoEntity);
+      if ($listenerMap) {
+        $file = (new \ReflectionClass($baoClass))->getFileName();
+        $container->addResource(new \Symfony\Component\Config\Resource\FileResource($file));
+        $dispatcherDefn->addMethodCall('addListenerMap', [$baoClass, $listenerMap]);
+      }
+    }
+
     \CRM_Api4_Services::hook_container($container);
 
     \CRM_Utils_Hook::container($container);