From: eileen Date: Sat, 28 Jul 2018 04:38:28 +0000 (+1200) Subject: Convert relationships to pseudonames at the point of loading X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=ce12a9e0288b5b8cc6cb4cc95331509a6133c060;p=civicrm-core.git Convert relationships to pseudonames at the point of loading --- diff --git a/CRM/Export/BAO/Export.php b/CRM/Export/BAO/Export.php index fec624e51f..3f7f906aa0 100644 --- a/CRM/Export/BAO/Export.php +++ b/CRM/Export/BAO/Export.php @@ -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); } /** diff --git a/CRM/Export/BAO/ExportProcessor.php b/CRM/Export/BAO/ExportProcessor.php index 026befc133..0b77a52eda 100644 --- a/CRM/Export/BAO/ExportProcessor.php +++ b/CRM/Export/BAO/ExportProcessor.php @@ -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. * diff --git a/tests/phpunit/CRM/Export/BAO/ExportTest.php b/tests/phpunit/CRM/Export/BAO/ExportTest.php index cea24c8dca..985c434d3c 100644 --- a/tests/phpunit/CRM/Export/BAO/ExportTest.php +++ b/tests/phpunit/CRM/Export/BAO/ExportTest.php @@ -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. *