CRM-21108 - Refactor greeting token replacement code to avoid calls to expensive...
authorJon goldberg <jon@palantetech.coop>
Thu, 24 Aug 2017 22:28:15 +0000 (18:28 -0400)
committerJon goldberg <jon@palantetech.coop>
Sun, 27 Aug 2017 22:54:53 +0000 (18:54 -0400)
Formatting fixes

CRM/Contact/BAO/Contact.php
CRM/Utils/Token.php
tests/phpunit/CRM/Utils/Token.php

index 58d030770e35e6812739dc6d84393f9765d569e8..b7bae7d7f709031d2ef117e402b9b6b2dee92644 100644 (file)
@@ -2651,10 +2651,9 @@ AND       civicrm_openid.is_primary = 1";
       $contactDefaults = $defaultGreetings[$contact->contact_type];
     }
 
-    // note that contact object not always has required greeting related
-    // fields that are required to calculate greeting and
-    // also other fields used in tokens etc,
-    // hence we need to retrieve it again.
+    // The contact object has not always required the
+    // fields that are required to calculate greetings
+    // so we need to retrieve it again.
     if ($contact->_query !== FALSE) {
       $contact->find(TRUE);
     }
index 402667069954a12eb1389883f0c3a7c906d0bdf9..faf98635042701c2eae32fb2ace6ad6e5c977af7 100644 (file)
@@ -1235,9 +1235,7 @@ class CRM_Utils_Token {
       }
     }
 
-    $query = new CRM_Contact_BAO_Query($params, $returnProperties);
-
-    $details = $query->apiQuery($params, $returnProperties, NULL, NULL, 0, count($contactIDs));
+    $details = CRM_Contact_BAO_Query::apiQuery($params, $returnProperties, NULL, NULL, 0, count($contactIDs));
 
     $contactDetails = &$details[0];
 
@@ -1368,6 +1366,7 @@ class CRM_Utils_Token {
         $tokenString = CRM_Utils_Token::replaceContactTokens($tokenString, $contactDetails, TRUE, $greetingTokens, TRUE, $escapeSmarty);
       }
 
+      self::removeNullContactTokens($tokenString, $contactDetails[0][$contactId], $greetingTokens);
       // check if there are any unevaluated tokens
       $greetingTokens = self::getTokens($tokenString);
 
@@ -1426,6 +1425,44 @@ class CRM_Utils_Token {
     }
   }
 
+  /**
+   * At this point, $contactDetails has loaded the contact from the DAO. Any
+   * (non-custom) missing fields are null.  By removing them, we can avoid
+   * expensive calls to CRM_Contact_BAO_Query.
+   *
+   * @param string $tokenString
+   * @param array $contactDetails
+   */
+  private static function removeNullContactTokens(&$tokenString, $contactDetails, &$greetingTokens) {
+    $greetingTokensOriginal = $greetingTokens;
+    $contactFieldList = CRM_Contact_DAO_Contact::fields();
+    $nullFields = array_keys(array_diff_key($contactFieldList, $contactDetails));
+
+    // Handle legacy tokens
+    foreach (self::legacyContactTokens() as $oldToken => $newToken) {
+      if (CRM_Utils_Array::key($newToken, $nullFields)) {
+        $nullFields[] = $oldToken;
+      }
+    }
+
+    // Remove null contact fields from $greetingTokens
+    $greetingTokens['contact'] = array_diff($greetingTokens['contact'], $nullFields);
+
+    // Also remove them from $tokenString
+    $removedTokens = array_diff($greetingTokensOriginal['contact'], $greetingTokens['contact']);
+    // Handle legacy tokens again, sigh
+    if (!empty($removedTokens)) {
+      foreach ($removedTokens as $token) {
+        if (CRM_Utils_Array::value($token, self::legacyContactTokens()) !== NULL) {
+          $removedTokens[] = CRM_Utils_Array::value($token, self::legacyContactTokens());
+        }
+      }
+      foreach ($removedTokens as $token) {
+        $tokenString = str_replace("{contact.$token}", '', $tokenString);
+      }
+    }
+  }
+
   /**
    * @param $tokens
    *
index 04e7394d9899aaed82d6227bf5475ea53e68457b..2512b5cba6de2b9116b384f945ba18ff4d6a9feb 100644 (file)
@@ -15,6 +15,30 @@ class CRM_Utils_TokenTest extends CiviUnitTestCase {
     $this->assertEquals('Phone, Fax', $resolvedTokens[0][$contactID]['preferred_communication_method']);
   }
 
+  /**
+   * Test for replaceContactTokens.
+   *
+   */
+  public function testReplaceGreetingTokens() {
+    $tokenString = 'First Name: {contact.first_name} Last Name: {contact.last_name} Birth Date: {contact.birth_date} Prefix: {contact.prefix_id} Suffix: {contact.individual_suffix}';
+    $contactDetails = array(
+      array(
+        2811 => array(
+          'id' => '2811',
+          'contact_type' => 'Individual',
+          'first_name' => 'Morticia',
+          'last_name' => 'Addams',
+          'prefix_id' => 2,
+        ),
+      ),
+    );
+    $contactId = 2811;
+    $className = 'CRM_Contact_BAO_Contact';
+    $escapeSmarty = TRUE;
+    CRM_Utils_Token::replaceGreetingTokens($tokenString, $contactDetails, $contactId, $className, $escapeSmarty);
+    $this->assertEquals($tokenString, 'First Name: Morticia Last Name: Addams Birth Date:  Prefix: Ms. Suffix: ');
+  }
+
   /**
    * Test getting multiple contacts.
    *