Merge pull request #14577 from mattwire/type_consolidation
[civicrm-core.git] / CRM / Export / BAO / Export.php
index 207fea22189f56e4c8a3f0328ac4b70d383f7029..a95df716d8f73142bfe454d3668ead18e346a5e0 100644 (file)
@@ -41,125 +41,6 @@ class CRM_Export_BAO_Export {
   // CRM-7675
   const EXPORT_ROW_COUNT = 100000;
 
-  /**
-   * Get default return property for export based on mode
-   *
-   * @param int $exportMode
-   *   Export mode.
-   *
-   * @return string
-   *   Default Return property
-   */
-  public static function defaultReturnProperty($exportMode) {
-    // hack to add default return property based on export mode
-    $property = NULL;
-    if ($exportMode == CRM_Export_Form_Select::CONTRIBUTE_EXPORT) {
-      $property = 'contribution_id';
-    }
-    elseif ($exportMode == CRM_Export_Form_Select::EVENT_EXPORT) {
-      $property = 'participant_id';
-    }
-    elseif ($exportMode == CRM_Export_Form_Select::MEMBER_EXPORT) {
-      $property = 'membership_id';
-    }
-    elseif ($exportMode == CRM_Export_Form_Select::PLEDGE_EXPORT) {
-      $property = 'pledge_id';
-    }
-    elseif ($exportMode == CRM_Export_Form_Select::CASE_EXPORT) {
-      $property = 'case_id';
-    }
-    elseif ($exportMode == CRM_Export_Form_Select::GRANT_EXPORT) {
-      $property = 'grant_id';
-    }
-    elseif ($exportMode == CRM_Export_Form_Select::ACTIVITY_EXPORT) {
-      $property = 'activity_id';
-    }
-    return $property;
-  }
-
-  /**
-   * Get Export component
-   *
-   * @param int $exportMode
-   *   Export mode.
-   *
-   * @return string
-   *   CiviCRM Export Component
-   */
-  public static function exportComponent($exportMode) {
-    switch ($exportMode) {
-      case CRM_Export_Form_Select::CONTRIBUTE_EXPORT:
-        $component = 'civicrm_contribution';
-        break;
-
-      case CRM_Export_Form_Select::EVENT_EXPORT:
-        $component = 'civicrm_participant';
-        break;
-
-      case CRM_Export_Form_Select::MEMBER_EXPORT:
-        $component = 'civicrm_membership';
-        break;
-
-      case CRM_Export_Form_Select::PLEDGE_EXPORT:
-        $component = 'civicrm_pledge';
-        break;
-
-      case CRM_Export_Form_Select::GRANT_EXPORT:
-        $component = 'civicrm_grant';
-        break;
-    }
-    return $component;
-  }
-
-  /**
-   * Get Query Group By Clause
-   * @param \CRM_Export_BAO_ExportProcessor $processor
-   *   Export Mode
-   * @param array $returnProperties
-   *   Return Properties
-   * @param object $query
-   *   CRM_Contact_BAO_Query
-   *
-   * @return string
-   *   Group By Clause
-   */
-  public static function getGroupBy($processor, $returnProperties, $query) {
-    $groupBy = NULL;
-    $exportMode = $processor->getExportMode();
-    $queryMode = $processor->getQueryMode();
-    if (!empty($returnProperties['tags']) || !empty($returnProperties['groups']) ||
-      CRM_Utils_Array::value('notes', $returnProperties) ||
-      // CRM-9552
-      ($queryMode & CRM_Contact_BAO_Query::MODE_CONTACTS && $query->_useGroupBy)
-    ) {
-      $groupBy = "contact_a.id";
-    }
-
-    switch ($exportMode) {
-      case CRM_Export_Form_Select::CONTRIBUTE_EXPORT:
-        $groupBy = 'civicrm_contribution.id';
-        if (CRM_Contribute_BAO_Query::isSoftCreditOptionEnabled()) {
-          // especial group by  when soft credit columns are included
-          $groupBy = ['contribution_search_scredit_combined.id', 'contribution_search_scredit_combined.scredit_id'];
-        }
-        break;
-
-      case CRM_Export_Form_Select::EVENT_EXPORT:
-        $groupBy = 'civicrm_participant.id';
-        break;
-
-      case CRM_Export_Form_Select::MEMBER_EXPORT:
-        $groupBy = "civicrm_membership.id";
-        break;
-    }
-
-    if ($queryMode & CRM_Contact_BAO_Query::MODE_ACTIVITY) {
-      $groupBy = "civicrm_activity.id ";
-    }
-
-    return $groupBy ? ' GROUP BY ' . implode(', ', (array) $groupBy) : '';
-  }
-
   /**
    * Get the list the export fields.
    *
@@ -189,9 +70,6 @@ class CRM_Export_BAO_Export {
    * @param array $exportParams
    * @param string $queryOperator
    *
-   * @return array|null
-   *   An array can be requested from within a unit test.
-   *
    * @throws \CRM_Core_Exception
    */
   public static function exportComponents(
@@ -215,193 +93,41 @@ class CRM_Export_BAO_Export {
       $exportParams['postal_mailing_export']['postal_mailing_export'] == 1
     );
 
-    $processor = new CRM_Export_BAO_ExportProcessor($exportMode, $fields, $queryOperator, $mergeSameHousehold, $isPostalOnly);
-    $returnProperties = [];
-
-    if ($fields) {
-      foreach ($fields as $key => $value) {
-        $fieldName = CRM_Utils_Array::value(1, $value);
-        if (!$fieldName || $processor->isHouseholdMergeRelationshipTypeKey($fieldName)) {
-          continue;
-        }
-
-        if ($processor->isRelationshipTypeKey($fieldName) && (!empty($value[2]) || !empty($value[4]))) {
-          $returnProperties[$fieldName] = $processor->setRelationshipReturnProperties($value, $fieldName);
-        }
-        elseif (is_numeric(CRM_Utils_Array::value(2, $value))) {
-          $locationName = CRM_Core_PseudoConstant::getName('CRM_Core_BAO_Address', 'location_type_id', $value[2]);
-          if ($fieldName == 'phone') {
-            $returnProperties['location'][$locationName]['phone-' . CRM_Utils_Array::value(3, $value)] = 1;
-          }
-          elseif ($fieldName == 'im') {
-            $returnProperties['location'][$locationName]['im-' . CRM_Utils_Array::value(3, $value)] = 1;
-          }
-          else {
-            $returnProperties['location'][$locationName][$fieldName] = 1;
-          }
-        }
-        else {
-          //hack to fix component fields
-          //revert mix of event_id and title
-          if ($fieldName == 'event_id') {
-            $returnProperties['event_id'] = 1;
-          }
-          else {
-            $returnProperties[$fieldName] = 1;
-          }
-        }
-      }
-      $defaultExportMode = self::defaultReturnProperty($exportMode);
-      if ($defaultExportMode) {
-        $returnProperties[$defaultExportMode] = 1;
-      }
-    }
-    else {
-      $returnProperties = $processor->getDefaultReturnProperties();
-    }
-    // @todo - we are working towards this being entirely a property of the processor
-    $processor->setReturnProperties($returnProperties);
-    $paymentTableId = $processor->getPaymentTableID();
-
-    if ($mergeSameAddress) {
-      //make sure the addressee fields are selected
-      //while using merge same address feature
-      $returnProperties['addressee'] = 1;
-      $returnProperties['postal_greeting'] = 1;
-      $returnProperties['email_greeting'] = 1;
-      $returnProperties['street_name'] = 1;
-      $returnProperties['household_name'] = 1;
-      $returnProperties['street_address'] = 1;
-      $returnProperties['city'] = 1;
-      $returnProperties['state_province'] = 1;
-
-      // some columns are required for assistance incase they are not already present
-      $exportParams['merge_same_address']['temp_columns'] = [];
-      $tempColumns = ['id', 'master_id', 'state_province_id', 'postal_greeting_id', 'addressee_id'];
-      foreach ($tempColumns as $column) {
-        if (!array_key_exists($column, $returnProperties)) {
-          $returnProperties[$column] = 1;
-          $column = $column == 'id' ? 'civicrm_primary_id' : $column;
-          $exportParams['merge_same_address']['temp_columns'][$column] = 1;
-        }
-      }
-    }
-
     if (!$selectAll && $componentTable && !empty($exportParams['additional_group'])) {
       // If an Additional Group is selected, then all contacts in that group are
       // added to the export set (filtering out duplicates).
-      $query = "
-INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_contact gc WHERE gc.group_id = {$exportParams['additional_group']} ON DUPLICATE KEY UPDATE {$componentTable}.contact_id = gc.contact_id";
-      CRM_Core_DAO::executeQuery($query);
-    }
-
-    if ($moreReturnProperties) {
-      // fix for CRM-7066
-      if (!empty($moreReturnProperties['group'])) {
-        unset($moreReturnProperties['group']);
-        $moreReturnProperties['groups'] = 1;
-      }
-      $returnProperties = array_merge($returnProperties, $moreReturnProperties);
-    }
-
-    $exportParams['postal_mailing_export']['temp_columns'] = [];
-    if ($exportParams['exportOption'] == 2 &&
-      isset($exportParams['postal_mailing_export']) &&
-      CRM_Utils_Array::value('postal_mailing_export', $exportParams['postal_mailing_export']) == 1
-    ) {
-      $postalColumns = ['is_deceased', 'do_not_mail', 'street_address', 'supplemental_address_1'];
-      foreach ($postalColumns as $column) {
-        if (!array_key_exists($column, $returnProperties)) {
-          $returnProperties[$column] = 1;
-          $exportParams['postal_mailing_export']['temp_columns'][$column] = 1;
-        }
-      }
+      // Really - the calling function could do this ... just saying
+      // @todo take a whip to the calling function.
+      CRM_Core_DAO::executeQuery("
+INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_contact gc WHERE gc.group_id = {$exportParams['additional_group']} ON DUPLICATE KEY UPDATE {$componentTable}.contact_id = gc.contact_id"
+      );
     }
-
     // rectify params to what proximity search expects if there is a value for prox_distance
     // CRM-7021
+    // @todo - move this back to the calling functions
     if (!empty($params)) {
       CRM_Contact_BAO_ProximityQuery::fixInputParams($params);
     }
-
-    list($query, $select, $from, $where, $having) = $processor->runQuery($params, $order, $returnProperties);
-
-    if ($mergeSameHousehold == 1) {
-      if (empty($returnProperties['id'])) {
-        $returnProperties['id'] = 1;
-      }
-
-      $processor->setHouseholdMergeReturnProperties(array_diff_key($returnProperties, array_fill_keys(['location_type', 'im_provider'], 1)));
+    // @todo everything from this line up should go back to the calling functions.
+    $processor = new CRM_Export_BAO_ExportProcessor($exportMode, $fields, $queryOperator, $mergeSameHousehold, $isPostalOnly, $mergeSameAddress, $exportParams);
+    if ($moreReturnProperties) {
+      $processor->setAdditionalRequestedReturnProperties($moreReturnProperties);
     }
+    $processor->setComponentTable($componentTable);
+    $processor->setComponentClause($componentClause);
+    $processor->setIds($ids);
+
+    list($query, $queryString) = $processor->runQuery($params, $order);
 
     // This perhaps only needs calling when $mergeSameHousehold == 1
     self::buildRelatedContactArray($selectAll, $ids, $processor, $componentTable);
 
-    // make sure the groups stuff is included only if specifically specified
-    // by the fields param (CRM-1969), else we limit the contacts outputted to only
-    // ones that are part of a group
-    if (!empty($returnProperties['groups'])) {
-      $oldClause = "( contact_a.id = civicrm_group_contact.contact_id )";
-      $newClause = " ( $oldClause AND ( civicrm_group_contact.status = 'Added' OR civicrm_group_contact.status IS NULL ) )";
-      // total hack for export, CRM-3618
-      $from = str_replace($oldClause,
-        $newClause,
-        $from
-      );
-    }
-
-    $whereClauses = ['trash_clause' => "contact_a.is_deleted != 1"];
-    if (!$selectAll && $componentTable) {
-      $from .= " INNER JOIN $componentTable ctTable ON ctTable.contact_id = contact_a.id ";
-    }
-    elseif ($componentClause) {
-      $whereClauses[] = $componentClause;
-    }
-
-    // CRM-13982 - check if is deleted
-    foreach ($params as $value) {
-      if ($value[0] == 'contact_is_deleted') {
-        unset($whereClauses['trash_clause']);
-      }
-    }
-
-    if (empty($where)) {
-      $where = "WHERE " . implode(' AND ', $whereClauses);
-    }
-    else {
-      $where .= " AND " . implode(' AND ', $whereClauses);
-    }
-
-    $queryString = "$select $from $where $having";
-
-    $groupBy = self::getGroupBy($processor, $returnProperties, $query);
-
-    $queryString .= $groupBy;
-
-    if ($order) {
-      // always add contact_a.id to the ORDER clause
-      // so the order is deterministic
-      //CRM-15301
-      if (strpos('contact_a.id', $order) === FALSE) {
-        $order .= ", contact_a.id";
-      }
-
-      list($field, $dir) = explode(' ', $order, 2);
-      $field = trim($field);
-      if (!empty($returnProperties[$field])) {
-        //CRM-15301
-        $queryString .= " ORDER BY $order";
-      }
-    }
-
     $addPaymentHeader = FALSE;
 
-    list($outputColumns, $metadata) = self::getExportStructureArrays($returnProperties, $processor);
+    list($outputColumns, $metadata) = $processor->getExportStructureArrays();
 
-    if (!empty($exportParams['merge_same_address']['temp_columns'])) {
-      // @todo - this is a temp fix  - ideally later we don't set stuff only to unset it.
-      // test exists covering this...
-      foreach (array_keys($exportParams['merge_same_address']['temp_columns']) as $field) {
+    if ($processor->isMergeSameAddress()) {
+      foreach (array_keys($processor->getAdditionalFieldsForSameAddressMerge()) as $field) {
         $processor->setColumnAsCalculationOnly($field);
       }
     }
@@ -432,9 +158,8 @@ INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_c
 
     $count = -1;
 
-    $headerRows = $processor->getHeaderRows();
     $sqlColumns = $processor->getSQLColumns();
-    $processor->setTemporaryTable(self::createTempTable($sqlColumns));
+    $processor->createTempTable();
     $limitReached = FALSE;
 
     while (!$limitReached) {
@@ -449,7 +174,7 @@ INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_c
       while ($iterationDAO->fetch()) {
         $count++;
         $rowsThisIteration++;
-        $row = $processor->buildRow($query, $iterationDAO, $outputColumns, $metadata, $paymentDetails, $addPaymentHeader, $paymentTableId);
+        $row = $processor->buildRow($query, $iterationDAO, $outputColumns, $metadata, $paymentDetails, $addPaymentHeader);
         if ($row === FALSE) {
           continue;
         }
@@ -471,30 +196,14 @@ INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_c
     }
 
     if ($processor->getTemporaryTable()) {
-      self::writeDetailsToTable($processor, $componentDetails, $sqlColumns);
+      self::writeDetailsToTable($processor, $componentDetails);
 
       // do merge same address and merge same household processing
       if ($mergeSameAddress) {
-        self::mergeSameAddress($processor, $sqlColumns, $exportParams);
+        $processor->mergeSameAddress();
       }
 
-      // call export hook
-      $table = $processor->getTemporaryTable();
-      CRM_Utils_Hook::export($table, $headerRows, $sqlColumns, $exportMode, $componentTable, $ids);
-      if ($table !== $processor->getTemporaryTable()) {
-        CRM_Core_Error::deprecatedFunctionWarning('altering the export table in the hook is deprecated (in some flows the table itself will be)');
-        $processor->setTemporaryTable($table);
-      }
-
-      // In order to be able to write a unit test against this function we need to suppress
-      // the csv writing. In future hopefully the csv writing & the main processing will be in separate functions.
-      if (empty($exportParams['suppress_csv_for_testing'])) {
-        self::writeCSVFromTable($headerRows, $sqlColumns, $processor);
-      }
-      else {
-        // return tableName sqlColumns headerRows in test context
-        return [$processor->getTemporaryTable(), $sqlColumns, $headerRows, $processor];
-      }
+      $processor->writeCSVFromTable();
 
       // delete the export temp table and component table
       $sql = "DROP TABLE IF EXISTS " . $processor->getTemporaryTable();
@@ -601,9 +310,8 @@ INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_c
   /**
    * @param \CRM_Export_BAO_ExportProcessor $processor
    * @param $details
-   * @param $sqlColumns
    */
-  public static function writeDetailsToTable($processor, $details, $sqlColumns) {
+  public static function writeDetailsToTable($processor, $details) {
     $tableName = $processor->getTemporaryTable();
     if (empty($details)) {
       return;
@@ -634,7 +342,7 @@ FROM   $tableName
       }
       $sqlClause[] = '(' . implode(',', $valueString) . ')';
     }
-    $sqlColumns = array_merge(['id' => 1], $sqlColumns);
+    $sqlColumns = array_merge(['id' => 1], $processor->getSQLColumns());
     $sqlColumnString = '(' . implode(',', array_keys($sqlColumns)) . ')';
 
     $sqlValueString = implode(",\n", $sqlClause);
@@ -646,587 +354,6 @@ VALUES $sqlValueString
     CRM_Core_DAO::executeQuery($sql);
   }
 
-  /**
-   * @param $sqlColumns
-   *
-   * @return string
-   */
-  public static function createTempTable($sqlColumns) {
-    //creating a temporary table for the search result that need be exported
-    $exportTempTable = CRM_Utils_SQL_TempTable::build()->setDurable()->setCategory('export');
-
-    // also create the sql table
-    $exportTempTable->drop();
-
-    $sql = " id int unsigned NOT NULL AUTO_INCREMENT, ";
-    if (!empty($sqlColumns)) {
-      $sql .= implode(",\n", array_values($sqlColumns)) . ',';
-    }
-
-    $sql .= "\n PRIMARY KEY ( id )";
-
-    // add indexes for street_address and household_name if present
-    $addIndices = [
-      'street_address',
-      'household_name',
-      'civicrm_primary_id',
-    ];
-
-    foreach ($addIndices as $index) {
-      if (isset($sqlColumns[$index])) {
-        $sql .= ",
-  INDEX index_{$index}( $index )
-";
-      }
-    }
-
-    $exportTempTable->createWithColumns($sql);
-    return $exportTempTable->getName();
-  }
-
-  /**
-   * @param \CRM_Export_BAO_ExportProcessor $processor
-   * @param $sqlColumns
-   * @param array $exportParams
-   */
-  public static function mergeSameAddress($processor, &$sqlColumns, $exportParams) {
-    $tableName = $processor->getTemporaryTable();
-    // check if any records are present based on if they have used shared address feature,
-    // and not based on if city / state .. matches.
-    $sql = "
-SELECT    r1.id                 as copy_id,
-          r1.civicrm_primary_id as copy_contact_id,
-          r1.addressee          as copy_addressee,
-          r1.addressee_id       as copy_addressee_id,
-          r1.postal_greeting    as copy_postal_greeting,
-          r1.postal_greeting_id as copy_postal_greeting_id,
-          r2.id                 as master_id,
-          r2.civicrm_primary_id as master_contact_id,
-          r2.postal_greeting    as master_postal_greeting,
-          r2.postal_greeting_id as master_postal_greeting_id,
-          r2.addressee          as master_addressee,
-          r2.addressee_id       as master_addressee_id
-FROM      $tableName r1
-INNER JOIN civicrm_address adr ON r1.master_id   = adr.id
-INNER JOIN $tableName      r2  ON adr.contact_id = r2.civicrm_primary_id
-ORDER BY  r1.id";
-    $linkedMerge = self::_buildMasterCopyArray($sql, $exportParams, TRUE);
-
-    // find all the records that have the same street address BUT not in a household
-    // require match on city and state as well
-    $sql = "
-SELECT    r1.id                 as master_id,
-          r1.civicrm_primary_id as master_contact_id,
-          r1.postal_greeting    as master_postal_greeting,
-          r1.postal_greeting_id as master_postal_greeting_id,
-          r1.addressee          as master_addressee,
-          r1.addressee_id       as master_addressee_id,
-          r2.id                 as copy_id,
-          r2.civicrm_primary_id as copy_contact_id,
-          r2.postal_greeting    as copy_postal_greeting,
-          r2.postal_greeting_id as copy_postal_greeting_id,
-          r2.addressee          as copy_addressee,
-          r2.addressee_id       as copy_addressee_id
-FROM      $tableName r1
-LEFT JOIN $tableName r2 ON ( r1.street_address = r2.street_address AND
-               r1.city = r2.city AND
-               r1.state_province_id = r2.state_province_id )
-WHERE     ( r1.household_name IS NULL OR r1.household_name = '' )
-AND       ( r2.household_name IS NULL OR r2.household_name = '' )
-AND       ( r1.street_address != '' )
-AND       r2.id > r1.id
-ORDER BY  r1.id
-";
-    $merge = self::_buildMasterCopyArray($sql, $exportParams);
-
-    // unset ids from $merge already present in $linkedMerge
-    foreach ($linkedMerge as $masterID => $values) {
-      $keys = [$masterID];
-      $keys = array_merge($keys, array_keys($values['copy']));
-      foreach ($merge as $mid => $vals) {
-        if (in_array($mid, $keys)) {
-          unset($merge[$mid]);
-        }
-        else {
-          foreach ($values['copy'] as $copyId) {
-            if (in_array($copyId, $keys)) {
-              unset($merge[$mid]['copy'][$copyId]);
-            }
-          }
-        }
-      }
-    }
-    $merge = $merge + $linkedMerge;
-
-    foreach ($merge as $masterID => $values) {
-      $sql = "
-UPDATE $tableName
-SET    addressee = %1, postal_greeting = %2, email_greeting = %3
-WHERE  id = %4
-";
-      $params = [
-        1 => [$values['addressee'], 'String'],
-        2 => [$values['postalGreeting'], 'String'],
-        3 => [$values['emailGreeting'], 'String'],
-        4 => [$masterID, 'Integer'],
-      ];
-      CRM_Core_DAO::executeQuery($sql, $params);
-
-      // delete all copies
-      $deleteIDs = array_keys($values['copy']);
-      $deleteIDString = implode(',', $deleteIDs);
-      $sql = "
-DELETE FROM $tableName
-WHERE  id IN ( $deleteIDString )
-";
-      CRM_Core_DAO::executeQuery($sql);
-    }
-
-    // unset temporary columns that were added for postal mailing format
-    // @todo - this part is pretty close to ready to be removed....
-    if (!empty($exportParams['merge_same_address']['temp_columns'])) {
-      $unsetKeys = array_keys($sqlColumns);
-      foreach ($unsetKeys as $headerKey => $sqlColKey) {
-        if (array_key_exists($sqlColKey, $exportParams['merge_same_address']['temp_columns'])) {
-          unset($sqlColumns[$sqlColKey]);
-        }
-      }
-    }
-  }
-
-  /**
-   * @param int $contactId
-   * @param array $exportParams
-   *
-   * @return array
-   */
-  public static function _replaceMergeTokens($contactId, $exportParams) {
-    $greetings = [];
-    $contact = NULL;
-
-    $greetingFields = [
-      'postal_greeting',
-      'addressee',
-    ];
-    foreach ($greetingFields as $greeting) {
-      if (!empty($exportParams[$greeting])) {
-        $greetingLabel = $exportParams[$greeting];
-        if (empty($contact)) {
-          $values = [
-            'id' => $contactId,
-            'version' => 3,
-          ];
-          $contact = civicrm_api('contact', 'get', $values);
-
-          if (!empty($contact['is_error'])) {
-            return $greetings;
-          }
-          $contact = $contact['values'][$contact['id']];
-        }
-
-        $tokens = ['contact' => $greetingLabel];
-        $greetings[$greeting] = CRM_Utils_Token::replaceContactTokens($greetingLabel, $contact, NULL, $tokens);
-      }
-    }
-    return $greetings;
-  }
-
-  /**
-   * The function unsets static part of the string, if token is the dynamic part.
-   *
-   * Example: 'Hello {contact.first_name}' => converted to => '{contact.first_name}'
-   * i.e 'Hello Alan' => converted to => 'Alan'
-   *
-   * @param string $parsedString
-   * @param string $defaultGreeting
-   * @param bool $addressMergeGreetings
-   * @param string $greetingType
-   *
-   * @return mixed
-   */
-  public static function _trimNonTokens(
-    &$parsedString, $defaultGreeting,
-    $addressMergeGreetings, $greetingType = 'postal_greeting'
-  ) {
-    if (!empty($addressMergeGreetings[$greetingType])) {
-      $greetingLabel = $addressMergeGreetings[$greetingType];
-    }
-    $greetingLabel = empty($greetingLabel) ? $defaultGreeting : $greetingLabel;
-
-    $stringsToBeReplaced = preg_replace('/(\{[a-zA-Z._ ]+\})/', ';;', $greetingLabel);
-    $stringsToBeReplaced = explode(';;', $stringsToBeReplaced);
-    foreach ($stringsToBeReplaced as $key => $string) {
-      // to keep one space
-      $stringsToBeReplaced[$key] = ltrim($string);
-    }
-    $parsedString = str_replace($stringsToBeReplaced, "", $parsedString);
-
-    return $parsedString;
-  }
-
-  /**
-   * @param $sql
-   * @param array $exportParams
-   * @param bool $sharedAddress
-   *
-   * @return array
-   */
-  public static function _buildMasterCopyArray($sql, $exportParams, $sharedAddress = FALSE) {
-    static $contactGreetingTokens = [];
-
-    $addresseeOptions = CRM_Core_OptionGroup::values('addressee');
-    $postalOptions = CRM_Core_OptionGroup::values('postal_greeting');
-
-    $merge = $parents = [];
-    $dao = CRM_Core_DAO::executeQuery($sql);
-
-    while ($dao->fetch()) {
-      $masterID = $dao->master_id;
-      $copyID = $dao->copy_id;
-      $masterPostalGreeting = $dao->master_postal_greeting;
-      $masterAddressee = $dao->master_addressee;
-      $copyAddressee = $dao->copy_addressee;
-
-      if (!$sharedAddress) {
-        if (!isset($contactGreetingTokens[$dao->master_contact_id])) {
-          $contactGreetingTokens[$dao->master_contact_id] = self::_replaceMergeTokens($dao->master_contact_id, $exportParams);
-        }
-        $masterPostalGreeting = CRM_Utils_Array::value('postal_greeting',
-          $contactGreetingTokens[$dao->master_contact_id], $dao->master_postal_greeting
-        );
-        $masterAddressee = CRM_Utils_Array::value('addressee',
-          $contactGreetingTokens[$dao->master_contact_id], $dao->master_addressee
-        );
-
-        if (!isset($contactGreetingTokens[$dao->copy_contact_id])) {
-          $contactGreetingTokens[$dao->copy_contact_id] = self::_replaceMergeTokens($dao->copy_contact_id, $exportParams);
-        }
-        $copyPostalGreeting = CRM_Utils_Array::value('postal_greeting',
-          $contactGreetingTokens[$dao->copy_contact_id], $dao->copy_postal_greeting
-        );
-        $copyAddressee = CRM_Utils_Array::value('addressee',
-          $contactGreetingTokens[$dao->copy_contact_id], $dao->copy_addressee
-        );
-      }
-
-      if (!isset($merge[$masterID])) {
-        // check if this is an intermediate child
-        // this happens if there are 3 or more matches a,b, c
-        // the above query will return a, b / a, c / b, c
-        // we might be doing a bit more work, but for now its ok, unless someone
-        // knows how to fix the query above
-        if (isset($parents[$masterID])) {
-          $masterID = $parents[$masterID];
-        }
-        else {
-          $merge[$masterID] = [
-            'addressee' => $masterAddressee,
-            'copy' => [],
-            'postalGreeting' => $masterPostalGreeting,
-          ];
-          $merge[$masterID]['emailGreeting'] = &$merge[$masterID]['postalGreeting'];
-        }
-      }
-      $parents[$copyID] = $masterID;
-
-      if (!$sharedAddress && !array_key_exists($copyID, $merge[$masterID]['copy'])) {
-
-        if (!empty($exportParams['postal_greeting_other']) &&
-          count($merge[$masterID]['copy']) >= 1
-        ) {
-          // use static greetings specified if no of contacts > 2
-          $merge[$masterID]['postalGreeting'] = $exportParams['postal_greeting_other'];
-        }
-        elseif ($copyPostalGreeting) {
-          self::_trimNonTokens($copyPostalGreeting,
-            $postalOptions[$dao->copy_postal_greeting_id],
-            $exportParams
-          );
-          $merge[$masterID]['postalGreeting'] = "{$merge[$masterID]['postalGreeting']}, {$copyPostalGreeting}";
-          // if there happens to be a duplicate, remove it
-          $merge[$masterID]['postalGreeting'] = str_replace(" {$copyPostalGreeting},", "", $merge[$masterID]['postalGreeting']);
-        }
-
-        if (!empty($exportParams['addressee_other']) &&
-          count($merge[$masterID]['copy']) >= 1
-        ) {
-          // use static greetings specified if no of contacts > 2
-          $merge[$masterID]['addressee'] = $exportParams['addressee_other'];
-        }
-        elseif ($copyAddressee) {
-          self::_trimNonTokens($copyAddressee,
-            $addresseeOptions[$dao->copy_addressee_id],
-            $exportParams, 'addressee'
-          );
-          $merge[$masterID]['addressee'] = "{$merge[$masterID]['addressee']}, " . trim($copyAddressee);
-        }
-      }
-      $merge[$masterID]['copy'][$copyID] = $copyAddressee;
-    }
-
-    return $merge;
-  }
-
-  /**
-   * @param $headerRows
-   * @param $sqlColumns
-   * @param \CRM_Export_BAO_ExportProcessor $processor
-   */
-  public static function writeCSVFromTable($headerRows, $sqlColumns, $processor) {
-    $exportTempTable = $processor->getTemporaryTable();
-    $exportMode = $processor->getExportMode();
-    $writeHeader = TRUE;
-    $offset = 0;
-    $limit = self::EXPORT_ROW_COUNT;
-
-    $query = "SELECT * FROM $exportTempTable";
-
-    while (1) {
-      $limitQuery = $query . "
-LIMIT $offset, $limit
-";
-      $dao = CRM_Core_DAO::executeQuery($limitQuery);
-
-      if ($dao->N <= 0) {
-        break;
-      }
-
-      $componentDetails = [];
-      while ($dao->fetch()) {
-        $row = [];
-
-        foreach ($sqlColumns as $column => $dontCare) {
-          $row[$column] = $dao->$column;
-        }
-        $componentDetails[] = $row;
-      }
-      CRM_Core_Report_Excel::writeCSVFile($processor->getExportFileName(),
-        $headerRows,
-        $componentDetails,
-        NULL,
-        $writeHeader
-      );
-
-      $writeHeader = FALSE;
-      $offset += $limit;
-    }
-  }
-
-  /**
-   * Build componentPayment fields.
-   *
-   * This is no longer used by export but BAO_Mapping still calls it & we
-   * should find a generic way to handle this or move this to that class.
-   *
-   * @deprecated
-   */
-  public static function componentPaymentFields() {
-    static $componentPaymentFields;
-    if (!isset($componentPaymentFields)) {
-      $componentPaymentFields = [
-        'componentPaymentField_total_amount' => ts('Total Amount'),
-        'componentPaymentField_contribution_status' => ts('Contribution Status'),
-        'componentPaymentField_received_date' => ts('Date Received'),
-        'componentPaymentField_payment_instrument' => ts('Payment Method'),
-        'componentPaymentField_transaction_id' => ts('Transaction ID'),
-      ];
-    }
-    return $componentPaymentFields;
-  }
-
-  /**
-   * Get the various arrays that we use to structure our output.
-   *
-   * The extraction of these has been moved to a separate function for clarity and so that
-   * tests can be added - in particular on the $outputHeaders array.
-   *
-   * However it still feels a bit like something that I'm too polite to write down and this should be seen
-   * as a step on the refactoring path rather than how it should be.
-   *
-   * @param array $returnProperties
-   * @param \CRM_Export_BAO_ExportProcessor $processor
-   *
-   * @return array
-   *   - outputColumns Array of columns to be exported. The values don't matter but the key must match the
-   *   alias for the field generated by BAO_Query object.
-   *   - headerRows Array of the column header strings to put in the csv header - non-associative.
-   *   - sqlColumns Array of column names for the temp table. Not too sure why outputColumns can't be used here.
-   *   - metadata Array of fields with specific parameters to pass to the translate function or another hacky nasty solution
-   *    I'm too embarassed to discuss here.
-   *    The keys need
-   *    - to match the outputColumns keys (yes, the fact we ignore the output columns values & then pass another array with values
-   *    we could use does suggest further refactors. However, you future improver, do remember that every check you do
-   *    in the main DAO loop is done once per row & that coule be 100,000 times.)
-   *    Finally a pop quiz: We need the translate context because we use a function other than ts() - is this because
-   *    - a) the function used is more efficient or
-   *    - b) this code is old & outdated. Submit your answers to circular bin or better
-   *       yet find a way to comment them for posterity.
-   */
-  public static function getExportStructureArrays($returnProperties, $processor) {
-    $outputColumns = $metadata = [];
-    $queryFields = $processor->getQueryFields();
-    foreach ($returnProperties as $key => $value) {
-      if (($key != 'location' || !is_array($value)) && !$processor->isRelationshipTypeKey($key)) {
-        $outputColumns[$key] = $value;
-        $processor->addOutputSpecification($key);
-      }
-      elseif ($processor->isRelationshipTypeKey($key)) {
-        $outputColumns[$key] = $value;
-        foreach ($value as $relationField => $relationValue) {
-          // below block is same as primary block (duplicate)
-          if (isset($queryFields[$relationField]['title'])) {
-            $processor->addOutputSpecification($relationField, $key);
-          }
-          elseif (is_array($relationValue) && $relationField == 'location') {
-            // fix header for location type case
-            foreach ($relationValue as $ltype => $val) {
-              foreach (array_keys($val) as $fld) {
-                $type = explode('-', $fld);
-                $processor->addOutputSpecification($type[0], $key, $ltype, CRM_Utils_Array::value(1, $type));
-              }
-            }
-          }
-        }
-      }
-      else {
-        foreach ($value as $locationType => $locationFields) {
-          foreach (array_keys($locationFields) as $locationFieldName) {
-            $type = explode('-', $locationFieldName);
-
-            $actualDBFieldName = $type[0];
-            $daoFieldName = CRM_Utils_String::munge($locationType) . '-' . $actualDBFieldName;
-
-            if (!empty($type[1])) {
-              $daoFieldName .= "-" . $type[1];
-            }
-            $processor->addOutputSpecification($actualDBFieldName, NULL, $locationType, CRM_Utils_Array::value(1, $type));
-            $metadata[$daoFieldName] = $processor->getMetaDataForField($actualDBFieldName);
-            $outputColumns[$daoFieldName] = TRUE;
-          }
-        }
-      }
-    }
-    return [$outputColumns, $metadata];
-  }
-
-  /**
-   * Get the values of linked household contact.
-   *
-   * @param CRM_Core_DAO $relDAO
-   * @param array $value
-   * @param string $field
-   * @param array $row
-   */
-  private static function fetchRelationshipDetails($relDAO, $value, $field, &$row) {
-    $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id');
-    $imProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id');
-    $i18n = CRM_Core_I18n::singleton();
-    $field = $field . '_';
-
-    foreach ($value as $relationField => $relationValue) {
-      if (is_object($relDAO) && property_exists($relDAO, $relationField)) {
-        $fieldValue = $relDAO->$relationField;
-        if ($relationField == 'phone_type_id') {
-          $fieldValue = $phoneTypes[$relationValue];
-        }
-        elseif ($relationField == 'provider_id') {
-          $fieldValue = CRM_Utils_Array::value($relationValue, $imProviders);
-        }
-        // CRM-13995
-        elseif (is_object($relDAO) && in_array($relationField, [
-          'email_greeting',
-          'postal_greeting',
-          'addressee',
-        ])) {
-          //special case for greeting replacement
-          $fldValue = "{$relationField}_display";
-          $fieldValue = $relDAO->$fldValue;
-        }
-      }
-      elseif (is_object($relDAO) && $relationField == 'state_province') {
-        $fieldValue = CRM_Core_PseudoConstant::stateProvince($relDAO->state_province_id);
-      }
-      elseif (is_object($relDAO) && $relationField == 'country') {
-        $fieldValue = CRM_Core_PseudoConstant::country($relDAO->country_id);
-      }
-      else {
-        $fieldValue = '';
-      }
-      $relPrefix = $field . $relationField;
-
-      if (is_object($relDAO) && $relationField == 'id') {
-        $row[$relPrefix] = $relDAO->contact_id;
-      }
-      elseif (is_array($relationValue) && $relationField == 'location') {
-        foreach ($relationValue as $ltype => $val) {
-          // If the location name has a space in it the we need to handle that. This
-          // is kinda hacky but specifically covered in the ExportTest so later efforts to
-          // improve it should be secure in the knowled it will be caught.
-          $ltype = str_replace(' ', '_', $ltype);
-          foreach (array_keys($val) as $fld) {
-            $type = explode('-', $fld);
-            $fldValue = "{$ltype}-" . $type[0];
-            if (!empty($type[1])) {
-              $fldValue .= "-" . $type[1];
-            }
-            // CRM-3157: localise country, region (both have ‘country’ context)
-            // and state_province (‘province’ context)
-            switch (TRUE) {
-              case (!is_object($relDAO)):
-                $row[$field . '_' . $fldValue] = '';
-                break;
-
-              case in_array('country', $type):
-              case in_array('world_region', $type):
-                $row[$field . '_' . $fldValue] = $i18n->crm_translate($relDAO->$fldValue,
-                  ['context' => 'country']
-                );
-                break;
-
-              case in_array('state_province', $type):
-                $row[$field . '_' . $fldValue] = $i18n->crm_translate($relDAO->$fldValue,
-                  ['context' => 'province']
-                );
-                break;
-
-              default:
-                $row[$field . '_' . $fldValue] = $relDAO->$fldValue;
-                break;
-            }
-          }
-        }
-      }
-      elseif (isset($fieldValue) && $fieldValue != '') {
-        //check for custom data
-        if ($cfID = CRM_Core_BAO_CustomField::getKeyID($relationField)) {
-          $row[$relPrefix] = CRM_Core_BAO_CustomField::displayValue($fieldValue, $cfID);
-        }
-        else {
-          //normal relationship fields
-          // CRM-3157: localise country, region (both have ‘country’ context) and state_province (‘province’ context)
-          switch ($relationField) {
-            case 'country':
-            case 'world_region':
-              $row[$relPrefix] = $i18n->crm_translate($fieldValue, ['context' => 'country']);
-              break;
-
-            case 'state_province':
-              $row[$relPrefix] = $i18n->crm_translate($fieldValue, ['context' => 'province']);
-              break;
-
-            default:
-              $row[$relPrefix] = $fieldValue;
-              break;
-          }
-        }
-      }
-      else {
-        // if relation field is empty or null
-        $row[$relPrefix] = '';
-      }
-    }
-  }
-
   /**
    * Get the ids that we want to get related contact details for.
    *
@@ -1253,13 +380,19 @@ LIMIT $offset, $limit
       }
       return $relIDs;
     }
-    $component = self::exportComponent($exportMode);
+    $componentMapping = [
+      CRM_Export_Form_Select::CONTRIBUTE_EXPORT => 'civicrm_contribution',
+      CRM_Export_Form_Select::EVENT_EXPORT => 'civicrm_participant',
+      CRM_Export_Form_Select::MEMBER_EXPORT => 'civicrm_membership',
+      CRM_Export_Form_Select::PLEDGE_EXPORT => 'civicrm_pledge',
+      CRM_Export_Form_Select::GRANT_EXPORT => 'civicrm_grant',
+    ];
 
     if ($exportMode == CRM_Export_Form_Select::CASE_EXPORT) {
       return CRM_Case_BAO_Case::retrieveContactIdsByCaseId($ids);
     }
     else {
-      return CRM_Core_DAO::getContactIDsFromComponent($ids, $component);
+      return CRM_Core_DAO::getContactIDsFromComponent($ids, $componentMapping[$exportMode]);
     }
   }
 
@@ -1320,10 +453,10 @@ LIMIT $offset, $limit
         $relationQuery->convertToPseudoNames($allRelContactDAO);
         $row = [];
         // @todo pass processor to fetchRelationshipDetails and set fields directly within it.
-        self::fetchRelationshipDetails($allRelContactDAO, $relationReturnProperties, $relationshipKey, $row);
+        $processor->fetchRelationshipDetails($allRelContactDAO, $relationReturnProperties, $relationshipKey, $row);
         foreach (array_keys($relationReturnProperties) as $property) {
           if ($property === 'location') {
-            // @todo - simplify location in self::fetchRelationshipDetails - remove handling here. Or just call
+            // @todo - simplify location in fetchRelationshipDetails - remove handling here. Or just call
             // $processor->setRelationshipValue from fetchRelationshipDetails
             foreach ($relationReturnProperties['location'] as $locationName => $locationValues) {
               foreach (array_keys($locationValues) as $locationValue) {