From 663cc0b40b9e2d5c0311b22f2d892b563b6c29b4 Mon Sep 17 00:00:00 2001 From: Jon goldberg Date: Thu, 24 Aug 2017 18:28:15 -0400 Subject: [PATCH] CRM-21108 - Refactor greeting token replacement code to avoid calls to expensive queries in most situations Formatting fixes --- CRM/Contact/BAO/Contact.php | 7 +++-- CRM/Utils/Token.php | 43 ++++++++++++++++++++++++++++--- tests/phpunit/CRM/Utils/Token.php | 24 +++++++++++++++++ 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/CRM/Contact/BAO/Contact.php b/CRM/Contact/BAO/Contact.php index 58d030770e..b7bae7d7f7 100644 --- a/CRM/Contact/BAO/Contact.php +++ b/CRM/Contact/BAO/Contact.php @@ -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); } diff --git a/CRM/Utils/Token.php b/CRM/Utils/Token.php index 4026670699..faf9863504 100644 --- a/CRM/Utils/Token.php +++ b/CRM/Utils/Token.php @@ -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 * diff --git a/tests/phpunit/CRM/Utils/Token.php b/tests/phpunit/CRM/Utils/Token.php index 04e7394d98..2512b5cba6 100644 --- a/tests/phpunit/CRM/Utils/Token.php +++ b/tests/phpunit/CRM/Utils/Token.php @@ -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. * -- 2.25.1