ActionSchedule - Fill TokenProcessor will real batches
authorTim Otten <totten@civicrm.org>
Wed, 11 Aug 2021 00:52:13 +0000 (17:52 -0700)
committerTim Otten <totten@civicrm.org>
Wed, 11 Aug 2021 01:57:05 +0000 (18:57 -0700)
Background: `ActionSchedule::sendMailings()` fetches a batch of pending reminders (per
some specific schedule/rule). Then it composes and sends a message for each.

Before: For each item in the batch, it makes a new `TokenProcessor`.  This
means that the `TokenProcessor` only sees one pending reminder. Therefore, the
`TokenProcessor` cannot meaningfully fetch batched data.

After: It creates one `TokenProcessor` and adds rows for each pending
reminder.  This means that `TokenProcessor` can fetch batched data.# On
branch master-action-batch

Comments: This part of a longer plot to simplify the `CRM_*_Tokens`.
Currently, `CRM_*_Tokens` implements duplicate prefetch mechanisms -- the
general-purpose `prefetch()` and the special-purpose
`alterActionScheduleQuery()`.  This patch makes it possible to use
`prefetch()` for all the real work of prefetching (and phasing-out
`alterActionScheduleQuery()`).

CRM/Core/BAO/ActionSchedule.php
Civi/Token/TokenProcessor.php

index 014978230e83c6046649024456a0a3a206005c63..5821fced0653a95488f549f0b8be8c41d52de5e7 100644 (file)
@@ -267,9 +267,8 @@ FROM civicrm_action_schedule cas
       );
 
       $multilingual = CRM_Core_I18n::isMultilingual();
+      $tokenProcessor = self::createTokenProcessor($actionSchedule, $mapping);
       while ($dao->fetch()) {
-        $errors = [];
-        $tokenProcessor = self::createTokenProcessor($actionSchedule, $mapping);
         $row = $tokenProcessor->addRow()
           ->context('contactId', $dao->contactID)
           ->context('actionSearchResult', (object) $dao->toArray());
@@ -279,27 +278,31 @@ FROM civicrm_action_schedule cas
           $preferred_language = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $dao->contactID, 'preferred_language');
           $row->context('locale', CRM_Core_BAO_ActionSchedule::pickLocale($actionSchedule->communication_language, $preferred_language));
         }
+      }
 
-        foreach ($tokenProcessor->evaluate()->getRows() as $tokenRow) {
-          // It's possible, eg, that sendReminderEmail fires Hook::alterMailParams() and that some listener use ts().
-          $swapLocale = empty($row->context['locale']) ? NULL : \CRM_Utils_AutoClean::swapLocale($row->context['locale']);
+      $tokenProcessor->evaluate();
+      foreach ($tokenProcessor->getRows() as $tokenRow) {
+        $dao = $tokenRow->context['actionSearchResult'];
+        $errors = [];
 
-          if ($actionSchedule->mode === 'SMS' || $actionSchedule->mode === 'User_Preference') {
-            CRM_Utils_Array::extend($errors, self::sendReminderSms($tokenRow, $actionSchedule, $dao->contactID));
-          }
+        // It's possible, eg, that sendReminderEmail fires Hook::alterMailParams() and that some listener use ts().
+        $swapLocale = empty($row->context['locale']) ? NULL : \CRM_Utils_AutoClean::swapLocale($row->context['locale']);
 
-          if ($actionSchedule->mode === 'Email' || $actionSchedule->mode === 'User_Preference') {
-            CRM_Utils_Array::extend($errors, self::sendReminderEmail($tokenRow, $actionSchedule, $dao->contactID));
-          }
-          // insert activity log record if needed
-          if ($actionSchedule->record_activity && empty($errors)) {
-            $caseID = empty($dao->case_id) ? NULL : $dao->case_id;
-            CRM_Core_BAO_ActionSchedule::createMailingActivity($tokenRow, $mapping, $dao->contactID, $dao->entityID, $caseID);
-          }
+        if ($actionSchedule->mode === 'SMS' || $actionSchedule->mode === 'User_Preference') {
+          CRM_Utils_Array::extend($errors, self::sendReminderSms($tokenRow, $actionSchedule, $dao->contactID));
+        }
 
-          unset($swapLocale);
+        if ($actionSchedule->mode === 'Email' || $actionSchedule->mode === 'User_Preference') {
+          CRM_Utils_Array::extend($errors, self::sendReminderEmail($tokenRow, $actionSchedule, $dao->contactID));
+        }
+        // insert activity log record if needed
+        if ($actionSchedule->record_activity && empty($errors)) {
+          $caseID = empty($dao->case_id) ? NULL : $dao->case_id;
+          CRM_Core_BAO_ActionSchedule::createMailingActivity($tokenRow, $mapping, $dao->contactID, $dao->entityID, $caseID);
         }
 
+        unset($swapLocale);
+
         // update action log record
         $logParams = [
           'id' => $dao->reminderID,
index 7ad2ec7b58429f5235be9202145b886f1fa33dc6..d434a1a13919e1c95e2786c39c4760f8783a8049 100644 (file)
@@ -256,7 +256,7 @@ class TokenProcessor {
    *   Each row is presented with a fluent, OOP facade.
    */
   public function getRows() {
-    return new TokenRowIterator($this, new \ArrayIterator($this->rowContexts));
+    return new TokenRowIterator($this, new \ArrayIterator($this->rowContexts ?: []));
   }
 
   /**