ManagedEntities - Allow "match" param to convert existing records to managed entities
authorColeman Watts <coleman@civicrm.org>
Thu, 3 Mar 2022 21:35:52 +0000 (16:35 -0500)
committerColeman Watts <coleman@civicrm.org>
Thu, 3 Mar 2022 21:35:52 +0000 (16:35 -0500)
This can help ease the pain of declaring managed entities which may or may not
already exist - now they can be matched by name or other unique identifier.

CRM/Core/ManagedEntities.php
tests/phpunit/api/v4/Entity/ManagedEntityTest.php

index eeb78f947d601ce29d16e9f30d2adca1a5974d30..ff9ae5268ef103eaa9c75d2d311e2f03c0b6788d 100644 (file)
@@ -311,22 +311,30 @@ class CRM_Core_ManagedEntities {
    *   Entity specification (per hook_civicrm_managedEntities).
    */
   protected function insertNewEntity($todo) {
-    if ($todo['params']['version'] == 4) {
-      $todo['params']['checkPermissions'] = FALSE;
-    }
-
-    $result = civicrm_api($todo['entity_type'], 'create', ['debug' => TRUE] + $todo['params']);
-    if (!empty($result['is_error'])) {
-      $this->onApiError($todo['entity_type'], 'create', $todo['params'], $result);
+    $params = $todo['params'];
+    // APIv4
+    if ($params['version'] == 4) {
+      $params['checkPermissions'] = FALSE;
+      // Use "save" instead of "create" action to accommodate a "match" param
+      $params['records'] = [$params['values']];
+      unset($params['values']);
+      $result = civicrm_api4($todo['entity_type'], 'save', $params);
+      $id = $result->first()['id'];
+    }
+    // APIv3
+    else {
+      $result = civicrm_api($todo['entity_type'], 'create', $params);
+      if (!empty($result['is_error'])) {
+        $this->onApiError($todo['entity_type'], 'create', $params, $result);
+      }
+      $id = $result['id'];
     }
 
     $dao = new CRM_Core_DAO_Managed();
     $dao->module = $todo['module'];
     $dao->name = $todo['name'];
     $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'];
+    $dao->entity_id = $id;
     $dao->cleanup = $todo['cleanup'] ?? NULL;
     $dao->save();
   }
@@ -382,6 +390,8 @@ class CRM_Core_ManagedEntities {
     elseif ($doUpdate && $todo['params']['version'] == 4) {
       $params = ['checkPermissions' => FALSE] + $todo['params'];
       $params['values']['id'] = $dao->entity_id;
+      // 'match' param doesn't apply to "update" action
+      unset($params['match']);
       civicrm_api4($dao->entity_type, 'update', $params);
     }
 
index 481beb99af09cc2824d472ab5c61d2b5b2ad6f59..13eb440498a0d82e129b8925a70b3a20ed2c70ed 100644 (file)
@@ -586,6 +586,57 @@ class ManagedEntityTest extends UnitTestCase implements TransactionalInterface,
     $this->assertGreaterThan($original['id'], $created['id']);
   }
 
+  /**
+   * Tests a scenario where a record may already exist and we want to make it a managed entity
+   */
+  public function testMatchExisting() {
+    $optionGroup = OptionGroup::create(FALSE)
+      ->addValue('title', 'My pre-existing group')
+      ->addValue('name', 'My_pre_existing_group')
+      ->execute()->first();
+
+    $managed = [
+      'module' => 'civicrm',
+      'name' => 'preExistingGroup',
+      'entity' => 'OptionGroup',
+      'cleanup' => 'always',
+      'update' => 'always',
+      'params' => [
+        'version' => 4,
+        'values' => [
+          'name' => $optionGroup['name'],
+          'title' => "Cool new title",
+          'description' => 'Cool new description',
+        ],
+      ],
+    ];
+    $this->_managedEntities = [$managed];
+
+    // Without "match" in the params, it will try and fail to add a duplicate managed record
+    try {
+      \CRM_Core_ManagedEntities::singleton(TRUE)->reconcile();
+    }
+    catch (\Exception $e) {
+    }
+    $this->assertStringContainsString('already exists', $e->getMessage());
+
+    // Now reconcile using a match param
+    $managed['params']['match'] = ['name'];
+    $this->_managedEntities = [$managed];
+    \CRM_Core_ManagedEntities::singleton(TRUE)->reconcile();
+
+    $managedGroup = OptionGroup::get(FALSE)
+      ->addWhere('name', '=', $optionGroup['name'])
+      ->addSelect('id', 'title', 'description', 'base_module')
+      ->execute()->single();
+
+    $this->assertEquals($optionGroup['id'], $managedGroup['id']);
+    $this->assertEquals('Cool new title', $managedGroup['title']);
+    $this->assertEquals('Cool new description', $managedGroup['description']);
+    // The existing record has been converted to a managed entity!
+    $this->assertEquals('civicrm', $managedGroup['base_module']);
+  }
+
   /**
    * @dataProvider sampleEntityTypes
    * @param string $entityName