dev/core#2823 Restructure determination of required actions.
authorEileen McNaughton <emcnaughton@wikimedia.org>
Wed, 8 Sep 2021 05:08:34 +0000 (17:08 +1200)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Sun, 12 Sep 2021 22:25:20 +0000 (10:25 +1200)
The expected actions are now calculated at the start of reconcile.
and retrievable by function.

The code that calls the action-actions
is still pretty cludgey - but the intent is that
we would stop passing the dao object into those
action-action functions in the near future.

This PR punts any decisions about how outputs might look
(eg. making the array retrievable from outside of the function.

Note that there is pretty comprehensive
test cover in CRM_Core_ManagedEntitiesTest

CRM/Core/ManagedEntities.php

index e4415db15f763de65620d5bd518a986b83ddfe87..2f862483ecaf95cf80db33b7a04a846497832652 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use Civi\Api4\Managed;
+
 /**
  * The ManagedEntities system allows modules to add records to the database
  * declaratively.  Those records will be automatically inserted, updated,
@@ -26,6 +28,13 @@ class CRM_Core_ManagedEntities {
    */
   protected $moduleIndex;
 
+  /**
+   * Actions arising from the managed entities.
+   *
+   * @var array
+   */
+  protected $managedActions = [];
+
   /**
    * @var array
    *   List of all entity declarations.
@@ -72,6 +81,8 @@ class CRM_Core_ManagedEntities {
   /**
    * Read a managed entity using APIv3.
    *
+   * @deprecated
+   *
    * @param string $moduleName
    *   The name of the module which declared entity.
    * @param string $name
@@ -114,9 +125,11 @@ class CRM_Core_ManagedEntities {
     if (CRM_Core_Config::singleton()->isUpgradeMode() && !$ignoreUpgradeMode) {
       return;
     }
+
     if ($error = $this->validate($this->getDeclarations())) {
       throw new CRM_Core_Exception($error);
     }
+    $this->loadManagedEntityActions();
     $this->reconcileEnabledModules();
     $this->reconcileDisabledModules();
     $this->reconcileUnknownModules();
@@ -136,7 +149,7 @@ class CRM_Core_ManagedEntities {
     $decls = $this->createDeclarationIndex($this->moduleIndex, $this->getDeclarations());
     foreach ($decls as $moduleName => $todos) {
       if ($this->isModuleEnabled($moduleName)) {
-        $this->reconcileEnabledModule($this->moduleIndex[TRUE][$moduleName], $todos);
+        $this->reconcileEnabledModule($moduleName);
       }
     }
   }
@@ -145,33 +158,87 @@ class CRM_Core_ManagedEntities {
    * For one enabled module, add new entities, update existing entities,
    * and remove orphaned (stale) entities.
    *
-   * @param \CRM_Core_Module $module
-   * @param array $todos
-   *   List of entities currently declared by this module.
-   *   array(string $name => array $entityDef).
+   * @param string $module
    */
-  public function reconcileEnabledModule(CRM_Core_Module $module, $todos) {
-    $dao = new CRM_Core_DAO_Managed();
-    $dao->module = $module->name;
-    $dao->find();
-    while ($dao->fetch()) {
-      if (isset($todos[$dao->name]) && $todos[$dao->name]) {
-        // update existing entity; remove from $todos
-        $this->updateExistingEntity($dao, $todos[$dao->name]);
-        unset($todos[$dao->name]);
-      }
-      else {
-        // remove stale entity; not in $todos
-        $this->removeStaleEntity($dao);
-      }
+  public function reconcileEnabledModule(string $module): void {
+    foreach ($this->getManagedEntitiesToUpdate(['module' => $module]) as $todo) {
+      $dao = new CRM_Core_DAO_Managed();
+      $dao->module = $todo['module'];
+      $dao->name = $todo['name'];
+      $dao->entity_type = $todo['entity_type'];
+      $dao->entity_id = $todo['entity_id'];
+      $dao->id = $todo['id'];
+      $this->updateExistingEntity($dao, $todo);
     }
 
-    // create new entities from leftover $todos
-    foreach ($todos as $name => $todo) {
+    foreach ($this->getManagedEntitiesToDelete(['module' => $module]) as $todo) {
+      $dao = new CRM_Core_DAO_Managed();
+      $dao->module = $todo['module'];
+      $dao->name = $todo['name'];
+      $dao->entity_type = $todo['entity_type'];
+      $dao->id = $todo['id'];
+      $dao->cleanup = $todo['cleanup'];
+      $dao->entity_id = $todo['entity_id'];
+      $this->removeStaleEntity($dao);
+    }
+    foreach ($this->getManagedEntitiesToCreate(['module' => $module]) as $todo) {
       $this->insertNewEntity($todo);
     }
   }
 
+  /**
+   * Get the managed entities to be created.
+   *
+   * @param array $filters
+   *
+   * @return array
+   */
+  protected function getManagedEntitiesToCreate(array $filters = []): array {
+    return $this->getManagedEntities(array_merge($filters, ['managed_action' => 'create']));
+  }
+
+  /**
+   * Get the managed entities to be created.
+   *
+   * @param array $filters
+   *
+   * @return array
+   */
+  protected function getManagedEntitiesToUpdate(array $filters = []): array {
+    return $this->getManagedEntities(array_merge($filters, ['managed_action' => 'update']));
+  }
+
+  /**
+   * Get the managed entities to be deleted.
+   *
+   * @param array $filters
+   *
+   * @return array
+   */
+  protected function getManagedEntitiesToDelete(array $filters = []): array {
+    return $this->getManagedEntities(array_merge($filters, ['managed_action' => 'delete']));
+  }
+
+  /**
+   * Get the managed entities that fit the criteria.
+   *
+   * @param array $filters
+   *
+   * @return array
+   */
+  protected function getManagedEntities(array $filters = []): array {
+    $return = [];
+    foreach ($this->managedActions as $actionKey => $action) {
+      foreach ($filters as $filterKey => $filterValue) {
+        if ($action[$filterKey] !== $filterValue) {
+          continue 2;
+        }
+      }
+      $return[$actionKey] = $action;
+    }
+    return $return;
+  }
+
   /**
    * For all disabled modules, disable any managed entities.
    */
@@ -223,15 +290,15 @@ class CRM_Core_ManagedEntities {
    *   Entity specification (per hook_civicrm_managedEntities).
    */
   protected function insertNewEntity($todo) {
-    $result = civicrm_api($todo['entity'], 'create', $todo['params']);
+    $result = civicrm_api($todo['entity_type'], 'create', $todo['params']);
     if (!empty($result['is_error'])) {
-      $this->onApiError($todo['entity'], 'create', $todo['params'], $result);
+      $this->onApiError($todo['entity_type'], 'create', $todo['params'], $result);
     }
 
     $dao = new CRM_Core_DAO_Managed();
     $dao->module = $todo['module'];
     $dao->name = $todo['name'];
-    $dao->entity_type = $todo['entity'];
+    $dao->entity_type = $todo['entity_type'];
     // A fatal error will result if there is no valid id but if
     // this is v4 api we might need to access it via ->first().
     $dao->entity_id = $result['id'] ?? $result->first()['id'];
@@ -314,7 +381,7 @@ class CRM_Core_ManagedEntities {
    * Remove a stale entity (if policy allows).
    *
    * @param CRM_Core_DAO_Managed $dao
-   * @throws Exception
+   * @throws CRM_Core_Exception
    */
   protected function removeStaleEntity($dao) {
     $policy = empty($dao->cleanup) ? 'always' : $dao->cleanup;
@@ -342,7 +409,7 @@ class CRM_Core_ManagedEntities {
         break;
 
       default:
-        throw new \Exception('Unrecognized cleanup policy: ' . $policy);
+        throw new CRM_Core_Exception('Unrecognized cleanup policy: ' . $policy);
     }
 
     if ($doDelete) {
@@ -535,4 +602,34 @@ class CRM_Core_ManagedEntities {
     $this->declarations = $this->cleanDeclarations($this->declarations);
   }
 
+  protected function loadManagedEntityActions(): void {
+    $managedEntities = Managed::get(FALSE)->addSelect('*')->execute();
+    foreach ($managedEntities as $managedEntity) {
+      $key = "{$managedEntity['module']}_{$managedEntity['name']}_{$managedEntity['entity_type']}";
+      // Set to 'delete' - it will be overwritten below if it is to be updated.
+      $action = 'delete';
+      $this->managedActions[$key] = array_merge($managedEntity, ['managed_action' => $action]);
+    }
+    foreach ($this->declarations as $declaration) {
+      $key = "{$declaration['module']}_{$declaration['name']}_{$declaration['entity']}";
+      if (isset($this->managedActions[$key])) {
+        $this->managedActions[$key]['params'] = $declaration['params'];
+        $this->managedActions[$key]['managed_action'] = 'update';
+        $this->managedActions[$key]['cleanup'] = $declaration['cleanup'] ?? NULL;
+        $this->managedActions[$key]['update'] = $declaration['update'] ?? 'always';
+      }
+      else {
+        $this->managedActions[$key] = [
+          'module' => $declaration['module'],
+          'name' => $declaration['name'],
+          'entity_type' => $declaration['entity'],
+          'managed_action' => 'create',
+          'params' => $declaration['params'],
+          'cleanup' => $declaration['cleanup'] ?? NULL,
+          'update' => $declaration['update'] ?? 'always',
+        ];
+      }
+    }
+  }
+
 }