Merge pull request #5169 from PalanteJon/CRM-15745
[civicrm-core.git] / CRM / Dedupe / Merger.php
index f62096e3f2de31128b4be7d25911656792b6cc5e..5bb0785828a00e2e8c81227c2a2d5e4a465e8eeb 100644 (file)
@@ -23,7 +23,7 @@
  | GNU Affero General Public License or the licensing of CiviCRM,     |
  | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
  +--------------------------------------------------------------------+
-*/
+ */
 
 /**
  *
  */
 class CRM_Dedupe_Merger {
 
-  // FIXME: consider creating a common structure with cidRefs() and eidRefs()
-  // FIXME: the sub-pages references by the URLs should
-  // be loaded dynamically on the merge form instead
   /**
+   * FIXME: consider creating a common structure with cidRefs() and eidRefs()
+   * FIXME: the sub-pages references by the URLs should
+   * be loaded dynamically on the merge form instead
    * @return array
    */
-  static function relTables() {
+  public static function relTables() {
     static $relTables;
 
     $config = CRM_Core_Config::singleton();
@@ -156,9 +156,9 @@ class CRM_Dedupe_Merger {
   }
 
   /**
-   * Returns the related tables groups for which a contact has any info entered
+   * Returns the related tables groups for which a contact has any info entered.
    */
-  static function getActiveRelTables($cid) {
+  public static function getActiveRelTables($cid) {
     $cid = (int) $cid;
     $groups = array();
 
@@ -191,7 +191,7 @@ class CRM_Dedupe_Merger {
   /**
    * Return tables and their fields referencing civicrm_contact.contact_id explicitly
    */
-  static function cidRefs() {
+  public static function cidRefs() {
     static $cidRefs;
     if (!$cidRefs) {
       $sql = "
@@ -222,7 +222,7 @@ WHERE
   /**
    * Return tables and their fields referencing civicrm_contact.contact_id with entity_id
    */
-  static function eidRefs() {
+  public static function eidRefs() {
     static $eidRefs;
     if (!$eidRefs) {
       // FIXME: this should be generated dynamically from the schema
@@ -243,12 +243,12 @@ WHERE
   }
 
   /**
-   * Return tables using locations
+   * Return tables using locations.
    */
-  static function locTables() {
+  public static function locTables() {
     static $locTables;
     if (!$locTables) {
-      $locTables = array( 'civicrm_email', 'civicrm_address', 'civicrm_phone' );
+      $locTables = array('civicrm_email', 'civicrm_address', 'civicrm_phone');
 
       // Allow hook_civicrm_merge() to adjust $locTables
       CRM_Utils_Hook::merge('locTables', $locTables);
@@ -258,10 +258,11 @@ WHERE
 
   /**
    * We treat multi-valued custom sets as "related tables" similar to activities, contributions, etc.
-   * @param string $request 'relTables' or 'cidRefs'
+   * @param string $request
+   *   'relTables' or 'cidRefs'.
    * @see CRM-13836
    */
-  static function getMultiValueCustomSets($request) {
+  public static function getMultiValueCustomSets($request) {
     static $data = NULL;
     if ($data === NULL) {
       $data = array(
@@ -273,7 +274,7 @@ WHERE
         'extends' => array('IN' => array('Individual', 'Organization', 'Household', 'Contact')),
         'return' => array('id', 'title', 'table_name', 'style'),
       ));
-      foreach($result['values'] as $custom) {
+      foreach ($result['values'] as $custom) {
         $data['cidRefs'][$custom['table_name']] = array('entity_id');
         $urlSuffix = $custom['style'] == 'Tab' ? '&selectedChild=custom_' . $custom['id'] : '';
         $data['relTables']['rel_table_custom_' . $custom['id']] = array(
@@ -290,7 +291,7 @@ WHERE
    * Tables which require custom processing should declare functions to call here.
    * Doing so will override normal processing.
    */
-  static function cpTables() {
+  public static function cpTables() {
     static $tables;
     if (!$tables) {
       $tables = array(
@@ -307,7 +308,7 @@ WHERE
   /**
    * Return payment related table.
    */
-  static function paymentTables() {
+  public static function paymentTables() {
     static $tables;
     if (!$tables) {
       $tables = array('civicrm_pledge', 'civicrm_membership', 'civicrm_participant');
@@ -319,7 +320,7 @@ WHERE
   /**
    * Return payment update Query.
    */
-  static function paymentSql($tableName, $mainContactId, $otherContactId) {
+  public static function paymentSql($tableName, $mainContactId, $otherContactId) {
     $sqls = array();
     if (!$tableName || !$mainContactId || !$otherContactId) {
       return $sqls;
@@ -371,34 +372,34 @@ INNER JOIN  civicrm_participant participant ON ( participant.id = payment.partic
    *
    * @return array
    */
-  static function operationSql($mainId, $otherId, $tableName, $tableOperations = array(), $mode = 'add') {
+  public static function operationSql($mainId, $otherId, $tableName, $tableOperations = array(), $mode = 'add') {
     $sqls = array();
     if (!$tableName || !$mainId || !$otherId) {
       return $sqls;
     }
 
-
     switch ($tableName) {
       case 'civicrm_membership':
-        if (array_key_exists($tableName, $tableOperations) && $tableOperations[$tableName]['add'])
-        break;
-      if ($mode == 'add') {
-        $sqls[] = "
+        if (array_key_exists($tableName, $tableOperations) && $tableOperations[$tableName]['add']) {
+          break;
+        }
+        if ($mode == 'add') {
+          $sqls[] = "
 DELETE membership1.* FROM civicrm_membership membership1
  INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = membership2.membership_type_id
              AND membership1.contact_id = {$mainId}
              AND membership2.contact_id = {$otherId} ";
-      }
-      if ($mode == 'payment') {
-        $sqls[] = "
+        }
+        if ($mode == 'payment') {
+          $sqls[] = "
 DELETE contribution.* FROM civicrm_contribution contribution
 INNER JOIN  civicrm_membership_payment payment ON payment.contribution_id = contribution.id
 INNER JOIN  civicrm_membership membership1 ON membership1.id = payment.membership_id
             AND membership1.contact_id = {$mainId}
 INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = membership2.membership_type_id
             AND membership2.contact_id = {$otherId}";
-      }
-      break;
+        }
+        break;
 
       case 'civicrm_uf_match':
         // normal queries won't work for uf_match since that will lead to violation of unique constraint,
@@ -414,9 +415,8 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
    * Based on the provided two contact_ids and a set of tables, move the
    * belongings of the other contact to the main one.
    *
-   * @static
    */
-  static function moveContactBelongings($mainId, $otherId, $tables = FALSE, $tableOperations = array()) {
+  public static function moveContactBelongings($mainId, $otherId, $tables = FALSE, $tableOperations = array()) {
     $cidRefs = self::cidRefs();
     $eidRefs = self::eidRefs();
     $cpTables = self::cpTables();
@@ -517,13 +517,14 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
   /**
    * Find differences between contacts.
    *
-   * @param array $main contact details
-   * @param array $other contact details
+   * @param array $main
+   *   Contact details.
+   * @param array $other
+   *   Contact details.
    *
    * @return array
-   * @static
    */
-  static function findDifferences($main, $other) {
+  public static function findDifferences($main, $other) {
     $result = array(
       'contact' => array(),
       'custom' => array(),
@@ -554,19 +555,21 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
   /**
    * Batch merge a set of contacts based on rule-group and group.
    *
-   * @param  int $rgid rule group id
-   * @param  int $gid group id
-   * @param  string $mode helps decide how to behave when there are conflicts.
+   * @param int $rgid
+   *   Rule group id.
+   * @param int $gid
+   *   Group id.
+   * @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.
-   * @param  boolean $autoFlip wether to let api decide which contact to retain and which to delete.
+   * @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
-   * @static
-   * @access public
    */
-  static function batchMerge($rgid, $gid = NULL, $mode = 'safe', $autoFlip = TRUE, $redirectForPerformance = FALSE) {
+  public static function batchMerge($rgid, $gid = NULL, $mode = 'safe', $autoFlip = TRUE, $redirectForPerformance = FALSE) {
     $contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup', $rgid, 'contact_type');
     $cacheKeyString = "merge {$contactType}";
     $cacheKeyString .= $rgid ? "_{$rgid}" : '_0';
@@ -596,23 +599,25 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
   /**
    * 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.
+   * @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.
+   * @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  boolean $autoFlip wether to let api decide which contact to retain and which to delete.
+   * @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
-   * @static
-   * @access public
    */
-  static function merge($dupePairs = array(), $cacheParams = array(), $mode = 'safe',
-    $autoFlip = TRUE, $redirectForPerformance = FALSE
+  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());
@@ -640,7 +645,7 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
 
         // 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);
+        $rowsElementsAndInfo = CRM_Dedupe_Merger::getRowsElementsAndInfo($mainId, $otherId);
 
         $migrationInfo = &$rowsElementsAndInfo['migration_info'];
 
@@ -653,10 +658,10 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
         // go ahead with merge if there is no conflict
         if (!CRM_Dedupe_Merger::skipMerge($mainId, $otherId, $migrationInfo, $mode)) {
           CRM_Dedupe_Merger::moveAllBelongings($mainId, $otherId, $migrationInfo);
-          $resultStats['merged'][] = array('main_d' => $mainId, 'other_id' => $otherId);
+          $resultStats['merged'][] = array('main_id' => $mainId, 'other_id' => $otherId);
         }
         else {
-          $resultStats['skipped'][] = array('main_d' => $mainId, 'other_id' => $otherId);
+          $resultStats['skipped'][] = array('main_id' => $mainId, 'other_id' => $otherId);
         }
 
         // delete entry from PrevNextCache table so we don't consider the pair next time
@@ -687,18 +692,20 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
    * A function which uses various rules / algorithms for choosing which contact to bias to
    * when there's a conflict (to handle "gotchas"). Plus the safest route to merge.
    *
-   * @param  int $mainId main contact with whom merge has to happen
-   * @param  int $otherId duplicate contact which would be deleted after merge operation
-   * @param  array $migrationInfo array of information about which elements to merge.
-   * @param  string $mode helps decide how to behave when there are conflicts.
+   * @param int $mainId
+   *   Main contact with whom merge has to happen.
+   * @param int $otherId
+   *   Duplicate contact which would be deleted after merge operation.
+   * @param array $migrationInfo
+   *   Array of information about which elements to 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).
    *
    * @return bool
-   * @static
-   * @access public
    */
-  static function skipMerge($mainId, $otherId, &$migrationInfo, $mode = 'safe') {
+  public static function skipMerge($mainId, $otherId, &$migrationInfo, $mode = 'safe') {
     $conflicts = array();
     $migrationData = array(
       'old_migration_info' => $migrationInfo,
@@ -714,7 +721,8 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
       }
       elseif ((in_array(substr($key, 5), CRM_Dedupe_Merger::getContactFields()) or
           substr($key, 0, 12) == 'move_custom_'
-        ) and $val != NULL) {
+        ) and $val != NULL
+      ) {
         // Rule: if both main-contact has other-contact, let $mode decide if to merge a
         // particular field or not
         if (!empty($migrationInfo['rows'][$key]['main'])) {
@@ -738,14 +746,13 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
         // Rule: resolve address conflict if any -
         if ($fieldName == 'address') {
           $mainNewLocTypeId = $migrationInfo['location'][$fieldName][$fieldCount]['locTypeId'];
-          if (!empty($migrationInfo['main_loc_address']) &&
-              array_key_exists("main_{$mainNewLocTypeId}", $migrationInfo['main_loc_address'])) {
+          if (!empty($migrationInfo['main_loc_block']) &&
+              array_key_exists("main_address{$mainNewLocTypeId}", $migrationInfo['main_loc_block'])) {
             // main loc already has some address for the loc-type. Its a overwrite situation.
-
             // look for next available loc-type
             $newTypeId = NULL;
             foreach ($allLocationTypes as $typeId => $typeLabel) {
-              if (!array_key_exists("main_{$typeId}", $migrationInfo['main_loc_address'])) {
+              if (!array_key_exists("main_address{$typeId}", $migrationInfo['main_loc_block'])) {
                 $newTypeId = $typeId;
               }
             }
@@ -799,19 +806,23 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
   /**
    * A function to build an array of information required by merge function and the merge UI.
    *
-   * @param  int $mainId main contact with whom merge has to happen
-   * @param  int $otherId duplicate contact which would be deleted after merge operation
+   * @param int $mainId
+   *   Main contact with whom merge has to happen.
+   * @param int $otherId
+   *   Duplicate contact which would be deleted after merge operation.
    *
    * @return array|bool|int
-   * @static
-   * @access public
    */
-  static function getRowsElementsAndInfo($mainId, $otherId) {
+  public static function getRowsElementsAndInfo($mainId, $otherId) {
     $qfZeroBug = 'e8cddb72-a257-11dc-b9cc-0016d3330ee9';
 
     // Fetch contacts
     foreach (array('main' => $mainId, 'other' => $otherId) as $moniker => $cid) {
-      $params = array('contact_id' => $cid, 'version' => 3, 'return' => array_merge(array('display_name'), self::getContactFields()));
+      $params = array(
+        'contact_id' => $cid,
+        'version' => 3,
+        'return' => array_merge(array('display_name'), self::getContactFields()),
+      );
       $result = civicrm_api('contact', 'get', $params);
 
       if (empty($result['values'][$cid]['contact_type'])) {
@@ -836,16 +847,13 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
         'communication_style_id' => $value,
       );
 
-      if (!empty($contact['preferred_communication_method'])){
-      // api 3 returns pref_comm_method as an array, which breaks the lookup; so we reconstruct
-      $prefCommList = is_array($specialValues[$moniker]['preferred_communication_method']) ?
-        implode(CRM_Core_DAO::VALUE_SEPARATOR, $specialValues[$moniker]['preferred_communication_method']) :
-        $specialValues[$moniker]['preferred_communication_method'];
+      if (!empty($contact['preferred_communication_method'])) {
+        // api 3 returns pref_comm_method as an array, which breaks the lookup; so we reconstruct
+        $prefCommList = is_array($specialValues[$moniker]['preferred_communication_method']) ? implode(CRM_Core_DAO::VALUE_SEPARATOR, $specialValues[$moniker]['preferred_communication_method']) : $specialValues[$moniker]['preferred_communication_method'];
         $specialValues[$moniker]['preferred_communication_method'] = CRM_Core_DAO::VALUE_SEPARATOR . $prefCommList . CRM_Core_DAO::VALUE_SEPARATOR;
       }
       $names = array(
-        'preferred_communication_method' =>
-        array(
+        'preferred_communication_method' => array(
           'newName' => 'preferred_communication_method_display',
           'groupName' => 'preferred_communication_method',
         ),
@@ -1028,7 +1036,7 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
             array(
               1 => $block,
               2 => $count,
-              3 => $allLocationTypes[$locTypeId]
+              3 => $allLocationTypes[$locTypeId],
             )
           );
 
@@ -1047,8 +1055,11 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
             $js = array('onChange' => "mergeBlock('$name', this, $count );");
           }
           $elements[] = array(
-            'select', "location[{$name}][$count][locTypeId]", NULL,
-            $defaultLocType + $locTypeValues, $js,
+            'select',
+            "location[{$name}][$count][locTypeId]",
+            NULL,
+            $defaultLocType + $locTypeValues,
+            $js,
           );
           // keep location-type-id same as that of other-contact
           $migrationInfo['location'][$name][$count]['locTypeId'] = $locTypeId;
@@ -1162,9 +1173,9 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
               );
               if ($values['data'] === 0 || $values['data'] === '0') {
                 $values['data'] = $qfZeroBug;
-            }
+              }
               $value = ($values['data']) ? $values['data'] : $value;
-          }
+            }
           }
           $rows["move_custom_$fid"]['title'] = $field['label'];
 
@@ -1195,16 +1206,16 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
    * other contact to the main one - be it Location / CustomFields or Contact .. related info.
    * A superset of moveContactBelongings() function.
    *
-   * @param  int $mainId main contact with whom merge has to happen
-   * @param  int $otherId duplicate contact which would be deleted after merge operation
+   * @param int $mainId
+   *   Main contact with whom merge has to happen.
+   * @param int $otherId
+   *   Duplicate contact which would be deleted after merge operation.
    *
    * @param $migrationInfo
    *
    * @return bool
-   * @static
-   * @access public
    */
-  static function moveAllBelongings($mainId, $otherId, $migrationInfo) {
+  public static function moveAllBelongings($mainId, $otherId, $migrationInfo) {
     if (empty($migrationInfo)) {
       return FALSE;
     }
@@ -1217,7 +1228,7 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
         $value = '0';
       }
       if ((in_array(substr($key, 5), CRM_Dedupe_Merger::getContactFields()) ||
-        substr($key, 0, 12) == 'move_custom_') &&
+          substr($key, 0, 12) == 'move_custom_') &&
         $value != NULL
       ) {
         $submitted[substr($key, 5)] = $value;
@@ -1247,7 +1258,6 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
       }
     }
 
-
     // **** Do location related migration:
     if (!empty($locBlocks)) {
       $locComponent = array(
@@ -1283,7 +1293,7 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
           }
 
           if (isset($migrationInfo['main_details']['loc_block_ids'][$name])) {
-          $mainBlockId = CRM_Utils_Array::value($idKey, $migrationInfo['main_details']['loc_block_ids'][$name]);
+            $mainBlockId = CRM_Utils_Array::value($idKey, $migrationInfo['main_details']['loc_block_ids'][$name]);
           }
 
           if (!$otherBlockId) {
@@ -1415,10 +1425,13 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
                 //keep state and country as array format.
                 //for checkbox and m-select format w/ VALUE_SEPARATOR
                 if (in_array($htmlType, array(
-                  'CheckBox', 'Multi-Select', 'AdvMulti-Select'))) {
+                  'CheckBox',
+                  'Multi-Select',
+                  'AdvMulti-Select',
+                ))) {
                   $submitted[$key] = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR,
-                    $mergeValue
-                  ) . CRM_Core_DAO::VALUE_SEPARATOR;
+                      $mergeValue
+                    ) . CRM_Core_DAO::VALUE_SEPARATOR;
                 }
                 else {
                   $submitted[$key] = $mergeValue;
@@ -1426,7 +1439,9 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
               }
             }
             elseif (in_array($htmlType, array(
-              'Multi-Select Country', 'Multi-Select State/Province'))) {
+              'Multi-Select Country',
+              'Multi-Select State/Province',
+            ))) {
               //we require submitted values should be in array format
               if ($value) {
                 $mergeValueArray = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value);
@@ -1475,18 +1490,19 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
 
       // move the other contact's file to main contact
       //NYSS need to INSERT or UPDATE depending on whether main contact has an existing record
-      if ( CRM_Core_DAO::singleValueQuery("SELECT id FROM {$tableName} WHERE entity_id = {$mainId}") ) {
-      $sql = "UPDATE {$tableName} SET {$columnName} = {$fileIds[$otherId]} WHERE entity_id = {$mainId}";
+      if (CRM_Core_DAO::singleValueQuery("SELECT id FROM {$tableName} WHERE entity_id = {$mainId}")) {
+        $sql = "UPDATE {$tableName} SET {$columnName} = {$fileIds[$otherId]} WHERE entity_id = {$mainId}";
       }
       else {
         $sql = "INSERT INTO {$tableName} ( entity_id, {$columnName} ) VALUES ( {$mainId}, {$fileIds[$otherId]} )";
       }
       CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray);
 
-      if ( CRM_Core_DAO::singleValueQuery("
+      if (CRM_Core_DAO::singleValueQuery("
         SELECT id
         FROM civicrm_entity_file
-        WHERE entity_table = '{$tableName}' AND file_id = {$fileIds[$otherId]}") ) {
+        WHERE entity_table = '{$tableName}' AND file_id = {$fileIds[$otherId]}")
+      ) {
         $sql = "
           UPDATE civicrm_entity_file
           SET entity_id = {$mainId}
@@ -1540,12 +1556,12 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
 
     /*         } */
 
-
     // CRM-15681 merge sub_types
     if ($other_sub_types = CRM_Utils_array::value('contact_sub_type', $migrationInfo['other_details'])) {
       if ($main_sub_types = CRM_Utils_array::value('contact_sub_type', $migrationInfo['main_details'])) {
-        $submitted['contact_sub_type'] = array_unique( array_merge($main_sub_types, $other_sub_types));
-      } else {
+        $submitted['contact_sub_type'] = array_unique(array_merge($main_sub_types, $other_sub_types));
+      }
+      else {
         $submitted['contact_sub_type'] = $other_sub_types;
       }
     }
@@ -1558,17 +1574,18 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
       if ($currentEmloyerId = CRM_Utils_Array::value('current_employer_id', $submitted)) {
         if (!CRM_Utils_System::isNull($currentEmloyerId)) {
           $submitted['current_employer'] = $submitted['current_employer_id'];
-        } else {
+        }
+        else {
           $submitted['current_employer'] = '';
         }
         unset($submitted['current_employer_id']);
       }
 
       //CRM-14312 include prefix/suffix from mainId if not overridden for proper construction of display/sort name
-      if ( !isset($submitted['prefix_id']) && !empty($migrationInfo['main_details']['prefix_id']) ) {
+      if (!isset($submitted['prefix_id']) && !empty($migrationInfo['main_details']['prefix_id'])) {
         $submitted['prefix_id'] = $migrationInfo['main_details']['prefix_id'];
       }
-      if ( !isset($submitted['suffix_id']) && !empty($migrationInfo['main_details']['suffix_id']) ) {
+      if (!isset($submitted['suffix_id']) && !empty($migrationInfo['main_details']['suffix_id'])) {
         $submitted['suffix_id'] = $migrationInfo['main_details']['suffix_id'];
       }
 
@@ -1576,16 +1593,29 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
       unset($submitted);
     }
 
+    CRM_Utils_Hook::post('merge', 'Contact', $mainId, CRM_Core_DAO::$_nullObject);
+
     return TRUE;
   }
 
   /**
-   * @return array of field names which will be compared, so everything except ID.
+   * @return array
+   *   Array of field names which will be compared, so everything except ID.
    */
-  static function getContactFields() {
+  public static function getContactFields() {
     $contactFields = CRM_Contact_DAO_Contact::fields();
-    $invalidFields = array('api_key', 'contact_is_deleted', 'created_date', 'display_name', 'hash', 'id', 'modified_date',
-      'primary_contact_id', 'sort_name', 'user_unique_id');
+    $invalidFields = array(
+      'api_key',
+      'contact_is_deleted',
+      'created_date',
+      'display_name',
+      'hash',
+      'id',
+      'modified_date',
+      'primary_contact_id',
+      'sort_name',
+      'user_unique_id',
+    );
     foreach ($contactFields as $field => $value) {
       if (in_array($field, $invalidFields)) {
         unset($contactFields[$field]);
@@ -1596,12 +1626,12 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
 
   /**
    * Added for CRM-12695
-   * Based on the contactId provided
+   * Based on the contactID provided
    * add/update membership(s) to related contacts
    *
-   * @param contactId
+   * @param int $contactID
    */
-  static function addMembershipToRealtedContacts($contactID) {
+  public static function addMembershipToRealtedContacts($contactID) {
     $dao = new CRM_Member_DAO_Membership();
     $dao->contact_id = $contactID;
     $dao->is_test = 0;
@@ -1619,11 +1649,12 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
           'start_date' => CRM_Utils_Date::isoToMysql($dao->start_date),
           'end_date' => CRM_Utils_Date::isoToMysql($dao->end_date),
           'source' => $dao->source,
-          'status_id' => $dao->status_id
+          'status_id' => $dao->status_id,
         );
         // create/update membership(s) for related contact(s)
         CRM_Member_BAO_Membership::createRelatedMemberships($membershipParams, $dao);
       } // end of if relationshipTypeId
     }
   }
+
 }