APIv4 - Fix contactFieldsToRemove to work with joins and pseudoconstants
authorColeman Watts <coleman@civicrm.org>
Wed, 22 Apr 2020 21:48:34 +0000 (17:48 -0400)
committerColeman Watts <coleman@civicrm.org>
Thu, 23 Apr 2020 00:49:19 +0000 (20:49 -0400)
Civi/Api4/Utils/FormattingUtil.php
tests/phpunit/api/v4/Entity/ContactTypeTest.php

index 89f4c7c54bd0cd7063e172a17a0a0e75f4f554e2..818f591417d453beeb2bde5fa9f857fe449650b0 100644 (file)
@@ -140,10 +140,7 @@ class FormattingUtil {
   public static function formatOutputValues(&$results, $fields, $entity, $action = 'get') {
     $fieldOptions = [];
     foreach ($results as &$result) {
-      // Remove inapplicable contact fields
-      if ($entity === 'Contact' && !empty($result['contact_type'])) {
-        \CRM_Utils_Array::remove($result, self::contactFieldsToRemove($result['contact_type']));
-      }
+      $contactTypePaths = [];
       foreach ($result as $fieldExpr => $value) {
         $field = $fields[$fieldExpr] ?? NULL;
         $dataType = $field['data_type'] ?? ($fieldExpr == 'id' ? 'Integer' : NULL);
@@ -163,9 +160,18 @@ class FormattingUtil {
           if (isset($fieldOptions[$fieldExpr])) {
             $value = self::replacePseudoconstant($fieldOptions[$fieldExpr], $value);
           }
+          // Keep track of contact types for self::contactFieldsToRemove
+          if ($value && isset($field['entity']) && $field['entity'] === 'Contact' && $field['name'] === 'contact_type') {
+            $prefix = strrpos($fieldExpr, '.');
+            $contactTypePaths[$prefix ? substr($fieldExpr, 0, $prefix + 1) : ''] = $value;
+          }
         }
         $result[$fieldExpr] = self::convertDataType($value, $dataType);
       }
+      // Remove inapplicable contact fields
+      foreach ($contactTypePaths as $prefix => $contactType) {
+        \CRM_Utils_Array::remove($result, self::contactFieldsToRemove($contactType, $prefix));
+      }
     }
   }
 
@@ -257,19 +263,33 @@ class FormattingUtil {
   }
 
   /**
+   * Lists all field names (including suffixed variants) that should be removed for a given contact type.
+   *
    * @param string $contactType
+   *   Individual|Organization|Household
+   * @param string $prefix
+   *   Path at which these fields are found, e.g. "address.contact."
    * @return array
    */
-  public static function contactFieldsToRemove($contactType) {
+  public static function contactFieldsToRemove($contactType, $prefix) {
     if (!isset(\Civi::$statics[__CLASS__][__FUNCTION__][$contactType])) {
       \Civi::$statics[__CLASS__][__FUNCTION__][$contactType] = [];
       foreach (\CRM_Contact_DAO_Contact::fields() as $field) {
         if (!empty($field['contactType']) && $field['contactType'] != $contactType) {
           \Civi::$statics[__CLASS__][__FUNCTION__][$contactType][] = $field['name'];
+          // Include suffixed variants like prefix_id:label
+          if (!empty($field['pseudoconstant'])) {
+            foreach (array_keys(self::$pseudoConstantContexts) as $suffix) {
+              \Civi::$statics[__CLASS__][__FUNCTION__][$contactType][] = $field['name'] . ':' . $suffix;
+            }
+          }
         }
       }
     }
-    return \Civi::$statics[__CLASS__][__FUNCTION__][$contactType];
+    // Add prefix paths
+    return array_map(function($name) use ($prefix) {
+      return $prefix . $name;
+    }, \Civi::$statics[__CLASS__][__FUNCTION__][$contactType]);
   }
 
 }
index c09e363b5c405434855a35162deacce6d2c11384..9e7efec1982c8c313c05b72b014b5046f9562088 100644 (file)
@@ -23,30 +23,35 @@ namespace api\v4\Entity;
 
 use Civi\Api4\Contact;
 use api\v4\UnitTestCase;
+use Civi\Api4\Email;
 
 /**
  * @group headless
  */
 class ContactTypeTest extends UnitTestCase {
 
-  public function testContactGetReturnsFieldsAppropriateToEachContactType() {
+  public function testGetReturnsFieldsAppropriateToEachContactType() {
     $indiv = Contact::create()
-      ->setValues(['first_name' => 'Joe', 'last_name' => 'Tester', 'contact_type' => 'Individual'])
+      ->setValues(['first_name' => 'Joe', 'last_name' => 'Tester', 'prefix_id:label' => 'Dr.', 'contact_type' => 'Individual'])
+      ->addChain('email', Email::create()->setValues(['contact_id' => '$id', 'email' => 'ind@example.com']))
       ->setCheckPermissions(FALSE)
       ->execute()->first()['id'];
 
     $org = Contact::create()
       ->setValues(['organization_name' => 'Tester Org', 'contact_type' => 'Organization'])
+      ->addChain('email', Email::create()->setValues(['contact_id' => '$id', 'email' => 'org@example.com']))
       ->setCheckPermissions(FALSE)
       ->execute()->first()['id'];
 
     $hh = Contact::create()
       ->setValues(['household_name' => 'Tester Family', 'contact_type' => 'Household'])
+      ->addChain('email', Email::create()->setValues(['contact_id' => '$id', 'email' => 'hh@example.com']))
       ->setCheckPermissions(FALSE)
       ->execute()->first()['id'];
 
     $result = Contact::get()
       ->setCheckPermissions(FALSE)
+      ->addSelect('*', 'prefix_id:label')
       ->addWhere('id', 'IN', [$indiv, $org, $hh])
       ->execute()
       ->indexBy('id');
@@ -55,6 +60,10 @@ class ContactTypeTest extends UnitTestCase {
     $this->assertArrayNotHasKey('first_name', $result[$org]);
     $this->assertArrayNotHasKey('first_name', $result[$hh]);
 
+    $this->assertEquals('Dr.', $result[$indiv]['prefix_id:label']);
+    $this->assertArrayNotHasKey('prefix_id:label', $result[$org]);
+    $this->assertArrayNotHasKey('prefix_id:label', $result[$hh]);
+
     $this->assertArrayHasKey('organization_name', $result[$org]);
     $this->assertArrayNotHasKey('organization_name', $result[$indiv]);
     $this->assertArrayNotHasKey('organization_name', $result[$hh]);
@@ -66,6 +75,34 @@ class ContactTypeTest extends UnitTestCase {
     $this->assertArrayHasKey('household_name', $result[$hh]);
     $this->assertArrayNotHasKey('household_name', $result[$org]);
     $this->assertArrayNotHasKey('household_name', $result[$indiv]);
+
+    $emails = Email::get()
+      ->setCheckPermissions(FALSE)
+      ->addWhere('contact_id', 'IN', [$indiv, $org, $hh])
+      ->addSelect('id', 'contact_id', 'contact.*', 'contact.prefix_id:label')
+      ->execute()
+      ->indexBy('contact_id');
+
+    $this->assertArrayHasKey('contact.first_name', $emails[$indiv]);
+    $this->assertArrayNotHasKey('contact.first_name', $emails[$org]);
+    $this->assertArrayNotHasKey('contact.first_name', $emails[$hh]);
+
+    $this->assertEquals('Dr.', $emails[$indiv]['contact.prefix_id:label']);
+    $this->assertArrayNotHasKey('contact.prefix_id:label', $emails[$org]);
+    $this->assertArrayNotHasKey('contact.prefix_id:label', $emails[$hh]);
+
+    $this->assertArrayHasKey('contact.organization_name', $emails[$org]);
+    $this->assertArrayNotHasKey('contact.organization_name', $emails[$indiv]);
+    $this->assertArrayNotHasKey('contact.organization_name', $emails[$hh]);
+
+    $this->assertArrayHasKey('contact.sic_code', $emails[$org]);
+    $this->assertArrayNotHasKey('contact.sic_code', $emails[$indiv]);
+    $this->assertArrayNotHasKey('contact.sic_code', $emails[$hh]);
+
+    $this->assertArrayHasKey('contact.household_name', $emails[$hh]);
+    $this->assertArrayNotHasKey('contact.household_name', $emails[$org]);
+    $this->assertArrayNotHasKey('contact.household_name', $emails[$indiv]);
+
   }
 
 }