Add API-based EntityLookupTrait
authorcolemanw <coleman@civicrm.org>
Sat, 2 Sep 2023 03:27:23 +0000 (23:27 -0400)
committercolemanw <coleman@civicrm.org>
Sat, 2 Sep 2023 03:27:23 +0000 (23:27 -0400)
Civi/API/EntityLookupTrait.php [new file with mode: 0644]
Civi/Test/EntityTrait.php
tests/phpunit/Civi/API/EntityLookupTest.php [new file with mode: 0644]

diff --git a/Civi/API/EntityLookupTrait.php b/Civi/API/EntityLookupTrait.php
new file mode 100644 (file)
index 0000000..d205dee
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+namespace Civi\API;
+
+trait EntityLookupTrait {
+
+  /**
+   * Array of defined entity identifiers.
+   *
+   * @var array
+   */
+  private $entityLookupDefinitions = [];
+
+  /**
+   * Array of defined entity values.
+   *
+   * @var array
+   */
+  private $entityLookupValues = [];
+
+  /**
+   * Defines a record so its values can be retrieved using `$this->lookup()`
+   *
+   * @param string $apiEntityName
+   * @param string $nickname
+   *   Handle to use to retrieve values with `$this->lookup()`
+   * @param array $identifier
+   *   A unique key or combination of keys to uniquely identify the record (usually id)
+   *   Most commonly looks like `['id' => 123]`
+   */
+  protected function define(string $apiEntityName, string $nickname, array $identifier): void {
+    $this->entityLookupDefinitions[$nickname] = [
+      'entityName' => $apiEntityName,
+      'identifier' => $identifier,
+    ];
+    $this->entityLookupValues[$nickname] = [];
+  }
+
+  /**
+   * Retrieve a field value for a defined entity
+   *
+   * @param string $nickname
+   *   Handle set by `$this->define()`
+   * @param string $fieldName
+   * @return mixed
+   * @throws \CRM_Core_Exception
+   */
+  public function lookup(string $nickname, string $fieldName) {
+    if (!isset($this->entityLookupValues[$nickname])) {
+      throw new \CRM_Core_Exception(sprintf('Cannot lookup entity "%s" before it has been defined.', $nickname));
+    }
+    if (array_key_exists($fieldName, $this->entityLookupValues[$nickname])) {
+      return $this->entityLookupValues[$nickname][$fieldName];
+    }
+    $entityName = $this->entityLookupDefinitions[$nickname]['entityName'];
+    $params = [
+      'select' => [$fieldName],
+      'where' => [],
+      'checkPermissions' => FALSE,
+    ];
+    foreach ($this->entityLookupDefinitions[$nickname]['identifier'] as $key => $val) {
+      $params['where'][] = [$key, '=', $val];
+    }
+    if (!$this->entityLookupValues[$nickname]) {
+      $params['select'][] = '*';
+      if ($entityName === 'Contact') {
+        $params['select'][] = 'email_primary.*';
+      }
+    }
+    // If requesting a join or a custom field, fetch them all by replacing the last part with a *
+    if (str_contains($fieldName, '.')) {
+      $parts = explode('.', $fieldName);
+      $parts[count($parts) - 1] = '*';
+      $params['select'][] = implode('.', $parts);
+    }
+    $retrieved = civicrm_api4($entityName, 'get', $params)->single();
+    $this->entityLookupValues[$nickname] += $retrieved;
+    return $this->entityLookupValues[$nickname][$fieldName] ?? NULL;
+  }
+
+}
index 1dfe24cbfacb36d987eed8ea23f4e69640bca8b9..6a3f0707b99de262ef030aa84894615eec524c21 100644 (file)
@@ -47,15 +47,15 @@ trait EntityTrait {
    * Create an entity, recording it's details for tearDown.
    *
    * @param string $entity
-   * @param array $params
+   * @param array $values
    * @param string $identifier
    *
    * @return array
    */
-  protected function createTestEntity(string $entity, array $params, string $identifier = 'default'): array {
+  protected function createTestEntity(string $entity, array $values, string $identifier = 'default'): array {
     $result = NULL;
     try {
-      $result = \civicrm_api4($entity, 'create', ['values' => $params, 'checkPermissions' => FALSE])->first();
+      $result = \civicrm_api4($entity, 'create', ['values' => $values, 'checkPermissions' => FALSE])->single();
       $this->setTestEntityID($entity, $result['id'], $identifier);
     }
     catch (\CRM_Core_Exception $e) {
diff --git a/tests/phpunit/Civi/API/EntityLookupTest.php b/tests/phpunit/Civi/API/EntityLookupTest.php
new file mode 100644 (file)
index 0000000..954c4fa
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+namespace Civi\API;
+
+class EntityLookupTest extends \CiviUnitTestCase {
+
+  use EntityLookupTrait;
+
+  public function testLookupContacts() {
+    $bob = $this->createTestEntity('Contact', ['first_name' => 'Bob', 'last_name' => 'One', 'gender_id:name' => 'Male', 'email_primary.email' => 'bob@one.test']);
+    $jan = $this->createTestEntity('Contact', ['first_name' => 'Jan', 'last_name' => 'Two', 'gender_id:name' => 'Female', 'external_identifier' => uniqid()]);
+    $this->define('Contact', 'Bob', ['id' => $bob['id']]);
+    $this->define('Contact', 'Jan', ['external_identifier' => $jan['external_identifier']]);
+    $this->assertEquals('One', $this->lookup('Bob', 'last_name'));
+    $this->assertEquals('bob@one.test', $this->lookup('Bob', 'email_primary.email'));
+    $this->assertEquals('Male', $this->lookup('Bob', 'gender_id:name'));
+    $this->assertEquals('Two', $this->lookup('Jan', 'last_name'));
+    $this->assertEquals('Female', $this->lookup('Jan', 'gender_id:name'));
+  }
+
+}