Skip to content



This hook allows modification of the data used to perform merging of duplicates. It can be useful if your custom module has added its own tables related to CiviCRM contacts.


If your extension has registered entities that have contact ID foreign keys defined correctly in their schema XML, these entities will be automatically handled by the merge process so you don’t need to implement this hook unless you need to do something fancy.


This hook was first available in CiviCRM 3.2.3.


hook_civicrm_merge($type, &$data, $mainId = NULL, $otherId = NULL, $tables = NULL)


  • string $type - the type of data being passed (cidRefs (deprecated, hook will no longer be called at some point for this), eidRefs, relTables, or sqls)
  • array $data - the data, which depends on the value of $type (see Details)
  • int $mainId - ID of the contact that survives the merge (only when $type is sqls)
  • int $otherId - ID of the contact that will be absorbed and deleted (only when $type is sqls)
  • array $tables - when $type is sqls, an array of tables as it may have been handed to the calling function


The contents of $data will vary based on the $type of data being passed:

  • relTables: an array of tables used for asking user which elements to merge, as used at civicrm/contact/merge. Each table in the array has this format:

    'rel_table_UNIQUE-TABLE-NICKNAME' => array(
      'title' => ts('TITLE'),
      'tables' => array('TABLE-NAME' [, ...]),
      'url' => CRM_Utils_System::url(PATH, QUERY),
  • sqls: a one-dimensional array of SQL statements to be run in the final merge operation. These SQL statements are run within a single transaction.

  • cidRefs: this is deprecated in favour of the entityTypes hook. If you alter cidRefs you will get a deprecation warning an array of tables and their fields referencing civicrm_contact.contact_id explicitly. Each table in the array has this format:

    'TABLE-NAME' => array('COLUMN-NAME' [, ...])
  • eidRefs: an array of polymorphic tables and the names of the columns that hold the entity table name and ID to which they relate.

    Each entry in the $data array has this format:

    'TABLE-NAME' => array('entity_table-COLUMN-NAME' => 'entity_id-COLUMN-NAME')

    Example: civicrm_note uses two columns to identify the thing a note record relates to: entity_table and entity_id.

    So if civicrm_note was not part of core and you wanted to include its contents in a merge, you might use this hook to set the following key and value in $data for the eidRefs invocation of this hook:

    'civicrm_note' => ['entity_table', 'entity_id']


 * In this example we assume our module has created its own tables to
 * store extra data on CiviCRM contacts:
 * Table civitest_foo stores a relationship between two contacts, and
 * contains the two relevant columns: civitest_foo.contact_id and
 * civitest_foo.foo_id, both of which are foreign keys to
 * Table civitest_bar stores extra properties for contacts, and contains
 * one relevant column: civitest_bar.contact_id is a foreign key to
 * This hook ensures that data in these two tables is included in CiviCRM
 * merge operations.
function civitest_civicrm_merge($type, &$data, $mainId = NULL, $otherId = NULL, $tables = NULL) {

  // If you are using Drupal and you use separate DBs for Drupal and
  // CiviCRM, use the following to prefix your tables with the name of the
  // Drupal database.
  global $db_url;
  if (!empty($db_url) {
    $db_default = is_array($db_url) ? $db_url['default'] : $db_url;
    $db_default = ltrim(parse_url($db_default, PHP_URL_PATH), '/');
  else {
    $db_default = '';

  switch ($type) {
    case 'relTables':
      // Allow user to decide whether or not to merge records in `civitest_foo` table
      $data['rel_table_foo'] = array(
        // Title as shown to user for this type of data
        'title'  => ts('Foos'),                 
        // Name of database table holding these records
        'tables' => array($db_default .'civitest_foo'),      
        // URL to view this data for this contact,
        // in this case using CiviCRM's native URL utility
        'url'    => CRM_Utils_System::url('civicrm/civitest/foo', 'action=browse&cid=$cid'),
        // NOTE: '$cid' will be replaced with correct CiviCRM contact ID.

    case 'cidRefs':
      // Use entityTypes hook instead as cidRefs is deprecated in this hook.

    case 'eidRefs':
      // Add references to civitest_bar table, which is keyed to
      // using `bar_entity_id` column, when the value
      // in its `entity_table` column is equal to 'civicrm_contact'. By
      // adding this to $data, records in this table will be automatically
      // included in the merge.
      $data[$db_default . 'civitest_bar'] = ['entity_table' => 'bar_entity_id'];

    case 'sqls':
      // Allows to add or modify SQL statements in $data
      if ($tables) {
        // Nothing to do in our case. In some cases, you might want to find and
        // modify existing SQL statements in $data.