/** * Merge given set of contacts. Performs core operation. * * @param array $dupePairs * Set of pair of contacts for whom merge is to be done. * @param array $cacheParams * Prev-next-cache params based on which next pair of contacts are computed. * Generally used with batch-merge. * @param string $mode * Helps decide how to behave when there are conflicts. * A 'safe' value skips the merge if there are any un-resolved conflicts. * Does a force merge otherwise (aggressive mode). * @param bool $autoFlip to let api decide which contact to retain and which to delete. * Wether to let api decide which contact to retain and which to delete. * * * @param bool $redirectForPerformance * * @return array|bool */ public static function merge($dupePairs = array(), $cacheParams = array(), $mode = 'safe', $autoFlip = TRUE, $redirectForPerformance = FALSE) { $cacheKeyString = CRM_Utils_Array::value('cache_key_string', $cacheParams); $resultStats = array('merged' => array(), 'skipped' => array()); // we don't want dupe caching to get reset after every-merge, and therefore set the // doNotResetCache flag $config = CRM_Core_Config::singleton(); $config->doNotResetCache = 1; while (!empty($dupePairs)) { foreach ($dupePairs as $dupes) { CRM_Utils_Hook::merge('flip', $dupes, $dupes['dstID'], $dupes['srcID']); $mainId = $dupes['dstID']; $otherId = $dupes['srcID']; $isAutoFlip = CRM_Utils_Array::value('auto_flip', $dupes, $autoFlip); // if we can, make sure that $mainId is the one with lower id number if ($isAutoFlip && $mainId > $otherId) { $mainId = $dupes['srcID']; $otherId = $dupes['dstID']; } if (!$mainId || !$otherId) { // return error return FALSE; } // Generate var $migrationInfo. The variable structure is exactly same as // $formValues submitted during a UI merge for a pair of contacts. $rowsElementsAndInfo = CRM_Dedupe_Merger::getRowsElementsAndInfo($mainId, $otherId); $migrationInfo =& $rowsElementsAndInfo['migration_info']; // add additional details that we might need to resolve conflicts $migrationInfo['main_details'] =& $rowsElementsAndInfo['main_details']; $migrationInfo['other_details'] =& $rowsElementsAndInfo['other_details']; $migrationInfo['main_loc_block'] =& $rowsElementsAndInfo['main_loc_block']; $migrationInfo['rows'] =& $rowsElementsAndInfo['rows']; // go ahead with merge if there is no conflict $conflicts = array(); if (!CRM_Dedupe_Merger::skipMerge($mainId, $otherId, $migrationInfo, $mode, $conflicts)) { CRM_Dedupe_Merger::moveAllBelongings($mainId, $otherId, $migrationInfo); $resultStats['merged'][] = array('main_id' => $mainId, 'other_id' => $otherId); } else { $resultStats['skipped'][] = array('main_id' => $mainId, 'other_id' => $otherId); } // store any conflicts if (!empty($conflicts)) { foreach ($conflicts as $key => $dnc) { $conflicts[$key] = "{$migrationInfo['rows'][$key]['title']}: '{$migrationInfo['rows'][$key]['main']}' vs. '{$migrationInfo['rows'][$key]['other']}'"; } CRM_Core_BAO_PrevNextCache::markConflict($mainId, $otherId, $cacheKeyString, $conflicts); } else { // delete entry from PrevNextCache table so we don't consider the pair next time // pair may have been flipped, so make sure we delete using both orders CRM_Core_BAO_PrevNextCache::deletePair($mainId, $otherId, $cacheKeyString, TRUE); } CRM_Core_DAO::freeResult(); unset($rowsElementsAndInfo, $migrationInfo); } if ($cacheKeyString && !$redirectForPerformance) { // retrieve next pair of dupes $dupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, $cacheParams['join'], $cacheParams['where']); } else { // do not proceed. Terminate the loop unset($dupePairs); } } CRM_Dedupe_Merger::updateMergeStats($cacheKeyString, $resultStats); return $resultStats; }
/** * Merge given set of contacts. Performs core operation. * * @param array $dupePairs * Set of pair of contacts for whom merge is to be done. * @param array $cacheParams * Prev-next-cache params based on which next pair of contacts are computed. * Generally used with batch-merge. * @param string $mode * Helps decide how to behave when there are conflicts. * A 'safe' value skips the merge if there are any un-resolved conflicts. * Does a force merge otherwise (aggressive mode). * @param bool $autoFlip to let api decide which contact to retain and which to delete. * Whether to let api decide which contact to retain and which to delete. * * @param bool $redirectForPerformance * Redirect to a url for batch processing. * * @param bool $checkPermissions * Respect logged in user permissions. * * @return array|bool */ public static function merge($dupePairs = array(), $cacheParams = array(), $mode = 'safe', $autoFlip = TRUE, $redirectForPerformance = FALSE, $checkPermissions = TRUE) { $cacheKeyString = CRM_Utils_Array::value('cache_key_string', $cacheParams); $resultStats = array('merged' => array(), 'skipped' => array()); // we don't want dupe caching to get reset after every-merge, and therefore set the // doNotResetCache flag $config = CRM_Core_Config::singleton(); $config->doNotResetCache = 1; $deletedContacts = array(); while (!empty($dupePairs)) { foreach ($dupePairs as $index => $dupes) { if (in_array($dupes['dstID'], $deletedContacts) || in_array($dupes['srcID'], $deletedContacts)) { unset($dupePairs[$index]); continue; } CRM_Utils_Hook::merge('flip', $dupes, $dupes['dstID'], $dupes['srcID']); $mainId = $dupes['dstID']; $otherId = $dupes['srcID']; if (!$mainId || !$otherId) { // return error return FALSE; } // Generate var $migrationInfo. The variable structure is exactly same as // $formValues submitted during a UI merge for a pair of contacts. $rowsElementsAndInfo = CRM_Dedupe_Merger::getRowsElementsAndInfo($mainId, $otherId, $checkPermissions); // add additional details that we might need to resolve conflicts $rowsElementsAndInfo['migration_info']['main_details'] =& $rowsElementsAndInfo['main_details']; $rowsElementsAndInfo['migration_info']['other_details'] =& $rowsElementsAndInfo['other_details']; $rowsElementsAndInfo['migration_info']['rows'] =& $rowsElementsAndInfo['rows']; self::dedupePair($rowsElementsAndInfo['migration_info'], $resultStats, $deletedContacts, $mode, $checkPermissions, $mainId, $otherId, $cacheKeyString); } if ($cacheKeyString && !$redirectForPerformance) { // retrieve next pair of dupes // @todo call getDuplicatePairs. $dupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, $cacheParams['join'], $cacheParams['where'], 0, 0, array(), '', FALSE); } else { // do not proceed. Terminate the loop unset($dupePairs); } } CRM_Dedupe_Merger::updateMergeStats($cacheKeyString, $resultStats); return $resultStats; }