Convert relationships to pseudonames at the point of loading
authoreileen <emcnaughton@wikimedia.org>
Sat, 28 Jul 2018 04:38:28 +0000 (16:38 +1200)
committereileen <emcnaughton@wikimedia.org>
Sat, 27 Oct 2018 02:51:11 +0000 (15:51 +1300)
CRM/Export/BAO/Export.php
CRM/Export/BAO/ExportProcessor.php
tests/phpunit/CRM/Export/BAO/ExportTest.php

index fec624e51fd6431b07cb02ebd0bcd75956c9f766..3f7f906aa0b8e2935d8877a6b958d6782d5e24e6 100644 (file)
@@ -357,11 +357,13 @@ INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_c
               $returnProperties[$householdRelationshipType][$key] = $value;
             }
           }
+          // @todo - don't use returnProperties above.
+          $processor->setHouseholdMergeReturnProperties($returnProperties[$householdRelationshipType]);
         }
       }
     }
 
-    list($relationQuery, $allRelContactArray) = self::buildRelatedContactArray($selectAll, $ids, $processor, $componentTable, $returnProperties);
+    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
@@ -510,9 +512,20 @@ INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_c
           }
 
           if ($processor->isRelationshipTypeKey($field)) {
-            $relDAO = CRM_Utils_Array::value($iterationDAO->contact_id, $allRelContactArray[$field]);
-            $relationQuery[$field]->convertToPseudoNames($relDAO);
-            self::fetchRelationshipDetails($relDAO, $value, $field, $row);
+            foreach (array_keys($value) as $property) {
+              if ($property === 'location') {
+                // @todo just undo all this nasty location wrangling!
+                foreach ($value['location'] as $locationKey => $locationFields) {
+                  foreach (array_keys($locationFields) as $locationField) {
+                    $fieldKey = str_replace(' ', '_', $locationKey . '-' . $locationField);
+                    $row[$field . '_' . $fieldKey] = $processor->getRelationshipValue($field, $iterationDAO->contact_id, $fieldKey);
+                  }
+                }
+              }
+              else {
+                $row[$field . '_' . $property] = $processor->getRelationshipValue($field, $iterationDAO->contact_id, $property);
+              }
+            }
           }
           else {
             $row[$field] = self::getTransformedFieldValue($field, $iterationDAO, $fieldValue, $i18n, $metadata, $paymentDetails, $processor);
@@ -1502,6 +1515,8 @@ WHERE  {$whereClause}";
     $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;
@@ -1532,7 +1547,6 @@ WHERE  {$whereClause}";
       else {
         $fieldValue = '';
       }
-      $field = $field . '_';
       $relPrefix = $field . $relationField;
 
       if (is_object($relDAO) && $relationField == 'id') {
@@ -1649,64 +1663,74 @@ WHERE  {$whereClause}";
    * @param $ids
    * @param \CRM_Export_BAO_ExportProcessor $processor
    * @param $componentTable
-   * @param $returnProperties
-   *
-   * @return array
    */
-  protected static function buildRelatedContactArray($selectAll, $ids, $processor, $componentTable, $returnProperties) {
+  protected static function buildRelatedContactArray($selectAll, $ids, $processor, $componentTable) {
     $allRelContactArray = $relationQuery = array();
     $queryMode = $processor->getQueryMode();
     $exportMode = $processor->getExportMode();
-    foreach (self::$relationshipTypes as $rel => $dnt) {
-      if ($relationReturnProperties = CRM_Utils_Array::value($rel, $returnProperties)) {
-        $allRelContactArray[$rel] = array();
-        // build Query for each relationship
-        $relationQuery[$rel] = new CRM_Contact_BAO_Query(NULL, $relationReturnProperties,
-          NULL, FALSE, FALSE, $queryMode
-        );
-        list($relationSelect, $relationFrom, $relationWhere, $relationHaving) = $relationQuery[$rel]->query();
-
-        list($id, $direction) = explode('_', $rel, 2);
-        // identify the relationship direction
-        $contactA = 'contact_id_a';
-        $contactB = 'contact_id_b';
-        if ($direction == 'b_a') {
-          $contactA = 'contact_id_b';
-          $contactB = 'contact_id_a';
-        }
-        $relIDs = self::getIDsForRelatedContact($ids, $exportMode);
 
-        $relationshipJoin = $relationshipClause = '';
-        if (!$selectAll && $componentTable) {
-          $relationshipJoin = " INNER JOIN {$componentTable} ctTable ON ctTable.contact_id = {$contactA}";
-        }
-        elseif (!empty($relIDs)) {
-          $relID = implode(',', $relIDs);
-          $relationshipClause = " AND crel.{$contactA} IN ( {$relID} )";
-        }
+    foreach ($processor->getRelationshipReturnProperties() as $relationshipKey => $relationReturnProperties) {
+      $allRelContactArray[$relationshipKey] = array();
+      // build Query for each relationship
+      $relationQuery = new CRM_Contact_BAO_Query(NULL, $relationReturnProperties,
+        NULL, FALSE, FALSE, $queryMode
+      );
+      list($relationSelect, $relationFrom, $relationWhere, $relationHaving) = $relationQuery->query();
+
+      list($id, $direction) = explode('_', $relationshipKey, 2);
+      // identify the relationship direction
+      $contactA = 'contact_id_a';
+      $contactB = 'contact_id_b';
+      if ($direction == 'b_a') {
+        $contactA = 'contact_id_b';
+        $contactB = 'contact_id_a';
+      }
+      $relIDs = self::getIDsForRelatedContact($ids, $exportMode);
+
+      $relationshipJoin = $relationshipClause = '';
+      if (!$selectAll && $componentTable) {
+        $relationshipJoin = " INNER JOIN {$componentTable} ctTable ON ctTable.contact_id = {$contactA}";
+      }
+      elseif (!empty($relIDs)) {
+        $relID = implode(',', $relIDs);
+        $relationshipClause = " AND crel.{$contactA} IN ( {$relID} )";
+      }
 
-        $relationFrom = " {$relationFrom}
-                INNER JOIN civicrm_relationship crel ON crel.{$contactB} = contact_a.id AND crel.relationship_type_id = {$id}
-                {$relationshipJoin} ";
-
-        //check for active relationship status only
-        $today = date('Ymd');
-        $relationActive = " AND (crel.is_active = 1 AND ( crel.end_date is NULL OR crel.end_date >= {$today} ) )";
-        $relationWhere = " WHERE contact_a.is_deleted = 0 {$relationshipClause} {$relationActive}";
-        $relationGroupBy = CRM_Contact_BAO_Query::getGroupByFromSelectColumns($relationQuery[$rel]->_select, "crel.{$contactA}");
-        $relationSelect = "{$relationSelect}, {$contactA} as refContact ";
-        $relationQueryString = "$relationSelect $relationFrom $relationWhere $relationHaving $relationGroupBy";
-
-        $allRelContactDAO = CRM_Core_DAO::executeQuery($relationQueryString);
-        while ($allRelContactDAO->fetch()) {
-          //FIX Me: Migrate this to table rather than array
-          // build the array of all related contacts
-          $allRelContactArray[$rel][$allRelContactDAO->refContact] = clone($allRelContactDAO);
+      $relationFrom = " {$relationFrom}
+              INNER JOIN civicrm_relationship crel ON crel.{$contactB} = contact_a.id AND crel.relationship_type_id = {$id}
+              {$relationshipJoin} ";
+
+      //check for active relationship status only
+      $today = date('Ymd');
+      $relationActive = " AND (crel.is_active = 1 AND ( crel.end_date is NULL OR crel.end_date >= {$today} ) )";
+      $relationWhere = " WHERE contact_a.is_deleted = 0 {$relationshipClause} {$relationActive}";
+      $relationGroupBy = CRM_Contact_BAO_Query::getGroupByFromSelectColumns($relationQuery->_select, "crel.{$contactA}");
+      $relationSelect = "{$relationSelect}, {$contactA} as refContact ";
+      $relationQueryString = "$relationSelect $relationFrom $relationWhere $relationHaving $relationGroupBy";
+
+      $allRelContactDAO = CRM_Core_DAO::executeQuery($relationQueryString);
+      while ($allRelContactDAO->fetch()) {
+        $relationQuery->convertToPseudoNames($allRelContactDAO);
+        $row = [];
+        // @todo pass processor to fetchRelationshipDetails and set fields directly within it.
+        self::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
+            // $processor->setRelationshipValue from fetchRelationshipDetails
+            foreach ($relationReturnProperties['location'] as $locationName => $locationValues) {
+              foreach (array_keys($locationValues) as $locationValue) {
+                $key = str_replace(' ', '_', $locationName) . '-' . $locationValue;
+                $processor->setRelationshipValue($relationshipKey, $allRelContactDAO->refContact, $key, $row[$relationshipKey . '__' . $key]);
+              }
+            }
+          }
+          else {
+            $processor->setRelationshipValue($relationshipKey, $allRelContactDAO->refContact, $property, $row[$relationshipKey . '_' . $property]);
+          }
         }
-        $allRelContactDAO->free();
       }
     }
-    return array($relationQuery, $allRelContactArray);
   }
 
   /**
index 026befc13373076bbf96260a0f553fca4bf75eec..0b77a52eda6e01de05a903991dc6b6c1812f4e50 100644 (file)
@@ -95,6 +95,21 @@ class CRM_Export_BAO_ExportProcessor {
    */
   protected $relationshipReturnProperties = [];
 
+  /**
+   * Get return properties by relationship.
+   * @return array
+   */
+  public function getRelationshipReturnProperties() {
+    return $this->relationshipReturnProperties;
+  }
+
+  /**
+   * Export values for related contacts.
+   *
+   * @var array
+   */
+  protected $relatedContactValues = [];
+
   /**
    * @var array
    */
@@ -131,7 +146,6 @@ class CRM_Export_BAO_ExportProcessor {
     $this->requestedFields = $requestedFields;
   }
 
-
   /**
    * @return array
    */
@@ -167,6 +181,41 @@ class CRM_Export_BAO_ExportProcessor {
     );
   }
 
+  /**
+   * Set the value for a relationship type field.
+   *
+   * In this case we are building up an array of properties for a related contact.
+   *
+   * These may be used for direct exporting or for merge to household depending on the
+   * options selected.
+   *
+   * @param string $relationshipType
+   * @param int $contactID
+   * @param string $field
+   * @param string $value
+   */
+  public function setRelationshipValue($relationshipType, $contactID, $field, $value) {
+    $this->relatedContactValues[$relationshipType][$contactID][$field] = $value;
+  }
+
+  /**
+   * Get the value for a relationship type field.
+   *
+   * In this case we are building up an array of properties for a related contact.
+   *
+   * These may be used for direct exporting or for merge to household depending on the
+   * options selected.
+   *
+   * @param string $relationshipType
+   * @param int $contactID
+   * @param string $field
+   *
+   * @return string
+   */
+  public function getRelationshipValue($relationshipType, $contactID, $field) {
+    return isset($this->relatedContactValues[$relationshipType][$contactID][$field]) ? $this->relatedContactValues[$relationshipType][$contactID][$field] : '';
+  }
+
   /**
    * @return bool
    */
@@ -567,6 +616,20 @@ class CRM_Export_BAO_ExportProcessor {
     return $this->relationshipReturnProperties[$relationshipKey];
   }
 
+  /**
+   * Add the main return properties to the household merge properties if needed for merging.
+   *
+   * If we are using household merge we need to add these to the relationship properties to
+   * be retrieved.
+   *
+   * @param $returnProperties
+   */
+  public function setHouseholdMergeReturnProperties($returnProperties) {
+    foreach ($this->getHouseholdRelationshipTypes() as $householdRelationshipType) {
+      $this->relationshipReturnProperties[$householdRelationshipType] = $returnProperties;
+    }
+  }
+
   /**
    * Get the default location fields to request.
    *
index cea24c8dcac36291d3c8d63c22beb59bdf42e629..985c434d3c15aa4b47d1ff40e37fb0bb6b4641f1 100644 (file)
@@ -419,6 +419,49 @@ class CRM_Export_BAO_ExportTest extends CiviUnitTestCase {
     $this->assertEquals('Big campaign,', CRM_Core_DAO::singleValueQuery("SELECT GROUP_CONCAT(contribution_campaign_title) FROM {$tableName}"));
   }
 
+  /**
+   * Test exporting relationships.
+   */
+  public function testExportRelationships() {
+    $organization1 = $this->organizationCreate(['organization_name' => 'Org 1', 'legal_name' => 'pretty legal']);
+    $organization2 = $this->organizationCreate(['organization_name' => 'Org 2', 'legal_name' => 'well dodgey']);
+    $contact1 = $this->individualCreate(['employer_id' => $organization1, 'first_name' => 'one']);
+    $contact2 = $this->individualCreate(['employer_id' => $organization2, 'first_name' => 'one']);
+    $employerRelationshipTypeID = $this->callAPISuccessGetValue('RelationshipType', ['return' => 'id', 'label_a_b' => 'Employee of']);
+    $selectedFields = [
+      ['Individual', 'first_name', ''],
+      ['Individual', $employerRelationshipTypeID . '_a_b', 'organization_name', ''],
+      ['Individual', $employerRelationshipTypeID . '_a_b', 'legal_name', ''],
+    ];
+    list($tableName, $sqlColumns, $headerRows) = CRM_Export_BAO_Export::exportComponents(
+      FALSE,
+      [$contact1, $contact2],
+      [],
+      NULL,
+      $selectedFields,
+      NULL,
+      CRM_Export_Form_Select::CONTACT_EXPORT,
+      "contact_a.id IN ( $contact1, $contact2 )",
+      NULL,
+      FALSE,
+      FALSE,
+      [
+        'exportOption' => CRM_Export_Form_Select::CONTACT_EXPORT,
+        'suppress_csv_for_testing' => TRUE,
+      ]
+    );
+
+    $dao = CRM_Core_DAO::executeQuery("SELECT * FROM {$tableName}");
+    $dao->fetch();
+    $this->assertEquals('one', $dao->first_name);
+    $this->assertEquals('Org 1', $dao->{$employerRelationshipTypeID . '_a_b_organization_name'});
+    $this->assertEquals('pretty legal', $dao->{$employerRelationshipTypeID . '_a_b_legal_name'});
+
+    $dao->fetch();
+    $this->assertEquals('Org 2', $dao->{$employerRelationshipTypeID . '_a_b_organization_name'});
+    $this->assertEquals('well dodgey', $dao->{$employerRelationshipTypeID . '_a_b_legal_name'});
+  }
+
   /**
    * Test exporting relationships.
    *