'class' => 'CRM_Event_Cart_DAO_Cart',
'table' => 'civicrm_event_carts',
],
- 'CRM_Dedupe_DAO_RuleGroup' => [
- 'name' => 'RuleGroup',
- 'class' => 'CRM_Dedupe_DAO_RuleGroup',
+ 'CRM_Dedupe_DAO_DedupeRuleGroup' => [
+ 'name' => 'DedupeRuleGroup',
+ 'class' => 'CRM_Dedupe_DAO_DedupeRuleGroup',
'table' => 'civicrm_dedupe_rule_group',
],
- 'CRM_Dedupe_DAO_Rule' => [
- 'name' => 'Rule',
- 'class' => 'CRM_Dedupe_DAO_Rule',
+ 'CRM_Dedupe_DAO_DedupeRule' => [
+ 'name' => 'DedupeRule',
+ 'class' => 'CRM_Dedupe_DAO_DedupeRule',
'table' => 'civicrm_dedupe_rule',
],
- 'CRM_Dedupe_DAO_Exception' => [
- 'name' => 'Exception',
- 'class' => 'CRM_Dedupe_DAO_Exception',
+ 'CRM_Dedupe_DAO_DedupeException' => [
+ 'name' => 'DedupeException',
+ 'class' => 'CRM_Dedupe_DAO_DedupeException',
'table' => 'civicrm_dedupe_exception',
],
'CRM_Case_DAO_CaseType' => [
--- /dev/null
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved. |
+ | |
+ | This work is published under the GNU AGPLv3 license with some |
+ | permitted exceptions and without any warranty. For full license |
+ | and copyright information, see https://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+/**
+ * Manages dedupe exceptions - ie pairs marked as non-duplicates.
+ */
+class CRM_Dedupe_BAO_DedupeException extends CRM_Dedupe_DAO_DedupeException {
+
+ /**
+ * Create a dedupe exception record.
+ *
+ * @param array $params
+ *
+ * @return \CRM_Dedupe_BAO_Exception
+ */
+ public static function create($params) {
+ $hook = empty($params['id']) ? 'create' : 'edit';
+ CRM_Utils_Hook::pre($hook, 'Exception', CRM_Utils_Array::value('id', $params), $params);
+ $contact1 = $params['contact_id1'] ?? NULL;
+ $contact2 = $params['contact_id2'] ?? NULL;
+ $dao = new CRM_Dedupe_BAO_Exception();
+ $dao->copyValues($params);
+ if ($contact1 && $contact2) {
+ CRM_Core_DAO::singleValueQuery("
+ DELETE FROM civicrm_prevnext_cache
+ WHERE (entity_id1 = %1 AND entity_id2 = %2)
+ OR (entity_id1 = %2 AND entity_id2 = %2)",
+ [1 => [$contact1, 'Integer'], 2 => [$contact2, 'Integer']]
+ );
+ if ($contact2 < $contact1) {
+ // These are expected to be saved lowest first.
+ $dao->contact_id1 = $contact2;
+ $dao->contact_id2 = $contact1;
+ }
+ }
+ $dao->save();
+
+ CRM_Utils_Hook::post($hook, 'Exception', $dao->id, $dao);
+ return $dao;
+ }
+
+}
--- /dev/null
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved. |
+ | |
+ | This work is published under the GNU AGPLv3 license with some |
+ | permitted exceptions and without any warranty. For full license |
+ | and copyright information, see https://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+/**
+ * The CiviCRM duplicate discovery engine is based on an
+ * algorithm designed by David Strauss <david@fourkitchens.com>.
+ */
+class CRM_Dedupe_BAO_DedupeRule extends CRM_Dedupe_DAO_DedupeRule {
+
+ /**
+ * Ids of the contacts to limit the SQL queries (whole-database queries otherwise)
+ * @var array
+ */
+ public $contactIds = [];
+
+ /**
+ * Params to dedupe against (queries against the whole contact set otherwise)
+ * @var array
+ */
+ public $params = [];
+
+ /**
+ * Return the SQL query for the given rule - either for finding matching
+ * pairs of contacts, or for matching against the $params variable (if set).
+ *
+ * @return string
+ * SQL query performing the search
+ *
+ * @throws \CRM_Core_Exception
+ * @throws \CiviCRM_API3_Exception
+ */
+ public function sql() {
+ if ($this->params &&
+ (!array_key_exists($this->rule_table, $this->params) ||
+ !array_key_exists($this->rule_field, $this->params[$this->rule_table])
+ )
+ ) {
+ // if params is present and doesn't have an entry for a field, don't construct the clause.
+ return NULL;
+ }
+
+ // we need to initialise WHERE, ON and USING here, as some table types
+ // extend them; $where is an array of required conditions, $on and
+ // $using are arrays of required field matchings (for substring and
+ // full matches, respectively)
+ $where = [];
+ $on = ["SUBSTR(t1.{$this->rule_field}, 1, {$this->rule_length}) = SUBSTR(t2.{$this->rule_field}, 1, {$this->rule_length})"];
+
+ $innerJoinClauses = [
+ "t1.{$this->rule_field} IS NOT NULL",
+ "t2.{$this->rule_field} IS NOT NULL",
+ "t1.{$this->rule_field} = t2.{$this->rule_field}",
+ ];
+
+ if (in_array($this->getFieldType($this->rule_field), CRM_Utils_Type::getTextTypes(), TRUE)) {
+ $innerJoinClauses[] = "t1.{$this->rule_field} <> ''";
+ $innerJoinClauses[] = "t2.{$this->rule_field} <> ''";
+ }
+
+ switch ($this->rule_table) {
+ case 'civicrm_contact':
+ $id = 'id';
+ //we should restrict by contact type in the first step
+ $sql = "SELECT contact_type FROM civicrm_dedupe_rule_group WHERE id = {$this->dedupe_rule_group_id};";
+ $ct = CRM_Core_DAO::singleValueQuery($sql);
+ if ($this->params) {
+ $where[] = "t1.contact_type = '{$ct}'";
+ }
+ else {
+ $where[] = "t1.contact_type = '{$ct}'";
+ $where[] = "t2.contact_type = '{$ct}'";
+ }
+ break;
+
+ case 'civicrm_address':
+ case 'civicrm_email':
+ case 'civicrm_im':
+ case 'civicrm_openid':
+ case 'civicrm_phone':
+ $id = 'contact_id';
+ break;
+
+ case 'civicrm_note':
+ $id = 'entity_id';
+ if ($this->params) {
+ $where[] = "t1.entity_table = 'civicrm_contact'";
+ }
+ else {
+ $where[] = "t1.entity_table = 'civicrm_contact'";
+ $where[] = "t2.entity_table = 'civicrm_contact'";
+ }
+ break;
+
+ default:
+ // custom data tables
+ if (preg_match('/^civicrm_value_/', $this->rule_table) || preg_match('/^custom_value_/', $this->rule_table)) {
+ $id = 'entity_id';
+ }
+ else {
+ throw new CRM_Core_Exception("Unsupported rule_table for civicrm_dedupe_rule.id of {$this->id}");
+ }
+ break;
+ }
+
+ // build SELECT based on the field names containing contact ids
+ // if there are params provided, id1 should be 0
+ if ($this->params) {
+ $select = "t1.$id id1, {$this->rule_weight} weight";
+ $subSelect = 'id1, weight';
+ }
+ else {
+ $select = "t1.$id id1, t2.$id id2, {$this->rule_weight} weight";
+ $subSelect = 'id1, id2, weight';
+ }
+
+ // build FROM (and WHERE, if it's a parametrised search)
+ // based on whether the rule is about substrings or not
+ if ($this->params) {
+ $from = "{$this->rule_table} t1";
+ $str = 'NULL';
+ if (isset($this->params[$this->rule_table][$this->rule_field])) {
+ $str = trim(CRM_Utils_Type::escape($this->params[$this->rule_table][$this->rule_field], 'String'));
+ }
+ if ($this->rule_length) {
+ $where[] = "SUBSTR(t1.{$this->rule_field}, 1, {$this->rule_length}) = SUBSTR('$str', 1, {$this->rule_length})";
+ $where[] = "t1.{$this->rule_field} IS NOT NULL";
+ }
+ else {
+ $where[] = "t1.{$this->rule_field} = '$str'";
+ }
+ }
+ else {
+ if ($this->rule_length) {
+ $from = "{$this->rule_table} t1 JOIN {$this->rule_table} t2 ON (" . implode(' AND ', $on) . ")";
+ }
+ else {
+ $from = "{$this->rule_table} t1 INNER JOIN {$this->rule_table} t2 ON (" . implode(' AND ', $innerJoinClauses) . ")";
+ }
+ }
+
+ // finish building WHERE, also limit the results if requested
+ if (!$this->params) {
+ $where[] = "t1.$id < t2.$id";
+ }
+ $query = "SELECT $select FROM $from WHERE " . implode(' AND ', $where);
+ if ($this->contactIds) {
+ $cids = [];
+ foreach ($this->contactIds as $cid) {
+ $cids[] = CRM_Utils_Type::escape($cid, 'Integer');
+ }
+ if (count($cids) == 1) {
+ $query .= " AND (t1.$id = {$cids[0]}) UNION $query AND t2.$id = {$cids[0]}";
+ }
+ else {
+ $query .= " AND t1.$id IN (" . implode(',', $cids) . ")
+ UNION $query AND t2.$id IN (" . implode(',', $cids) . ")";
+ }
+ // The `weight` is ambiguous in the context of the union; put the whole
+ // thing in a subquery.
+ $query = "SELECT $subSelect FROM ($query) subunion";
+ }
+
+ return $query;
+ }
+
+ /**
+ * find fields related to a rule group.
+ *
+ * @param array $params contains the rule group property to identify rule group
+ *
+ * @return array
+ * rule fields array associated to rule group
+ */
+ public static function dedupeRuleFields($params) {
+ $rgBao = new CRM_Dedupe_BAO_RuleGroup();
+ $rgBao->used = $params['used'];
+ $rgBao->contact_type = $params['contact_type'];
+ $rgBao->find(TRUE);
+
+ $ruleBao = new CRM_Dedupe_BAO_Rule();
+ $ruleBao->dedupe_rule_group_id = $rgBao->id;
+ $ruleBao->find();
+ $ruleFields = [];
+ while ($ruleBao->fetch()) {
+ $field_name = $ruleBao->rule_field;
+ if ($field_name == 'phone_numeric') {
+ $field_name = 'phone';
+ }
+ $ruleFields[] = $field_name;
+ }
+ return $ruleFields;
+ }
+
+ /**
+ * @param int $cid
+ * @param int $oid
+ *
+ * @return bool
+ */
+ public static function validateContacts($cid, $oid) {
+ if (!$cid || !$oid) {
+ return NULL;
+ }
+ $exception = new CRM_Dedupe_DAO_Exception();
+ $exception->contact_id1 = $cid;
+ $exception->contact_id2 = $oid;
+ //make sure contact2 > contact1.
+ if ($cid > $oid) {
+ $exception->contact_id1 = $oid;
+ $exception->contact_id2 = $cid;
+ }
+
+ return !$exception->find(TRUE);
+ }
+
+ /**
+ * Get the specification for the given field.
+ *
+ * @param string $fieldName
+ *
+ * @return array
+ * @throws \CiviCRM_API3_Exception
+ */
+ public function getFieldType($fieldName) {
+ $entity = CRM_Core_DAO_AllCoreTables::getBriefName(CRM_Core_DAO_AllCoreTables::getClassForTable($this->rule_table));
+ if (!$entity) {
+ // This means we have stored a custom field rather than an entity name in rule_table, figure out the entity.
+ $entity = civicrm_api3('CustomGroup', 'getvalue', ['table_name' => $this->rule_table, 'return' => 'extends']);
+ if (in_array($entity, ['Individual', 'Household', 'Organization'])) {
+ $entity = 'Contact';
+ }
+ $fieldName = 'custom_' . civicrm_api3('CustomField', 'getvalue', ['column_name' => $fieldName, 'return' => 'id']);
+ }
+ $fields = civicrm_api3($entity, 'getfields', ['action' => 'create'])['values'];
+ return $fields[$fieldName]['type'];
+ }
+
+}
--- /dev/null
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved. |
+ | |
+ | This work is published under the GNU AGPLv3 license with some |
+ | permitted exceptions and without any warranty. For full license |
+ | and copyright information, see https://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+/**
+ * The CiviCRM duplicate discovery engine is based on an
+ * algorithm designed by David Strauss <david@fourkitchens.com>.
+ */
+class CRM_Dedupe_BAO_DedupeRuleGroup extends CRM_Dedupe_DAO_DedupeRuleGroup {
+
+ /**
+ * Ids of the contacts to limit the SQL queries (whole-database queries otherwise)
+ * @var array
+ */
+ public $contactIds = [];
+
+ /**
+ * Set the contact IDs to restrict the dedupe to.
+ *
+ * @param array $contactIds
+ */
+ public function setContactIds($contactIds) {
+ $this->contactIds = $contactIds;
+ }
+
+ /**
+ * Params to dedupe against (queries against the whole contact set otherwise)
+ * @var array
+ */
+ public $params = [];
+
+ /**
+ * If there are no rules in rule group.
+ * @var bool
+ */
+ public $noRules = FALSE;
+
+ protected $temporaryTables = [];
+
+ /**
+ * Return a structure holding the supported tables, fields and their titles
+ *
+ * @param string $requestedType
+ * The requested contact type.
+ *
+ * @return array
+ * a table-keyed array of field-keyed arrays holding supported fields' titles
+ */
+ public static function supportedFields($requestedType) {
+ static $fields = NULL;
+ if (!$fields) {
+ // this is needed, as we're piggy-backing importableFields() below
+ $replacements = [
+ 'civicrm_country.name' => 'civicrm_address.country_id',
+ 'civicrm_county.name' => 'civicrm_address.county_id',
+ 'civicrm_state_province.name' => 'civicrm_address.state_province_id',
+ 'gender.label' => 'civicrm_contact.gender_id',
+ 'individual_prefix.label' => 'civicrm_contact.prefix_id',
+ 'individual_suffix.label' => 'civicrm_contact.suffix_id',
+ 'addressee.label' => 'civicrm_contact.addressee_id',
+ 'email_greeting.label' => 'civicrm_contact.email_greeting_id',
+ 'postal_greeting.label' => 'civicrm_contact.postal_greeting_id',
+ 'civicrm_phone.phone' => 'civicrm_phone.phone_numeric',
+ ];
+ // the table names we support in dedupe rules - a filter for importableFields()
+ $supportedTables = [
+ 'civicrm_address',
+ 'civicrm_contact',
+ 'civicrm_email',
+ 'civicrm_im',
+ 'civicrm_note',
+ 'civicrm_openid',
+ 'civicrm_phone',
+ ];
+
+ foreach (['Individual', 'Organization', 'Household'] as $ctype) {
+ // take the table.field pairs and their titles from importableFields() if the table is supported
+ foreach (CRM_Contact_BAO_Contact::importableFields($ctype) as $iField) {
+ if (isset($iField['where'])) {
+ $where = $iField['where'];
+ if (isset($replacements[$where])) {
+ $where = $replacements[$where];
+ }
+ list($table, $field) = explode('.', $where);
+ if (!in_array($table, $supportedTables)) {
+ continue;
+ }
+ $fields[$ctype][$table][$field] = $iField['title'];
+ }
+ }
+ // Note that most of the fields available come from 'importable fields' -
+ // I thought about making this field 'importable' but it felt like there might be unknown consequences
+ // so I opted for just adding it in & securing it with a unit test.
+ /// Example usage of sort_name - It is possible to alter sort name via hook so 2 organization names might differ as in
+ // Justice League vs The Justice League but these could have the same sort_name if 'the the'
+ // exension is installed (https://github.com/eileenmcnaughton/org.wikimedia.thethe)
+ $fields[$ctype]['civicrm_contact']['sort_name'] = ts('Sort Name');
+ // add custom data fields
+ foreach (CRM_Core_BAO_CustomGroup::getTree($ctype, NULL, NULL, -1) as $key => $cg) {
+ if (!is_int($key)) {
+ continue;
+ }
+ foreach ($cg['fields'] as $cf) {
+ $fields[$ctype][$cg['table_name']][$cf['column_name']] = $cf['label'];
+ }
+ }
+ }
+ }
+ CRM_Utils_Hook::dupeQuery(CRM_Core_DAO::$_nullObject, 'supportedFields', $fields);
+ return !empty($fields[$requestedType]) ? $fields[$requestedType] : [];
+ }
+
+ /**
+ * Return the SQL query for dropping the temporary table.
+ */
+ public function tableDropQuery() {
+ return 'DROP TEMPORARY TABLE IF EXISTS dedupe';
+ }
+
+ /**
+ * Return a set of SQL queries whose cummulative weights will mark matched
+ * records for the RuleGroup::threasholdQuery() to retrieve.
+ */
+ public function tableQuery() {
+ // make sure we've got a fetched dbrecord, not sure if this is enforced
+ if (!$this->name == NULL || $this->is_reserved == NULL) {
+ $this->find(TRUE);
+ }
+
+ // Reserved Rule Groups can optionally get special treatment by
+ // implementing an optimization class and returning a query array.
+ if ($this->is_reserved &&
+ CRM_Utils_File::isIncludable("CRM/Dedupe/BAO/QueryBuilder/{$this->name}.php")
+ ) {
+ $command = empty($this->params) ? 'internal' : 'record';
+ $queries = call_user_func(["CRM_Dedupe_BAO_QueryBuilder_{$this->name}", $command], $this);
+ }
+ else {
+ // All other rule groups have queries generated by the member dedupe
+ // rules defined in the administrative interface.
+
+ // Find all rules contained by this script sorted by weight so that
+ // their execution can be short circuited on RuleGroup::fillTable()
+ $bao = new CRM_Dedupe_BAO_Rule();
+ $bao->dedupe_rule_group_id = $this->id;
+ $bao->orderBy('rule_weight DESC');
+ $bao->find();
+
+ // Generate a SQL query for each rule in the rule group that is
+ // tailored to respect the param and contactId options provided.
+ $queries = [];
+ while ($bao->fetch()) {
+ $bao->contactIds = $this->contactIds;
+ $bao->params = $this->params;
+
+ // Skipping empty rules? Empty rules shouldn't exist; why check?
+ if ($query = $bao->sql()) {
+ $queries["{$bao->rule_table}.{$bao->rule_field}.{$bao->rule_weight}"] = $query;
+ }
+ }
+ }
+
+ // if there are no rules in this rule group
+ // add an empty query fulfilling the pattern
+ if (!$queries) {
+ $this->noRules = TRUE;
+ return [];
+ }
+
+ return $queries;
+ }
+
+ public function fillTable() {
+ // get the list of queries handy
+ $tableQueries = $this->tableQuery();
+
+ if ($this->params && !$this->noRules) {
+ $this->temporaryTables['dedupe'] = CRM_Utils_SQL_TempTable::build()
+ ->setCategory('dedupe')
+ ->createWithColumns("id1 int, weight int, UNIQUE UI_id1 (id1)")->getName();
+ $dedupeCopyTemporaryTableObject = CRM_Utils_SQL_TempTable::build()
+ ->setCategory('dedupe');
+ $this->temporaryTables['dedupe_copy'] = $dedupeCopyTemporaryTableObject->getName();
+ $insertClause = "INSERT INTO {$this->temporaryTables['dedupe']} (id1, weight)";
+ $groupByClause = "GROUP BY id1, weight";
+ $dupeCopyJoin = " JOIN {$this->temporaryTables['dedupe_copy']} ON {$this->temporaryTables['dedupe_copy']}.id1 = t1.column WHERE ";
+ }
+ else {
+ $this->temporaryTables['dedupe'] = CRM_Utils_SQL_TempTable::build()
+ ->setCategory('dedupe')
+ ->createWithColumns("id1 int, id2 int, weight int, UNIQUE UI_id1_id2 (id1, id2)")->getName();
+ $dedupeCopyTemporaryTableObject = CRM_Utils_SQL_TempTable::build()
+ ->setCategory('dedupe');
+ $this->temporaryTables['dedupe_copy'] = $dedupeCopyTemporaryTableObject->getName();
+ $insertClause = "INSERT INTO {$this->temporaryTables['dedupe']} (id1, id2, weight)";
+ $groupByClause = "GROUP BY id1, id2, weight";
+ $dupeCopyJoin = " JOIN {$this->temporaryTables['dedupe_copy']} ON {$this->temporaryTables['dedupe_copy']}.id1 = t1.column AND {$this->temporaryTables['dedupe_copy']}.id2 = t2.column WHERE ";
+ }
+ $patternColumn = '/t1.(\w+)/';
+ $exclWeightSum = [];
+
+ CRM_Utils_Hook::dupeQuery($this, 'table', $tableQueries);
+
+ while (!empty($tableQueries)) {
+ list($isInclusive, $isDie) = self::isQuerySetInclusive($tableQueries, $this->threshold, $exclWeightSum);
+
+ if ($isInclusive) {
+ // order queries by table count
+ self::orderByTableCount($tableQueries);
+
+ $weightSum = array_sum($exclWeightSum);
+ $searchWithinDupes = !empty($exclWeightSum) ? 1 : 0;
+
+ while (!empty($tableQueries)) {
+ // extract the next query ( and weight ) to be executed
+ $fieldWeight = array_keys($tableQueries);
+ $fieldWeight = $fieldWeight[0];
+ $query = array_shift($tableQueries);
+
+ if ($searchWithinDupes) {
+ // drop dedupe_copy table just in case if its already there.
+ $dedupeCopyTemporaryTableObject->drop();
+ // get prepared to search within already found dupes if $searchWithinDupes flag is set
+ $dedupeCopyTemporaryTableObject->createWithQuery("SELECT * FROM {$this->temporaryTables['dedupe']} WHERE weight >= {$weightSum}");
+
+ preg_match($patternColumn, $query, $matches);
+ $query = str_replace(' WHERE ', str_replace('column', $matches[1], $dupeCopyJoin), $query);
+
+ // CRM-19612: If there's a union, there will be two WHEREs, and you
+ // can't use the temp table twice.
+ if (preg_match('/' . $this->temporaryTables['dedupe_copy'] . '[\S\s]*(union)[\S\s]*' . $this->temporaryTables['dedupe_copy'] . '/i', $query, $matches, PREG_OFFSET_CAPTURE)) {
+ // Make a second temp table:
+ $this->temporaryTables['dedupe_copy_2'] = CRM_Utils_SQL_TempTable::build()
+ ->setCategory('dedupe')
+ ->createWithQuery("SELECT * FROM {$this->temporaryTables['dedupe']} WHERE weight >= {$weightSum}")
+ ->getName();
+ // After the union, use that new temp table:
+ $part1 = substr($query, 0, $matches[1][1]);
+ $query = $part1 . str_replace($this->temporaryTables['dedupe_copy'], $this->temporaryTables['dedupe_copy_2'], substr($query, $matches[1][1]));
+ }
+ }
+ $searchWithinDupes = 1;
+
+ // construct and execute the intermediate query
+ $query = "{$insertClause} {$query} {$groupByClause} ON DUPLICATE KEY UPDATE weight = weight + VALUES(weight)";
+ $dao = CRM_Core_DAO::executeQuery($query);
+
+ // FIXME: we need to be more acurate with affected rows, especially for insert vs duplicate insert.
+ // And that will help optimize further.
+ $affectedRows = $dao->affectedRows();
+
+ // In an inclusive situation, failure of any query means no further processing -
+ if ($affectedRows == 0) {
+ // reset to make sure no further execution is done.
+ $tableQueries = [];
+ break;
+ }
+ $weightSum = substr($fieldWeight, strrpos($fieldWeight, '.') + 1) + $weightSum;
+ }
+ // An exclusive situation -
+ }
+ elseif (!$isDie) {
+ // since queries are already sorted by weights, we can continue as is
+ $fieldWeight = array_keys($tableQueries);
+ $fieldWeight = $fieldWeight[0];
+ $query = array_shift($tableQueries);
+ $query = "{$insertClause} {$query} {$groupByClause} ON DUPLICATE KEY UPDATE weight = weight + VALUES(weight)";
+ $dao = CRM_Core_DAO::executeQuery($query);
+ if ($dao->affectedRows() >= 1) {
+ $exclWeightSum[] = substr($fieldWeight, strrpos($fieldWeight, '.') + 1);
+ }
+ }
+ else {
+ // its a die situation
+ break;
+ }
+ }
+ }
+
+ /**
+ * Function to determine if a given query set contains inclusive or exclusive set of weights.
+ * The function assumes that the query set is already ordered by weight in desc order.
+ * @param $tableQueries
+ * @param $threshold
+ * @param array $exclWeightSum
+ *
+ * @return array
+ */
+ public static function isQuerySetInclusive($tableQueries, $threshold, $exclWeightSum = []) {
+ $input = [];
+ foreach ($tableQueries as $key => $query) {
+ $input[] = substr($key, strrpos($key, '.') + 1);
+ }
+
+ if (!empty($exclWeightSum)) {
+ $input = array_merge($input, $exclWeightSum);
+ rsort($input);
+ }
+
+ if (count($input) == 1) {
+ return [FALSE, $input[0] < $threshold];
+ }
+
+ $totalCombinations = 0;
+ for ($i = 0; $i < count($input); $i++) {
+ $combination = [$input[$i]];
+ if (array_sum($combination) >= $threshold) {
+ $totalCombinations++;
+ continue;
+ }
+ for ($j = $i + 1; $j < count($input); $j++) {
+ $combination[] = $input[$j];
+ if (array_sum($combination) >= $threshold) {
+ $totalCombinations++;
+ }
+ }
+ }
+ return [$totalCombinations == 1, $totalCombinations <= 0];
+ }
+
+ /**
+ * sort queries by number of records for the table associated with them.
+ * @param $tableQueries
+ */
+ public static function orderByTableCount(&$tableQueries) {
+ static $tableCount = [];
+
+ $tempArray = [];
+ foreach ($tableQueries as $key => $query) {
+ $table = explode(".", $key);
+ $table = $table[0];
+ if (!array_key_exists($table, $tableCount)) {
+ $query = "SELECT COUNT(*) FROM {$table}";
+ $tableCount[$table] = CRM_Core_DAO::singleValueQuery($query);
+ }
+ $tempArray[$key] = $tableCount[$table];
+ }
+
+ asort($tempArray);
+ foreach ($tempArray as $key => $count) {
+ $tempArray[$key] = $tableQueries[$key];
+ }
+ $tableQueries = $tempArray;
+ }
+
+ /**
+ * Return the SQL query for getting only the interesting results out of the dedupe table.
+ *
+ * @$checkPermission boolean $params a flag to indicate if permission should be considered.
+ * default is to always check permissioning but public pages for example might not want
+ * permission to be checked for anonymous users. Refer CRM-6211. We might be beaking
+ * Multi-Site dedupe for public pages.
+ *
+ * @param bool $checkPermission
+ *
+ * @return string
+ */
+ public function thresholdQuery($checkPermission = TRUE) {
+ $this->_aclFrom = '';
+ $aclWhere = '';
+
+ if ($this->params && !$this->noRules) {
+ if ($checkPermission) {
+ list($this->_aclFrom, $aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause('civicrm_contact');
+ $aclWhere = $aclWhere ? "AND {$aclWhere}" : '';
+ }
+ $query = "SELECT {$this->temporaryTables['dedupe']}.id1 as id
+ FROM {$this->temporaryTables['dedupe']} JOIN civicrm_contact ON {$this->temporaryTables['dedupe']}.id1 = civicrm_contact.id {$this->_aclFrom}
+ WHERE contact_type = '{$this->contact_type}' AND is_deleted = 0 $aclWhere
+ AND weight >= {$this->threshold}";
+ }
+ else {
+ $aclWhere = '';
+ if ($checkPermission) {
+ list($this->_aclFrom, $aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause(['c1', 'c2']);
+ $aclWhere = $aclWhere ? "AND {$aclWhere}" : '';
+ }
+ $query = "SELECT IF({$this->temporaryTables['dedupe']}.id1 < {$this->temporaryTables['dedupe']}.id2, {$this->temporaryTables['dedupe']}.id1, {$this->temporaryTables['dedupe']}.id2) as id1,
+ IF({$this->temporaryTables['dedupe']}.id1 < {$this->temporaryTables['dedupe']}.id2, {$this->temporaryTables['dedupe']}.id2, {$this->temporaryTables['dedupe']}.id1) as id2, {$this->temporaryTables['dedupe']}.weight
+ FROM {$this->temporaryTables['dedupe']} JOIN civicrm_contact c1 ON {$this->temporaryTables['dedupe']}.id1 = c1.id
+ JOIN civicrm_contact c2 ON {$this->temporaryTables['dedupe']}.id2 = c2.id {$this->_aclFrom}
+ LEFT JOIN civicrm_dedupe_exception exc ON {$this->temporaryTables['dedupe']}.id1 = exc.contact_id1 AND {$this->temporaryTables['dedupe']}.id2 = exc.contact_id2
+ WHERE c1.contact_type = '{$this->contact_type}' AND
+ c2.contact_type = '{$this->contact_type}'
+ AND c1.is_deleted = 0 AND c2.is_deleted = 0
+ {$aclWhere}
+ AND weight >= {$this->threshold} AND exc.contact_id1 IS NULL";
+ }
+
+ CRM_Utils_Hook::dupeQuery($this, 'threshold', $query);
+ return $query;
+ }
+
+ /**
+ * find fields related to a rule group.
+ *
+ * @param array $params
+ *
+ * @return array
+ * (rule field => weight) array and threshold associated to rule group
+ */
+ public static function dedupeRuleFieldsWeight($params) {
+ $rgBao = new CRM_Dedupe_BAO_RuleGroup();
+ $rgBao->contact_type = $params['contact_type'];
+ if (!empty($params['id'])) {
+ // accept an ID if provided
+ $rgBao->id = $params['id'];
+ }
+ else {
+ $rgBao->used = $params['used'];
+ }
+ $rgBao->find(TRUE);
+
+ $ruleBao = new CRM_Dedupe_BAO_Rule();
+ $ruleBao->dedupe_rule_group_id = $rgBao->id;
+ $ruleBao->find();
+ $ruleFields = [];
+ while ($ruleBao->fetch()) {
+ $field_name = $ruleBao->rule_field;
+ if ($field_name == 'phone_numeric') {
+ $field_name = 'phone';
+ }
+ $ruleFields[$field_name] = $ruleBao->rule_weight;
+ }
+
+ return [$ruleFields, $rgBao->threshold];
+ }
+
+ /**
+ * Get all of the combinations of fields that would work with a rule.
+ *
+ * @param array $rgFields
+ * @param int $threshold
+ * @param array $combos
+ * @param array $running
+ */
+ public static function combos($rgFields, $threshold, &$combos, $running = []) {
+ foreach ($rgFields as $rgField => $weight) {
+ unset($rgFields[$rgField]);
+ $diff = $threshold - $weight;
+ $runningnow = $running;
+ $runningnow[] = $rgField;
+ if ($diff > 0) {
+ self::combos($rgFields, $diff, $combos, $runningnow);
+ }
+ else {
+ $combos[] = $runningnow;
+ }
+ }
+ }
+
+ /**
+ * Get an array of rule group id to rule group name
+ * for all th groups for that contactType. If contactType
+ * not specified, do it for all
+ *
+ * @param string $contactType
+ * Individual, Household or Organization.
+ *
+ *
+ * @return array
+ * id => "nice name" of rule group
+ */
+ public static function getByType($contactType = NULL) {
+ $dao = new CRM_Dedupe_DAO_RuleGroup();
+
+ if ($contactType) {
+ $dao->contact_type = $contactType;
+ }
+
+ $dao->find();
+ $result = [];
+ while ($dao->fetch()) {
+ $title = !empty($dao->title) ? $dao->title : (!empty($dao->name) ? $dao->name : $dao->contact_type);
+
+ $name = "$title - {$dao->used}";
+ $result[$dao->id] = $name;
+ }
+ return $result;
+ }
+
+ /**
+ * Get the cached contact type for a particular rule group.
+ *
+ * @param int $rule_group_id
+ *
+ * @return string
+ */
+ public static function getContactTypeForRuleGroup($rule_group_id) {
+ if (!isset(\Civi::$statics[__CLASS__]) || !isset(\Civi::$statics[__CLASS__]['rule_groups'])) {
+ \Civi::$statics[__CLASS__]['rule_groups'] = [];
+ }
+ if (empty(\Civi::$statics[__CLASS__]['rule_groups'][$rule_group_id])) {
+ \Civi::$statics[__CLASS__]['rule_groups'][$rule_group_id]['contact_type'] = CRM_Core_DAO::getFieldValue(
+ 'CRM_Dedupe_DAO_RuleGroup',
+ $rule_group_id,
+ 'contact_type'
+ );
+ }
+
+ return \Civi::$statics[__CLASS__]['rule_groups'][$rule_group_id]['contact_type'];
+ }
+
+}
/**
* Manages dedupe exceptions - ie pairs marked as non-duplicates.
*/
-class CRM_Dedupe_BAO_Exception extends CRM_Dedupe_DAO_Exception {
-
- /**
- * Create a dedupe exception record.
- *
- * @param array $params
- *
- * @return \CRM_Dedupe_BAO_Exception
- */
- public static function create($params) {
- $hook = empty($params['id']) ? 'create' : 'edit';
- CRM_Utils_Hook::pre($hook, 'Exception', CRM_Utils_Array::value('id', $params), $params);
- $contact1 = $params['contact_id1'] ?? NULL;
- $contact2 = $params['contact_id2'] ?? NULL;
- $dao = new CRM_Dedupe_BAO_Exception();
- $dao->copyValues($params);
- if ($contact1 && $contact2) {
- CRM_Core_DAO::singleValueQuery("
- DELETE FROM civicrm_prevnext_cache
- WHERE (entity_id1 = %1 AND entity_id2 = %2)
- OR (entity_id1 = %2 AND entity_id2 = %2)",
- [1 => [$contact1, 'Integer'], 2 => [$contact2, 'Integer']]
- );
- if ($contact2 < $contact1) {
- // These are expected to be saved lowest first.
- $dao->contact_id1 = $contact2;
- $dao->contact_id2 = $contact1;
- }
- }
- $dao->save();
-
- CRM_Utils_Hook::post($hook, 'Exception', $dao->id, $dao);
- return $dao;
- }
-
-}
+class CRM_Dedupe_BAO_Exception extends CRM_Dedupe_BAO_DedupeException {}
* The CiviCRM duplicate discovery engine is based on an
* algorithm designed by David Strauss <david@fourkitchens.com>.
*/
-class CRM_Dedupe_BAO_Rule extends CRM_Dedupe_DAO_Rule {
-
- /**
- * Ids of the contacts to limit the SQL queries (whole-database queries otherwise)
- * @var array
- */
- public $contactIds = [];
-
- /**
- * Params to dedupe against (queries against the whole contact set otherwise)
- * @var array
- */
- public $params = [];
-
- /**
- * Return the SQL query for the given rule - either for finding matching
- * pairs of contacts, or for matching against the $params variable (if set).
- *
- * @return string
- * SQL query performing the search
- *
- * @throws \CRM_Core_Exception
- * @throws \CiviCRM_API3_Exception
- */
- public function sql() {
- if ($this->params &&
- (!array_key_exists($this->rule_table, $this->params) ||
- !array_key_exists($this->rule_field, $this->params[$this->rule_table])
- )
- ) {
- // if params is present and doesn't have an entry for a field, don't construct the clause.
- return NULL;
- }
-
- // we need to initialise WHERE, ON and USING here, as some table types
- // extend them; $where is an array of required conditions, $on and
- // $using are arrays of required field matchings (for substring and
- // full matches, respectively)
- $where = [];
- $on = ["SUBSTR(t1.{$this->rule_field}, 1, {$this->rule_length}) = SUBSTR(t2.{$this->rule_field}, 1, {$this->rule_length})"];
-
- $innerJoinClauses = [
- "t1.{$this->rule_field} IS NOT NULL",
- "t2.{$this->rule_field} IS NOT NULL",
- "t1.{$this->rule_field} = t2.{$this->rule_field}",
- ];
-
- if (in_array($this->getFieldType($this->rule_field), CRM_Utils_Type::getTextTypes(), TRUE)) {
- $innerJoinClauses[] = "t1.{$this->rule_field} <> ''";
- $innerJoinClauses[] = "t2.{$this->rule_field} <> ''";
- }
-
- switch ($this->rule_table) {
- case 'civicrm_contact':
- $id = 'id';
- //we should restrict by contact type in the first step
- $sql = "SELECT contact_type FROM civicrm_dedupe_rule_group WHERE id = {$this->dedupe_rule_group_id};";
- $ct = CRM_Core_DAO::singleValueQuery($sql);
- if ($this->params) {
- $where[] = "t1.contact_type = '{$ct}'";
- }
- else {
- $where[] = "t1.contact_type = '{$ct}'";
- $where[] = "t2.contact_type = '{$ct}'";
- }
- break;
-
- case 'civicrm_address':
- case 'civicrm_email':
- case 'civicrm_im':
- case 'civicrm_openid':
- case 'civicrm_phone':
- $id = 'contact_id';
- break;
-
- case 'civicrm_note':
- $id = 'entity_id';
- if ($this->params) {
- $where[] = "t1.entity_table = 'civicrm_contact'";
- }
- else {
- $where[] = "t1.entity_table = 'civicrm_contact'";
- $where[] = "t2.entity_table = 'civicrm_contact'";
- }
- break;
-
- default:
- // custom data tables
- if (preg_match('/^civicrm_value_/', $this->rule_table) || preg_match('/^custom_value_/', $this->rule_table)) {
- $id = 'entity_id';
- }
- else {
- throw new CRM_Core_Exception("Unsupported rule_table for civicrm_dedupe_rule.id of {$this->id}");
- }
- break;
- }
-
- // build SELECT based on the field names containing contact ids
- // if there are params provided, id1 should be 0
- if ($this->params) {
- $select = "t1.$id id1, {$this->rule_weight} weight";
- $subSelect = 'id1, weight';
- }
- else {
- $select = "t1.$id id1, t2.$id id2, {$this->rule_weight} weight";
- $subSelect = 'id1, id2, weight';
- }
-
- // build FROM (and WHERE, if it's a parametrised search)
- // based on whether the rule is about substrings or not
- if ($this->params) {
- $from = "{$this->rule_table} t1";
- $str = 'NULL';
- if (isset($this->params[$this->rule_table][$this->rule_field])) {
- $str = trim(CRM_Utils_Type::escape($this->params[$this->rule_table][$this->rule_field], 'String'));
- }
- if ($this->rule_length) {
- $where[] = "SUBSTR(t1.{$this->rule_field}, 1, {$this->rule_length}) = SUBSTR('$str', 1, {$this->rule_length})";
- $where[] = "t1.{$this->rule_field} IS NOT NULL";
- }
- else {
- $where[] = "t1.{$this->rule_field} = '$str'";
- }
- }
- else {
- if ($this->rule_length) {
- $from = "{$this->rule_table} t1 JOIN {$this->rule_table} t2 ON (" . implode(' AND ', $on) . ")";
- }
- else {
- $from = "{$this->rule_table} t1 INNER JOIN {$this->rule_table} t2 ON (" . implode(' AND ', $innerJoinClauses) . ")";
- }
- }
-
- // finish building WHERE, also limit the results if requested
- if (!$this->params) {
- $where[] = "t1.$id < t2.$id";
- }
- $query = "SELECT $select FROM $from WHERE " . implode(' AND ', $where);
- if ($this->contactIds) {
- $cids = [];
- foreach ($this->contactIds as $cid) {
- $cids[] = CRM_Utils_Type::escape($cid, 'Integer');
- }
- if (count($cids) == 1) {
- $query .= " AND (t1.$id = {$cids[0]}) UNION $query AND t2.$id = {$cids[0]}";
- }
- else {
- $query .= " AND t1.$id IN (" . implode(',', $cids) . ")
- UNION $query AND t2.$id IN (" . implode(',', $cids) . ")";
- }
- // The `weight` is ambiguous in the context of the union; put the whole
- // thing in a subquery.
- $query = "SELECT $subSelect FROM ($query) subunion";
- }
-
- return $query;
- }
-
- /**
- * find fields related to a rule group.
- *
- * @param array $params contains the rule group property to identify rule group
- *
- * @return array
- * rule fields array associated to rule group
- */
- public static function dedupeRuleFields($params) {
- $rgBao = new CRM_Dedupe_BAO_RuleGroup();
- $rgBao->used = $params['used'];
- $rgBao->contact_type = $params['contact_type'];
- $rgBao->find(TRUE);
-
- $ruleBao = new CRM_Dedupe_BAO_Rule();
- $ruleBao->dedupe_rule_group_id = $rgBao->id;
- $ruleBao->find();
- $ruleFields = [];
- while ($ruleBao->fetch()) {
- $field_name = $ruleBao->rule_field;
- if ($field_name == 'phone_numeric') {
- $field_name = 'phone';
- }
- $ruleFields[] = $field_name;
- }
- return $ruleFields;
- }
-
- /**
- * @param int $cid
- * @param int $oid
- *
- * @return bool
- */
- public static function validateContacts($cid, $oid) {
- if (!$cid || !$oid) {
- return NULL;
- }
- $exception = new CRM_Dedupe_DAO_Exception();
- $exception->contact_id1 = $cid;
- $exception->contact_id2 = $oid;
- //make sure contact2 > contact1.
- if ($cid > $oid) {
- $exception->contact_id1 = $oid;
- $exception->contact_id2 = $cid;
- }
-
- return !$exception->find(TRUE);
- }
-
- /**
- * Get the specification for the given field.
- *
- * @param string $fieldName
- *
- * @return array
- * @throws \CiviCRM_API3_Exception
- */
- public function getFieldType($fieldName) {
- $entity = CRM_Core_DAO_AllCoreTables::getBriefName(CRM_Core_DAO_AllCoreTables::getClassForTable($this->rule_table));
- if (!$entity) {
- // This means we have stored a custom field rather than an entity name in rule_table, figure out the entity.
- $entity = civicrm_api3('CustomGroup', 'getvalue', ['table_name' => $this->rule_table, 'return' => 'extends']);
- if (in_array($entity, ['Individual', 'Household', 'Organization'])) {
- $entity = 'Contact';
- }
- $fieldName = 'custom_' . civicrm_api3('CustomField', 'getvalue', ['column_name' => $fieldName, 'return' => 'id']);
- }
- $fields = civicrm_api3($entity, 'getfields', ['action' => 'create'])['values'];
- return $fields[$fieldName]['type'];
- }
-
-}
+class CRM_Dedupe_BAO_Rule extends CRM_Dedupe_BAO_DedupeRule {}
* The CiviCRM duplicate discovery engine is based on an
* algorithm designed by David Strauss <david@fourkitchens.com>.
*/
-class CRM_Dedupe_BAO_RuleGroup extends CRM_Dedupe_DAO_RuleGroup {
-
- /**
- * Ids of the contacts to limit the SQL queries (whole-database queries otherwise)
- * @var array
- */
- public $contactIds = [];
-
- /**
- * Set the contact IDs to restrict the dedupe to.
- *
- * @param array $contactIds
- */
- public function setContactIds($contactIds) {
- $this->contactIds = $contactIds;
- }
-
- /**
- * Params to dedupe against (queries against the whole contact set otherwise)
- * @var array
- */
- public $params = [];
-
- /**
- * If there are no rules in rule group.
- * @var bool
- */
- public $noRules = FALSE;
-
- protected $temporaryTables = [];
-
- /**
- * Return a structure holding the supported tables, fields and their titles
- *
- * @param string $requestedType
- * The requested contact type.
- *
- * @return array
- * a table-keyed array of field-keyed arrays holding supported fields' titles
- */
- public static function supportedFields($requestedType) {
- static $fields = NULL;
- if (!$fields) {
- // this is needed, as we're piggy-backing importableFields() below
- $replacements = [
- 'civicrm_country.name' => 'civicrm_address.country_id',
- 'civicrm_county.name' => 'civicrm_address.county_id',
- 'civicrm_state_province.name' => 'civicrm_address.state_province_id',
- 'gender.label' => 'civicrm_contact.gender_id',
- 'individual_prefix.label' => 'civicrm_contact.prefix_id',
- 'individual_suffix.label' => 'civicrm_contact.suffix_id',
- 'addressee.label' => 'civicrm_contact.addressee_id',
- 'email_greeting.label' => 'civicrm_contact.email_greeting_id',
- 'postal_greeting.label' => 'civicrm_contact.postal_greeting_id',
- 'civicrm_phone.phone' => 'civicrm_phone.phone_numeric',
- ];
- // the table names we support in dedupe rules - a filter for importableFields()
- $supportedTables = [
- 'civicrm_address',
- 'civicrm_contact',
- 'civicrm_email',
- 'civicrm_im',
- 'civicrm_note',
- 'civicrm_openid',
- 'civicrm_phone',
- ];
-
- foreach (['Individual', 'Organization', 'Household'] as $ctype) {
- // take the table.field pairs and their titles from importableFields() if the table is supported
- foreach (CRM_Contact_BAO_Contact::importableFields($ctype) as $iField) {
- if (isset($iField['where'])) {
- $where = $iField['where'];
- if (isset($replacements[$where])) {
- $where = $replacements[$where];
- }
- list($table, $field) = explode('.', $where);
- if (!in_array($table, $supportedTables)) {
- continue;
- }
- $fields[$ctype][$table][$field] = $iField['title'];
- }
- }
- // Note that most of the fields available come from 'importable fields' -
- // I thought about making this field 'importable' but it felt like there might be unknown consequences
- // so I opted for just adding it in & securing it with a unit test.
- /// Example usage of sort_name - It is possible to alter sort name via hook so 2 organization names might differ as in
- // Justice League vs The Justice League but these could have the same sort_name if 'the the'
- // exension is installed (https://github.com/eileenmcnaughton/org.wikimedia.thethe)
- $fields[$ctype]['civicrm_contact']['sort_name'] = ts('Sort Name');
- // add custom data fields
- foreach (CRM_Core_BAO_CustomGroup::getTree($ctype, NULL, NULL, -1) as $key => $cg) {
- if (!is_int($key)) {
- continue;
- }
- foreach ($cg['fields'] as $cf) {
- $fields[$ctype][$cg['table_name']][$cf['column_name']] = $cf['label'];
- }
- }
- }
- }
- CRM_Utils_Hook::dupeQuery(CRM_Core_DAO::$_nullObject, 'supportedFields', $fields);
- return !empty($fields[$requestedType]) ? $fields[$requestedType] : [];
- }
-
- /**
- * Return the SQL query for dropping the temporary table.
- */
- public function tableDropQuery() {
- return 'DROP TEMPORARY TABLE IF EXISTS dedupe';
- }
-
- /**
- * Return a set of SQL queries whose cummulative weights will mark matched
- * records for the RuleGroup::threasholdQuery() to retrieve.
- */
- public function tableQuery() {
- // make sure we've got a fetched dbrecord, not sure if this is enforced
- if (!$this->name == NULL || $this->is_reserved == NULL) {
- $this->find(TRUE);
- }
-
- // Reserved Rule Groups can optionally get special treatment by
- // implementing an optimization class and returning a query array.
- if ($this->is_reserved &&
- CRM_Utils_File::isIncludable("CRM/Dedupe/BAO/QueryBuilder/{$this->name}.php")
- ) {
- $command = empty($this->params) ? 'internal' : 'record';
- $queries = call_user_func(["CRM_Dedupe_BAO_QueryBuilder_{$this->name}", $command], $this);
- }
- else {
- // All other rule groups have queries generated by the member dedupe
- // rules defined in the administrative interface.
-
- // Find all rules contained by this script sorted by weight so that
- // their execution can be short circuited on RuleGroup::fillTable()
- $bao = new CRM_Dedupe_BAO_Rule();
- $bao->dedupe_rule_group_id = $this->id;
- $bao->orderBy('rule_weight DESC');
- $bao->find();
-
- // Generate a SQL query for each rule in the rule group that is
- // tailored to respect the param and contactId options provided.
- $queries = [];
- while ($bao->fetch()) {
- $bao->contactIds = $this->contactIds;
- $bao->params = $this->params;
-
- // Skipping empty rules? Empty rules shouldn't exist; why check?
- if ($query = $bao->sql()) {
- $queries["{$bao->rule_table}.{$bao->rule_field}.{$bao->rule_weight}"] = $query;
- }
- }
- }
-
- // if there are no rules in this rule group
- // add an empty query fulfilling the pattern
- if (!$queries) {
- $this->noRules = TRUE;
- return [];
- }
-
- return $queries;
- }
-
- public function fillTable() {
- // get the list of queries handy
- $tableQueries = $this->tableQuery();
-
- if ($this->params && !$this->noRules) {
- $this->temporaryTables['dedupe'] = CRM_Utils_SQL_TempTable::build()
- ->setCategory('dedupe')
- ->createWithColumns("id1 int, weight int, UNIQUE UI_id1 (id1)")->getName();
- $dedupeCopyTemporaryTableObject = CRM_Utils_SQL_TempTable::build()
- ->setCategory('dedupe');
- $this->temporaryTables['dedupe_copy'] = $dedupeCopyTemporaryTableObject->getName();
- $insertClause = "INSERT INTO {$this->temporaryTables['dedupe']} (id1, weight)";
- $groupByClause = "GROUP BY id1, weight";
- $dupeCopyJoin = " JOIN {$this->temporaryTables['dedupe_copy']} ON {$this->temporaryTables['dedupe_copy']}.id1 = t1.column WHERE ";
- }
- else {
- $this->temporaryTables['dedupe'] = CRM_Utils_SQL_TempTable::build()
- ->setCategory('dedupe')
- ->createWithColumns("id1 int, id2 int, weight int, UNIQUE UI_id1_id2 (id1, id2)")->getName();
- $dedupeCopyTemporaryTableObject = CRM_Utils_SQL_TempTable::build()
- ->setCategory('dedupe');
- $this->temporaryTables['dedupe_copy'] = $dedupeCopyTemporaryTableObject->getName();
- $insertClause = "INSERT INTO {$this->temporaryTables['dedupe']} (id1, id2, weight)";
- $groupByClause = "GROUP BY id1, id2, weight";
- $dupeCopyJoin = " JOIN {$this->temporaryTables['dedupe_copy']} ON {$this->temporaryTables['dedupe_copy']}.id1 = t1.column AND {$this->temporaryTables['dedupe_copy']}.id2 = t2.column WHERE ";
- }
- $patternColumn = '/t1.(\w+)/';
- $exclWeightSum = [];
-
- CRM_Utils_Hook::dupeQuery($this, 'table', $tableQueries);
-
- while (!empty($tableQueries)) {
- list($isInclusive, $isDie) = self::isQuerySetInclusive($tableQueries, $this->threshold, $exclWeightSum);
-
- if ($isInclusive) {
- // order queries by table count
- self::orderByTableCount($tableQueries);
-
- $weightSum = array_sum($exclWeightSum);
- $searchWithinDupes = !empty($exclWeightSum) ? 1 : 0;
-
- while (!empty($tableQueries)) {
- // extract the next query ( and weight ) to be executed
- $fieldWeight = array_keys($tableQueries);
- $fieldWeight = $fieldWeight[0];
- $query = array_shift($tableQueries);
-
- if ($searchWithinDupes) {
- // drop dedupe_copy table just in case if its already there.
- $dedupeCopyTemporaryTableObject->drop();
- // get prepared to search within already found dupes if $searchWithinDupes flag is set
- $dedupeCopyTemporaryTableObject->createWithQuery("SELECT * FROM {$this->temporaryTables['dedupe']} WHERE weight >= {$weightSum}");
-
- preg_match($patternColumn, $query, $matches);
- $query = str_replace(' WHERE ', str_replace('column', $matches[1], $dupeCopyJoin), $query);
-
- // CRM-19612: If there's a union, there will be two WHEREs, and you
- // can't use the temp table twice.
- if (preg_match('/' . $this->temporaryTables['dedupe_copy'] . '[\S\s]*(union)[\S\s]*' . $this->temporaryTables['dedupe_copy'] . '/i', $query, $matches, PREG_OFFSET_CAPTURE)) {
- // Make a second temp table:
- $this->temporaryTables['dedupe_copy_2'] = CRM_Utils_SQL_TempTable::build()
- ->setCategory('dedupe')
- ->createWithQuery("SELECT * FROM {$this->temporaryTables['dedupe']} WHERE weight >= {$weightSum}")
- ->getName();
- // After the union, use that new temp table:
- $part1 = substr($query, 0, $matches[1][1]);
- $query = $part1 . str_replace($this->temporaryTables['dedupe_copy'], $this->temporaryTables['dedupe_copy_2'], substr($query, $matches[1][1]));
- }
- }
- $searchWithinDupes = 1;
-
- // construct and execute the intermediate query
- $query = "{$insertClause} {$query} {$groupByClause} ON DUPLICATE KEY UPDATE weight = weight + VALUES(weight)";
- $dao = CRM_Core_DAO::executeQuery($query);
-
- // FIXME: we need to be more acurate with affected rows, especially for insert vs duplicate insert.
- // And that will help optimize further.
- $affectedRows = $dao->affectedRows();
-
- // In an inclusive situation, failure of any query means no further processing -
- if ($affectedRows == 0) {
- // reset to make sure no further execution is done.
- $tableQueries = [];
- break;
- }
- $weightSum = substr($fieldWeight, strrpos($fieldWeight, '.') + 1) + $weightSum;
- }
- // An exclusive situation -
- }
- elseif (!$isDie) {
- // since queries are already sorted by weights, we can continue as is
- $fieldWeight = array_keys($tableQueries);
- $fieldWeight = $fieldWeight[0];
- $query = array_shift($tableQueries);
- $query = "{$insertClause} {$query} {$groupByClause} ON DUPLICATE KEY UPDATE weight = weight + VALUES(weight)";
- $dao = CRM_Core_DAO::executeQuery($query);
- if ($dao->affectedRows() >= 1) {
- $exclWeightSum[] = substr($fieldWeight, strrpos($fieldWeight, '.') + 1);
- }
- }
- else {
- // its a die situation
- break;
- }
- }
- }
-
- /**
- * Function to determine if a given query set contains inclusive or exclusive set of weights.
- * The function assumes that the query set is already ordered by weight in desc order.
- * @param $tableQueries
- * @param $threshold
- * @param array $exclWeightSum
- *
- * @return array
- */
- public static function isQuerySetInclusive($tableQueries, $threshold, $exclWeightSum = []) {
- $input = [];
- foreach ($tableQueries as $key => $query) {
- $input[] = substr($key, strrpos($key, '.') + 1);
- }
-
- if (!empty($exclWeightSum)) {
- $input = array_merge($input, $exclWeightSum);
- rsort($input);
- }
-
- if (count($input) == 1) {
- return [FALSE, $input[0] < $threshold];
- }
-
- $totalCombinations = 0;
- for ($i = 0; $i < count($input); $i++) {
- $combination = [$input[$i]];
- if (array_sum($combination) >= $threshold) {
- $totalCombinations++;
- continue;
- }
- for ($j = $i + 1; $j < count($input); $j++) {
- $combination[] = $input[$j];
- if (array_sum($combination) >= $threshold) {
- $totalCombinations++;
- }
- }
- }
- return [$totalCombinations == 1, $totalCombinations <= 0];
- }
-
- /**
- * sort queries by number of records for the table associated with them.
- * @param $tableQueries
- */
- public static function orderByTableCount(&$tableQueries) {
- static $tableCount = [];
-
- $tempArray = [];
- foreach ($tableQueries as $key => $query) {
- $table = explode(".", $key);
- $table = $table[0];
- if (!array_key_exists($table, $tableCount)) {
- $query = "SELECT COUNT(*) FROM {$table}";
- $tableCount[$table] = CRM_Core_DAO::singleValueQuery($query);
- }
- $tempArray[$key] = $tableCount[$table];
- }
-
- asort($tempArray);
- foreach ($tempArray as $key => $count) {
- $tempArray[$key] = $tableQueries[$key];
- }
- $tableQueries = $tempArray;
- }
-
- /**
- * Return the SQL query for getting only the interesting results out of the dedupe table.
- *
- * @$checkPermission boolean $params a flag to indicate if permission should be considered.
- * default is to always check permissioning but public pages for example might not want
- * permission to be checked for anonymous users. Refer CRM-6211. We might be beaking
- * Multi-Site dedupe for public pages.
- *
- * @param bool $checkPermission
- *
- * @return string
- */
- public function thresholdQuery($checkPermission = TRUE) {
- $this->_aclFrom = '';
- $aclWhere = '';
-
- if ($this->params && !$this->noRules) {
- if ($checkPermission) {
- list($this->_aclFrom, $aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause('civicrm_contact');
- $aclWhere = $aclWhere ? "AND {$aclWhere}" : '';
- }
- $query = "SELECT {$this->temporaryTables['dedupe']}.id1 as id
- FROM {$this->temporaryTables['dedupe']} JOIN civicrm_contact ON {$this->temporaryTables['dedupe']}.id1 = civicrm_contact.id {$this->_aclFrom}
- WHERE contact_type = '{$this->contact_type}' AND is_deleted = 0 $aclWhere
- AND weight >= {$this->threshold}";
- }
- else {
- $aclWhere = '';
- if ($checkPermission) {
- list($this->_aclFrom, $aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause(['c1', 'c2']);
- $aclWhere = $aclWhere ? "AND {$aclWhere}" : '';
- }
- $query = "SELECT IF({$this->temporaryTables['dedupe']}.id1 < {$this->temporaryTables['dedupe']}.id2, {$this->temporaryTables['dedupe']}.id1, {$this->temporaryTables['dedupe']}.id2) as id1,
- IF({$this->temporaryTables['dedupe']}.id1 < {$this->temporaryTables['dedupe']}.id2, {$this->temporaryTables['dedupe']}.id2, {$this->temporaryTables['dedupe']}.id1) as id2, {$this->temporaryTables['dedupe']}.weight
- FROM {$this->temporaryTables['dedupe']} JOIN civicrm_contact c1 ON {$this->temporaryTables['dedupe']}.id1 = c1.id
- JOIN civicrm_contact c2 ON {$this->temporaryTables['dedupe']}.id2 = c2.id {$this->_aclFrom}
- LEFT JOIN civicrm_dedupe_exception exc ON {$this->temporaryTables['dedupe']}.id1 = exc.contact_id1 AND {$this->temporaryTables['dedupe']}.id2 = exc.contact_id2
- WHERE c1.contact_type = '{$this->contact_type}' AND
- c2.contact_type = '{$this->contact_type}'
- AND c1.is_deleted = 0 AND c2.is_deleted = 0
- {$aclWhere}
- AND weight >= {$this->threshold} AND exc.contact_id1 IS NULL";
- }
-
- CRM_Utils_Hook::dupeQuery($this, 'threshold', $query);
- return $query;
- }
-
- /**
- * find fields related to a rule group.
- *
- * @param array $params
- *
- * @return array
- * (rule field => weight) array and threshold associated to rule group
- */
- public static function dedupeRuleFieldsWeight($params) {
- $rgBao = new CRM_Dedupe_BAO_RuleGroup();
- $rgBao->contact_type = $params['contact_type'];
- if (!empty($params['id'])) {
- // accept an ID if provided
- $rgBao->id = $params['id'];
- }
- else {
- $rgBao->used = $params['used'];
- }
- $rgBao->find(TRUE);
-
- $ruleBao = new CRM_Dedupe_BAO_Rule();
- $ruleBao->dedupe_rule_group_id = $rgBao->id;
- $ruleBao->find();
- $ruleFields = [];
- while ($ruleBao->fetch()) {
- $field_name = $ruleBao->rule_field;
- if ($field_name == 'phone_numeric') {
- $field_name = 'phone';
- }
- $ruleFields[$field_name] = $ruleBao->rule_weight;
- }
-
- return [$ruleFields, $rgBao->threshold];
- }
-
- /**
- * Get all of the combinations of fields that would work with a rule.
- *
- * @param array $rgFields
- * @param int $threshold
- * @param array $combos
- * @param array $running
- */
- public static function combos($rgFields, $threshold, &$combos, $running = []) {
- foreach ($rgFields as $rgField => $weight) {
- unset($rgFields[$rgField]);
- $diff = $threshold - $weight;
- $runningnow = $running;
- $runningnow[] = $rgField;
- if ($diff > 0) {
- self::combos($rgFields, $diff, $combos, $runningnow);
- }
- else {
- $combos[] = $runningnow;
- }
- }
- }
-
- /**
- * Get an array of rule group id to rule group name
- * for all th groups for that contactType. If contactType
- * not specified, do it for all
- *
- * @param string $contactType
- * Individual, Household or Organization.
- *
- *
- * @return array
- * id => "nice name" of rule group
- */
- public static function getByType($contactType = NULL) {
- $dao = new CRM_Dedupe_DAO_RuleGroup();
-
- if ($contactType) {
- $dao->contact_type = $contactType;
- }
-
- $dao->find();
- $result = [];
- while ($dao->fetch()) {
- $title = !empty($dao->title) ? $dao->title : (!empty($dao->name) ? $dao->name : $dao->contact_type);
-
- $name = "$title - {$dao->used}";
- $result[$dao->id] = $name;
- }
- return $result;
- }
-
- /**
- * Get the cached contact type for a particular rule group.
- *
- * @param int $rule_group_id
- *
- * @return string
- */
- public static function getContactTypeForRuleGroup($rule_group_id) {
- if (!isset(\Civi::$statics[__CLASS__]) || !isset(\Civi::$statics[__CLASS__]['rule_groups'])) {
- \Civi::$statics[__CLASS__]['rule_groups'] = [];
- }
- if (empty(\Civi::$statics[__CLASS__]['rule_groups'][$rule_group_id])) {
- \Civi::$statics[__CLASS__]['rule_groups'][$rule_group_id]['contact_type'] = CRM_Core_DAO::getFieldValue(
- 'CRM_Dedupe_DAO_RuleGroup',
- $rule_group_id,
- 'contact_type'
- );
- }
-
- return \Civi::$statics[__CLASS__]['rule_groups'][$rule_group_id]['contact_type'];
- }
-
-}
+class CRM_Dedupe_BAO_RuleGroup extends CRM_Dedupe_BAO_DedupeRuleGroup {}
--- /dev/null
+<?php
+
+/**
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ *
+ * Generated from xml/schema/CRM/Dedupe/DedupeException.xml
+ * DO NOT EDIT. Generated by CRM_Core_CodeGen
+ * (GenCodeChecksum:fdf10a930b1f5d921391bc085a8e486b)
+ */
+
+/**
+ * Database access object for the DedupeException entity.
+ */
+class CRM_Dedupe_DAO_DedupeException extends CRM_Core_DAO {
+ const EXT = 'civicrm';
+ const TABLE_ADDED = '3.3';
+
+ /**
+ * Static instance to hold the table name.
+ *
+ * @var string
+ */
+ public static $_tableName = 'civicrm_dedupe_exception';
+
+ /**
+ * Should CiviCRM log any modifications to this table in the civicrm_log table.
+ *
+ * @var bool
+ */
+ public static $_log = FALSE;
+
+ /**
+ * Unique dedupe exception id
+ *
+ * @var int
+ */
+ public $id;
+
+ /**
+ * FK to Contact ID
+ *
+ * @var int
+ */
+ public $contact_id1;
+
+ /**
+ * FK to Contact ID
+ *
+ * @var int
+ */
+ public $contact_id2;
+
+ /**
+ * Class constructor.
+ */
+ public function __construct() {
+ $this->__table = 'civicrm_dedupe_exception';
+ parent::__construct();
+ }
+
+ /**
+ * Returns localized title of this entity.
+ *
+ * @param bool $plural
+ * Whether to return the plural version of the title.
+ */
+ public static function getEntityTitle($plural = FALSE) {
+ return $plural ? ts('Dedupe Exceptions') : ts('Dedupe Exception');
+ }
+
+ /**
+ * Returns foreign keys and entity references.
+ *
+ * @return array
+ * [CRM_Core_Reference_Interface]
+ */
+ public static function getReferenceColumns() {
+ if (!isset(Civi::$statics[__CLASS__]['links'])) {
+ Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__);
+ Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id1', 'civicrm_contact', 'id');
+ Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id2', 'civicrm_contact', 'id');
+ CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']);
+ }
+ return Civi::$statics[__CLASS__]['links'];
+ }
+
+ /**
+ * Returns all the column names of this table
+ *
+ * @return array
+ */
+ public static function &fields() {
+ if (!isset(Civi::$statics[__CLASS__]['fields'])) {
+ Civi::$statics[__CLASS__]['fields'] = [
+ 'id' => [
+ 'name' => 'id',
+ 'type' => CRM_Utils_Type::T_INT,
+ 'title' => ts('Dedupe Exception ID'),
+ 'description' => ts('Unique dedupe exception id'),
+ 'required' => TRUE,
+ 'where' => 'civicrm_dedupe_exception.id',
+ 'table_name' => 'civicrm_dedupe_exception',
+ 'entity' => 'DedupeException',
+ 'bao' => 'CRM_Dedupe_DAO_DedupeException',
+ 'localizable' => 0,
+ 'html' => [
+ 'type' => 'Number',
+ ],
+ 'readonly' => TRUE,
+ 'add' => '3.3',
+ ],
+ 'contact_id1' => [
+ 'name' => 'contact_id1',
+ 'type' => CRM_Utils_Type::T_INT,
+ 'title' => ts('First Dupe Contact ID'),
+ 'description' => ts('FK to Contact ID'),
+ 'required' => TRUE,
+ 'where' => 'civicrm_dedupe_exception.contact_id1',
+ 'table_name' => 'civicrm_dedupe_exception',
+ 'entity' => 'DedupeException',
+ 'bao' => 'CRM_Dedupe_DAO_DedupeException',
+ 'localizable' => 0,
+ 'FKClassName' => 'CRM_Contact_DAO_Contact',
+ 'html' => [
+ 'label' => ts("First Dupe Contact"),
+ ],
+ 'add' => '3.3',
+ ],
+ 'contact_id2' => [
+ 'name' => 'contact_id2',
+ 'type' => CRM_Utils_Type::T_INT,
+ 'title' => ts('Second Dupe Contact ID'),
+ 'description' => ts('FK to Contact ID'),
+ 'required' => TRUE,
+ 'where' => 'civicrm_dedupe_exception.contact_id2',
+ 'table_name' => 'civicrm_dedupe_exception',
+ 'entity' => 'DedupeException',
+ 'bao' => 'CRM_Dedupe_DAO_DedupeException',
+ 'localizable' => 0,
+ 'FKClassName' => 'CRM_Contact_DAO_Contact',
+ 'html' => [
+ 'label' => ts("Second Dupe Contact"),
+ ],
+ 'add' => '3.3',
+ ],
+ ];
+ CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']);
+ }
+ return Civi::$statics[__CLASS__]['fields'];
+ }
+
+ /**
+ * Return a mapping from field-name to the corresponding key (as used in fields()).
+ *
+ * @return array
+ * Array(string $name => string $uniqueName).
+ */
+ public static function &fieldKeys() {
+ if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) {
+ Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields()));
+ }
+ return Civi::$statics[__CLASS__]['fieldKeys'];
+ }
+
+ /**
+ * Returns the names of this table
+ *
+ * @return string
+ */
+ public static function getTableName() {
+ return self::$_tableName;
+ }
+
+ /**
+ * Returns if this table needs to be logged
+ *
+ * @return bool
+ */
+ public function getLog() {
+ return self::$_log;
+ }
+
+ /**
+ * Returns the list of fields that can be imported
+ *
+ * @param bool $prefix
+ *
+ * @return array
+ */
+ public static function &import($prefix = FALSE) {
+ $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'dedupe_exception', $prefix, []);
+ return $r;
+ }
+
+ /**
+ * Returns the list of fields that can be exported
+ *
+ * @param bool $prefix
+ *
+ * @return array
+ */
+ public static function &export($prefix = FALSE) {
+ $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'dedupe_exception', $prefix, []);
+ return $r;
+ }
+
+ /**
+ * Returns the list of indices
+ *
+ * @param bool $localize
+ *
+ * @return array
+ */
+ public static function indices($localize = TRUE) {
+ $indices = [
+ 'UI_contact_id1_contact_id2' => [
+ 'name' => 'UI_contact_id1_contact_id2',
+ 'field' => [
+ 0 => 'contact_id1',
+ 1 => 'contact_id2',
+ ],
+ 'localizable' => FALSE,
+ 'unique' => TRUE,
+ 'sig' => 'civicrm_dedupe_exception::1::contact_id1::contact_id2',
+ ],
+ ];
+ return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices;
+ }
+
+}
--- /dev/null
+<?php
+
+/**
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ *
+ * Generated from xml/schema/CRM/Dedupe/DedupeRule.xml
+ * DO NOT EDIT. Generated by CRM_Core_CodeGen
+ * (GenCodeChecksum:782d96ecc76d4020270fce04574a39b5)
+ */
+
+/**
+ * Database access object for the DedupeRule entity.
+ */
+class CRM_Dedupe_DAO_DedupeRule extends CRM_Core_DAO {
+ const EXT = 'civicrm';
+ const TABLE_ADDED = '1.8';
+
+ /**
+ * Static instance to hold the table name.
+ *
+ * @var string
+ */
+ public static $_tableName = 'civicrm_dedupe_rule';
+
+ /**
+ * Should CiviCRM log any modifications to this table in the civicrm_log table.
+ *
+ * @var bool
+ */
+ public static $_log = FALSE;
+
+ /**
+ * Unique dedupe rule id
+ *
+ * @var int
+ */
+ public $id;
+
+ /**
+ * The id of the rule group this rule belongs to
+ *
+ * @var int
+ */
+ public $dedupe_rule_group_id;
+
+ /**
+ * The name of the table this rule is about
+ *
+ * @var string
+ */
+ public $rule_table;
+
+ /**
+ * The name of the field of the table referenced in rule_table
+ *
+ * @var string
+ */
+ public $rule_field;
+
+ /**
+ * The length of the matching substring
+ *
+ * @var int
+ */
+ public $rule_length;
+
+ /**
+ * The weight of the rule
+ *
+ * @var int
+ */
+ public $rule_weight;
+
+ /**
+ * Class constructor.
+ */
+ public function __construct() {
+ $this->__table = 'civicrm_dedupe_rule';
+ parent::__construct();
+ }
+
+ /**
+ * Returns localized title of this entity.
+ *
+ * @param bool $plural
+ * Whether to return the plural version of the title.
+ */
+ public static function getEntityTitle($plural = FALSE) {
+ return $plural ? ts('Dedupe Rules') : ts('Dedupe Rule');
+ }
+
+ /**
+ * Returns foreign keys and entity references.
+ *
+ * @return array
+ * [CRM_Core_Reference_Interface]
+ */
+ public static function getReferenceColumns() {
+ if (!isset(Civi::$statics[__CLASS__]['links'])) {
+ Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__);
+ Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'dedupe_rule_group_id', 'civicrm_dedupe_rule_group', 'id');
+ CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']);
+ }
+ return Civi::$statics[__CLASS__]['links'];
+ }
+
+ /**
+ * Returns all the column names of this table
+ *
+ * @return array
+ */
+ public static function &fields() {
+ if (!isset(Civi::$statics[__CLASS__]['fields'])) {
+ Civi::$statics[__CLASS__]['fields'] = [
+ 'id' => [
+ 'name' => 'id',
+ 'type' => CRM_Utils_Type::T_INT,
+ 'title' => ts('Dedupe Rule ID'),
+ 'description' => ts('Unique dedupe rule id'),
+ 'required' => TRUE,
+ 'where' => 'civicrm_dedupe_rule.id',
+ 'table_name' => 'civicrm_dedupe_rule',
+ 'entity' => 'DedupeRule',
+ 'bao' => 'CRM_Dedupe_BAO_DedupeRule',
+ 'localizable' => 0,
+ 'html' => [
+ 'type' => 'Number',
+ ],
+ 'readonly' => TRUE,
+ 'add' => '1.8',
+ ],
+ 'dedupe_rule_group_id' => [
+ 'name' => 'dedupe_rule_group_id',
+ 'type' => CRM_Utils_Type::T_INT,
+ 'title' => ts('Group ID'),
+ 'description' => ts('The id of the rule group this rule belongs to'),
+ 'required' => TRUE,
+ 'where' => 'civicrm_dedupe_rule.dedupe_rule_group_id',
+ 'table_name' => 'civicrm_dedupe_rule',
+ 'entity' => 'DedupeRule',
+ 'bao' => 'CRM_Dedupe_BAO_DedupeRule',
+ 'localizable' => 0,
+ 'FKClassName' => 'CRM_Dedupe_DAO_DedupeRuleGroup',
+ 'html' => [
+ 'label' => ts("Group"),
+ ],
+ 'add' => '1.8',
+ ],
+ 'rule_table' => [
+ 'name' => 'rule_table',
+ 'type' => CRM_Utils_Type::T_STRING,
+ 'title' => ts('Rule Table'),
+ 'description' => ts('The name of the table this rule is about'),
+ 'required' => TRUE,
+ 'maxlength' => 64,
+ 'size' => CRM_Utils_Type::BIG,
+ 'where' => 'civicrm_dedupe_rule.rule_table',
+ 'table_name' => 'civicrm_dedupe_rule',
+ 'entity' => 'DedupeRule',
+ 'bao' => 'CRM_Dedupe_BAO_DedupeRule',
+ 'localizable' => 0,
+ 'add' => '1.8',
+ ],
+ 'rule_field' => [
+ 'name' => 'rule_field',
+ 'type' => CRM_Utils_Type::T_STRING,
+ 'title' => ts('Rule Field'),
+ 'description' => ts('The name of the field of the table referenced in rule_table'),
+ 'required' => TRUE,
+ 'maxlength' => 64,
+ 'size' => CRM_Utils_Type::BIG,
+ 'where' => 'civicrm_dedupe_rule.rule_field',
+ 'table_name' => 'civicrm_dedupe_rule',
+ 'entity' => 'DedupeRule',
+ 'bao' => 'CRM_Dedupe_BAO_DedupeRule',
+ 'localizable' => 0,
+ 'add' => '1.8',
+ ],
+ 'rule_length' => [
+ 'name' => 'rule_length',
+ 'type' => CRM_Utils_Type::T_INT,
+ 'title' => ts('Rule Length'),
+ 'description' => ts('The length of the matching substring'),
+ 'where' => 'civicrm_dedupe_rule.rule_length',
+ 'table_name' => 'civicrm_dedupe_rule',
+ 'entity' => 'DedupeRule',
+ 'bao' => 'CRM_Dedupe_BAO_DedupeRule',
+ 'localizable' => 0,
+ 'html' => [
+ 'type' => 'Text',
+ ],
+ 'add' => '1.8',
+ ],
+ 'rule_weight' => [
+ 'name' => 'rule_weight',
+ 'type' => CRM_Utils_Type::T_INT,
+ 'title' => ts('Order'),
+ 'description' => ts('The weight of the rule'),
+ 'required' => TRUE,
+ 'where' => 'civicrm_dedupe_rule.rule_weight',
+ 'table_name' => 'civicrm_dedupe_rule',
+ 'entity' => 'DedupeRule',
+ 'bao' => 'CRM_Dedupe_BAO_DedupeRule',
+ 'localizable' => 0,
+ 'html' => [
+ 'type' => 'Text',
+ ],
+ 'add' => '1.8',
+ ],
+ ];
+ CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']);
+ }
+ return Civi::$statics[__CLASS__]['fields'];
+ }
+
+ /**
+ * Return a mapping from field-name to the corresponding key (as used in fields()).
+ *
+ * @return array
+ * Array(string $name => string $uniqueName).
+ */
+ public static function &fieldKeys() {
+ if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) {
+ Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields()));
+ }
+ return Civi::$statics[__CLASS__]['fieldKeys'];
+ }
+
+ /**
+ * Returns the names of this table
+ *
+ * @return string
+ */
+ public static function getTableName() {
+ return self::$_tableName;
+ }
+
+ /**
+ * Returns if this table needs to be logged
+ *
+ * @return bool
+ */
+ public function getLog() {
+ return self::$_log;
+ }
+
+ /**
+ * Returns the list of fields that can be imported
+ *
+ * @param bool $prefix
+ *
+ * @return array
+ */
+ public static function &import($prefix = FALSE) {
+ $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'dedupe_rule', $prefix, []);
+ return $r;
+ }
+
+ /**
+ * Returns the list of fields that can be exported
+ *
+ * @param bool $prefix
+ *
+ * @return array
+ */
+ public static function &export($prefix = FALSE) {
+ $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'dedupe_rule', $prefix, []);
+ return $r;
+ }
+
+ /**
+ * Returns the list of indices
+ *
+ * @param bool $localize
+ *
+ * @return array
+ */
+ public static function indices($localize = TRUE) {
+ $indices = [];
+ return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices;
+ }
+
+}
--- /dev/null
+<?php
+
+/**
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ *
+ * Generated from xml/schema/CRM/Dedupe/DedupeRuleGroup.xml
+ * DO NOT EDIT. Generated by CRM_Core_CodeGen
+ * (GenCodeChecksum:d2b969468d4841e3b7595d7beba7cea2)
+ */
+
+/**
+ * Database access object for the DedupeRuleGroup entity.
+ */
+class CRM_Dedupe_DAO_DedupeRuleGroup extends CRM_Core_DAO {
+ const EXT = 'civicrm';
+ const TABLE_ADDED = '1.8';
+
+ /**
+ * Static instance to hold the table name.
+ *
+ * @var string
+ */
+ public static $_tableName = 'civicrm_dedupe_rule_group';
+
+ /**
+ * Should CiviCRM log any modifications to this table in the civicrm_log table.
+ *
+ * @var bool
+ */
+ public static $_log = FALSE;
+
+ /**
+ * Unique dedupe rule group id
+ *
+ * @var int
+ */
+ public $id;
+
+ /**
+ * The type of contacts this group applies to
+ *
+ * @var string
+ */
+ public $contact_type;
+
+ /**
+ * The weight threshold the sum of the rule weights has to cross to consider two contacts the same
+ *
+ * @var int
+ */
+ public $threshold;
+
+ /**
+ * Whether the rule should be used for cases where usage is Unsupervised, Supervised OR General(programatically)
+ *
+ * @var string
+ */
+ public $used;
+
+ /**
+ * Name of the rule group
+ *
+ * @var string
+ */
+ public $name;
+
+ /**
+ * Label of the rule group
+ *
+ * @var string
+ */
+ public $title;
+
+ /**
+ * Is this a reserved rule - a rule group that has been optimized and cannot be changed by the admin
+ *
+ * @var bool
+ */
+ public $is_reserved;
+
+ /**
+ * Class constructor.
+ */
+ public function __construct() {
+ $this->__table = 'civicrm_dedupe_rule_group';
+ parent::__construct();
+ }
+
+ /**
+ * Returns localized title of this entity.
+ *
+ * @param bool $plural
+ * Whether to return the plural version of the title.
+ */
+ public static function getEntityTitle($plural = FALSE) {
+ return $plural ? ts('Dedupe Rule Groups') : ts('Dedupe Rule Group');
+ }
+
+ /**
+ * Returns all the column names of this table
+ *
+ * @return array
+ */
+ public static function &fields() {
+ if (!isset(Civi::$statics[__CLASS__]['fields'])) {
+ Civi::$statics[__CLASS__]['fields'] = [
+ 'id' => [
+ 'name' => 'id',
+ 'type' => CRM_Utils_Type::T_INT,
+ 'title' => ts('Rule Group ID'),
+ 'description' => ts('Unique dedupe rule group id'),
+ 'required' => TRUE,
+ 'where' => 'civicrm_dedupe_rule_group.id',
+ 'table_name' => 'civicrm_dedupe_rule_group',
+ 'entity' => 'DedupeRuleGroup',
+ 'bao' => 'CRM_Dedupe_BAO_DedupeRuleGroup',
+ 'localizable' => 0,
+ 'html' => [
+ 'type' => 'Number',
+ ],
+ 'readonly' => TRUE,
+ 'add' => '1.8',
+ ],
+ 'contact_type' => [
+ 'name' => 'contact_type',
+ 'type' => CRM_Utils_Type::T_STRING,
+ 'title' => ts('Contact Type'),
+ 'description' => ts('The type of contacts this group applies to'),
+ 'maxlength' => 12,
+ 'size' => CRM_Utils_Type::TWELVE,
+ 'where' => 'civicrm_dedupe_rule_group.contact_type',
+ 'table_name' => 'civicrm_dedupe_rule_group',
+ 'entity' => 'DedupeRuleGroup',
+ 'bao' => 'CRM_Dedupe_BAO_DedupeRuleGroup',
+ 'localizable' => 0,
+ 'html' => [
+ 'type' => 'Select',
+ ],
+ 'pseudoconstant' => [
+ 'table' => 'civicrm_contact_type',
+ 'keyColumn' => 'name',
+ 'labelColumn' => 'label',
+ 'condition' => 'parent_id IS NULL',
+ ],
+ 'add' => '1.8',
+ ],
+ 'threshold' => [
+ 'name' => 'threshold',
+ 'type' => CRM_Utils_Type::T_INT,
+ 'title' => ts('Threshold'),
+ 'description' => ts('The weight threshold the sum of the rule weights has to cross to consider two contacts the same'),
+ 'required' => TRUE,
+ 'where' => 'civicrm_dedupe_rule_group.threshold',
+ 'table_name' => 'civicrm_dedupe_rule_group',
+ 'entity' => 'DedupeRuleGroup',
+ 'bao' => 'CRM_Dedupe_BAO_DedupeRuleGroup',
+ 'localizable' => 0,
+ 'html' => [
+ 'type' => 'Text',
+ ],
+ 'add' => '1.8',
+ ],
+ 'used' => [
+ 'name' => 'used',
+ 'type' => CRM_Utils_Type::T_STRING,
+ 'title' => ts('Length'),
+ 'description' => ts('Whether the rule should be used for cases where usage is Unsupervised, Supervised OR General(programatically)'),
+ 'required' => TRUE,
+ 'maxlength' => 12,
+ 'size' => CRM_Utils_Type::TWELVE,
+ 'where' => 'civicrm_dedupe_rule_group.used',
+ 'table_name' => 'civicrm_dedupe_rule_group',
+ 'entity' => 'DedupeRuleGroup',
+ 'bao' => 'CRM_Dedupe_BAO_DedupeRuleGroup',
+ 'localizable' => 0,
+ 'html' => [
+ 'type' => 'Radio',
+ ],
+ 'pseudoconstant' => [
+ 'callback' => 'CRM_Core_SelectValues::getDedupeRuleTypes',
+ ],
+ 'add' => '4.3',
+ ],
+ 'name' => [
+ 'name' => 'name',
+ 'type' => CRM_Utils_Type::T_STRING,
+ 'title' => ts('Name'),
+ 'description' => ts('Name of the rule group'),
+ 'maxlength' => 64,
+ 'size' => CRM_Utils_Type::BIG,
+ 'where' => 'civicrm_dedupe_rule_group.name',
+ 'table_name' => 'civicrm_dedupe_rule_group',
+ 'entity' => 'DedupeRuleGroup',
+ 'bao' => 'CRM_Dedupe_BAO_DedupeRuleGroup',
+ 'localizable' => 0,
+ 'add' => '2.1',
+ ],
+ 'title' => [
+ 'name' => 'title',
+ 'type' => CRM_Utils_Type::T_STRING,
+ 'title' => ts('Title'),
+ 'description' => ts('Label of the rule group'),
+ 'maxlength' => 255,
+ 'size' => CRM_Utils_Type::HUGE,
+ 'where' => 'civicrm_dedupe_rule_group.title',
+ 'table_name' => 'civicrm_dedupe_rule_group',
+ 'entity' => 'DedupeRuleGroup',
+ 'bao' => 'CRM_Dedupe_BAO_DedupeRuleGroup',
+ 'localizable' => 0,
+ 'html' => [
+ 'type' => 'Text',
+ ],
+ 'add' => '4.1',
+ ],
+ 'is_reserved' => [
+ 'name' => 'is_reserved',
+ 'type' => CRM_Utils_Type::T_BOOLEAN,
+ 'title' => ts('Reserved?'),
+ 'description' => ts('Is this a reserved rule - a rule group that has been optimized and cannot be changed by the admin'),
+ 'where' => 'civicrm_dedupe_rule_group.is_reserved',
+ 'table_name' => 'civicrm_dedupe_rule_group',
+ 'entity' => 'DedupeRuleGroup',
+ 'bao' => 'CRM_Dedupe_BAO_DedupeRuleGroup',
+ 'localizable' => 0,
+ 'html' => [
+ 'type' => 'CheckBox',
+ ],
+ 'add' => '4.1',
+ ],
+ ];
+ CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']);
+ }
+ return Civi::$statics[__CLASS__]['fields'];
+ }
+
+ /**
+ * Return a mapping from field-name to the corresponding key (as used in fields()).
+ *
+ * @return array
+ * Array(string $name => string $uniqueName).
+ */
+ public static function &fieldKeys() {
+ if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) {
+ Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields()));
+ }
+ return Civi::$statics[__CLASS__]['fieldKeys'];
+ }
+
+ /**
+ * Returns the names of this table
+ *
+ * @return string
+ */
+ public static function getTableName() {
+ return self::$_tableName;
+ }
+
+ /**
+ * Returns if this table needs to be logged
+ *
+ * @return bool
+ */
+ public function getLog() {
+ return self::$_log;
+ }
+
+ /**
+ * Returns the list of fields that can be imported
+ *
+ * @param bool $prefix
+ *
+ * @return array
+ */
+ public static function &import($prefix = FALSE) {
+ $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'dedupe_rule_group', $prefix, []);
+ return $r;
+ }
+
+ /**
+ * Returns the list of fields that can be exported
+ *
+ * @param bool $prefix
+ *
+ * @return array
+ */
+ public static function &export($prefix = FALSE) {
+ $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'dedupe_rule_group', $prefix, []);
+ return $r;
+ }
+
+ /**
+ * Returns the list of indices
+ *
+ * @param bool $localize
+ *
+ * @return array
+ */
+ public static function indices($localize = TRUE) {
+ $indices = [];
+ return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices;
+ }
+
+}
/**
* Database access object for the Exception entity.
*/
-class CRM_Dedupe_DAO_Exception extends CRM_Core_DAO {
- const EXT = 'civicrm';
- const TABLE_ADDED = '3.3';
-
- /**
- * Static instance to hold the table name.
- *
- * @var string
- */
- public static $_tableName = 'civicrm_dedupe_exception';
-
- /**
- * Should CiviCRM log any modifications to this table in the civicrm_log table.
- *
- * @var bool
- */
- public static $_log = FALSE;
-
- /**
- * Unique dedupe exception id
- *
- * @var int
- */
- public $id;
-
- /**
- * FK to Contact ID
- *
- * @var int
- */
- public $contact_id1;
-
- /**
- * FK to Contact ID
- *
- * @var int
- */
- public $contact_id2;
-
- /**
- * Class constructor.
- */
- public function __construct() {
- $this->__table = 'civicrm_dedupe_exception';
- parent::__construct();
- }
-
- /**
- * Returns localized title of this entity.
- *
- * @param bool $plural
- * Whether to return the plural version of the title.
- */
- public static function getEntityTitle($plural = FALSE) {
- return $plural ? ts('Exceptions') : ts('Exception');
- }
-
- /**
- * Returns foreign keys and entity references.
- *
- * @return array
- * [CRM_Core_Reference_Interface]
- */
- public static function getReferenceColumns() {
- if (!isset(Civi::$statics[__CLASS__]['links'])) {
- Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__);
- Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id1', 'civicrm_contact', 'id');
- Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id2', 'civicrm_contact', 'id');
- CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']);
- }
- return Civi::$statics[__CLASS__]['links'];
- }
-
- /**
- * Returns all the column names of this table
- *
- * @return array
- */
- public static function &fields() {
- if (!isset(Civi::$statics[__CLASS__]['fields'])) {
- Civi::$statics[__CLASS__]['fields'] = [
- 'id' => [
- 'name' => 'id',
- 'type' => CRM_Utils_Type::T_INT,
- 'title' => ts('Dedupe Exception ID'),
- 'description' => ts('Unique dedupe exception id'),
- 'required' => TRUE,
- 'where' => 'civicrm_dedupe_exception.id',
- 'table_name' => 'civicrm_dedupe_exception',
- 'entity' => 'Exception',
- 'bao' => 'CRM_Dedupe_BAO_Exception',
- 'localizable' => 0,
- 'html' => [
- 'type' => 'Number',
- ],
- 'readonly' => TRUE,
- 'add' => '3.3',
- ],
- 'contact_id1' => [
- 'name' => 'contact_id1',
- 'type' => CRM_Utils_Type::T_INT,
- 'title' => ts('First Dupe Contact ID'),
- 'description' => ts('FK to Contact ID'),
- 'required' => TRUE,
- 'where' => 'civicrm_dedupe_exception.contact_id1',
- 'table_name' => 'civicrm_dedupe_exception',
- 'entity' => 'Exception',
- 'bao' => 'CRM_Dedupe_BAO_Exception',
- 'localizable' => 0,
- 'FKClassName' => 'CRM_Contact_DAO_Contact',
- 'html' => [
- 'label' => ts("First Dupe Contact"),
- ],
- 'add' => '3.3',
- ],
- 'contact_id2' => [
- 'name' => 'contact_id2',
- 'type' => CRM_Utils_Type::T_INT,
- 'title' => ts('Second Dupe Contact ID'),
- 'description' => ts('FK to Contact ID'),
- 'required' => TRUE,
- 'where' => 'civicrm_dedupe_exception.contact_id2',
- 'table_name' => 'civicrm_dedupe_exception',
- 'entity' => 'Exception',
- 'bao' => 'CRM_Dedupe_BAO_Exception',
- 'localizable' => 0,
- 'FKClassName' => 'CRM_Contact_DAO_Contact',
- 'html' => [
- 'label' => ts("Second Dupe Contact"),
- ],
- 'add' => '3.3',
- ],
- ];
- CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']);
- }
- return Civi::$statics[__CLASS__]['fields'];
- }
-
- /**
- * Return a mapping from field-name to the corresponding key (as used in fields()).
- *
- * @return array
- * Array(string $name => string $uniqueName).
- */
- public static function &fieldKeys() {
- if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) {
- Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields()));
- }
- return Civi::$statics[__CLASS__]['fieldKeys'];
- }
-
- /**
- * Returns the names of this table
- *
- * @return string
- */
- public static function getTableName() {
- return self::$_tableName;
- }
-
- /**
- * Returns if this table needs to be logged
- *
- * @return bool
- */
- public function getLog() {
- return self::$_log;
- }
-
- /**
- * Returns the list of fields that can be imported
- *
- * @param bool $prefix
- *
- * @return array
- */
- public static function &import($prefix = FALSE) {
- $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'dedupe_exception', $prefix, []);
- return $r;
- }
-
- /**
- * Returns the list of fields that can be exported
- *
- * @param bool $prefix
- *
- * @return array
- */
- public static function &export($prefix = FALSE) {
- $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'dedupe_exception', $prefix, []);
- return $r;
- }
-
- /**
- * Returns the list of indices
- *
- * @param bool $localize
- *
- * @return array
- */
- public static function indices($localize = TRUE) {
- $indices = [
- 'UI_contact_id1_contact_id2' => [
- 'name' => 'UI_contact_id1_contact_id2',
- 'field' => [
- 0 => 'contact_id1',
- 1 => 'contact_id2',
- ],
- 'localizable' => FALSE,
- 'unique' => TRUE,
- 'sig' => 'civicrm_dedupe_exception::1::contact_id1::contact_id2',
- ],
- ];
- return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices;
- }
-
-}
+class CRM_Dedupe_DAO_Exception extends CRM_Dedupe_DAO_DedupeException {}
* @package CRM
* @copyright CiviCRM LLC https://civicrm.org/licensing
*
- * Generated from xml/schema/CRM/Dedupe/Rule.xml
- * DO NOT EDIT. Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:2860c5a16ead4f23809027d8a2812265)
*/
/**
- * Database access object for the Rule entity.
+ * Database access object for the DedupeRule entity.
*/
-class CRM_Dedupe_DAO_Rule extends CRM_Core_DAO {
- const EXT = 'civicrm';
- const TABLE_ADDED = '1.8';
-
- /**
- * Static instance to hold the table name.
- *
- * @var string
- */
- public static $_tableName = 'civicrm_dedupe_rule';
-
- /**
- * Should CiviCRM log any modifications to this table in the civicrm_log table.
- *
- * @var bool
- */
- public static $_log = FALSE;
-
- /**
- * Unique dedupe rule id
- *
- * @var int
- */
- public $id;
-
- /**
- * The id of the rule group this rule belongs to
- *
- * @var int
- */
- public $dedupe_rule_group_id;
-
- /**
- * The name of the table this rule is about
- *
- * @var string
- */
- public $rule_table;
-
- /**
- * The name of the field of the table referenced in rule_table
- *
- * @var string
- */
- public $rule_field;
-
- /**
- * The length of the matching substring
- *
- * @var int
- */
- public $rule_length;
-
- /**
- * The weight of the rule
- *
- * @var int
- */
- public $rule_weight;
-
- /**
- * Class constructor.
- */
- public function __construct() {
- $this->__table = 'civicrm_dedupe_rule';
- parent::__construct();
- }
-
- /**
- * Returns localized title of this entity.
- *
- * @param bool $plural
- * Whether to return the plural version of the title.
- */
- public static function getEntityTitle($plural = FALSE) {
- return $plural ? ts('Rules') : ts('Rule');
- }
-
- /**
- * Returns foreign keys and entity references.
- *
- * @return array
- * [CRM_Core_Reference_Interface]
- */
- public static function getReferenceColumns() {
- if (!isset(Civi::$statics[__CLASS__]['links'])) {
- Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__);
- Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'dedupe_rule_group_id', 'civicrm_dedupe_rule_group', 'id');
- CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']);
- }
- return Civi::$statics[__CLASS__]['links'];
- }
-
- /**
- * Returns all the column names of this table
- *
- * @return array
- */
- public static function &fields() {
- if (!isset(Civi::$statics[__CLASS__]['fields'])) {
- Civi::$statics[__CLASS__]['fields'] = [
- 'id' => [
- 'name' => 'id',
- 'type' => CRM_Utils_Type::T_INT,
- 'title' => ts('Dedupe Rule ID'),
- 'description' => ts('Unique dedupe rule id'),
- 'required' => TRUE,
- 'where' => 'civicrm_dedupe_rule.id',
- 'table_name' => 'civicrm_dedupe_rule',
- 'entity' => 'Rule',
- 'bao' => 'CRM_Dedupe_BAO_Rule',
- 'localizable' => 0,
- 'html' => [
- 'type' => 'Number',
- ],
- 'readonly' => TRUE,
- 'add' => '1.8',
- ],
- 'dedupe_rule_group_id' => [
- 'name' => 'dedupe_rule_group_id',
- 'type' => CRM_Utils_Type::T_INT,
- 'title' => ts('Group ID'),
- 'description' => ts('The id of the rule group this rule belongs to'),
- 'required' => TRUE,
- 'where' => 'civicrm_dedupe_rule.dedupe_rule_group_id',
- 'table_name' => 'civicrm_dedupe_rule',
- 'entity' => 'Rule',
- 'bao' => 'CRM_Dedupe_BAO_Rule',
- 'localizable' => 0,
- 'FKClassName' => 'CRM_Dedupe_DAO_RuleGroup',
- 'html' => [
- 'label' => ts("Group"),
- ],
- 'add' => '1.8',
- ],
- 'rule_table' => [
- 'name' => 'rule_table',
- 'type' => CRM_Utils_Type::T_STRING,
- 'title' => ts('Rule Table'),
- 'description' => ts('The name of the table this rule is about'),
- 'required' => TRUE,
- 'maxlength' => 64,
- 'size' => CRM_Utils_Type::BIG,
- 'where' => 'civicrm_dedupe_rule.rule_table',
- 'table_name' => 'civicrm_dedupe_rule',
- 'entity' => 'Rule',
- 'bao' => 'CRM_Dedupe_BAO_Rule',
- 'localizable' => 0,
- 'add' => '1.8',
- ],
- 'rule_field' => [
- 'name' => 'rule_field',
- 'type' => CRM_Utils_Type::T_STRING,
- 'title' => ts('Rule Field'),
- 'description' => ts('The name of the field of the table referenced in rule_table'),
- 'required' => TRUE,
- 'maxlength' => 64,
- 'size' => CRM_Utils_Type::BIG,
- 'where' => 'civicrm_dedupe_rule.rule_field',
- 'table_name' => 'civicrm_dedupe_rule',
- 'entity' => 'Rule',
- 'bao' => 'CRM_Dedupe_BAO_Rule',
- 'localizable' => 0,
- 'add' => '1.8',
- ],
- 'rule_length' => [
- 'name' => 'rule_length',
- 'type' => CRM_Utils_Type::T_INT,
- 'title' => ts('Rule Length'),
- 'description' => ts('The length of the matching substring'),
- 'where' => 'civicrm_dedupe_rule.rule_length',
- 'table_name' => 'civicrm_dedupe_rule',
- 'entity' => 'Rule',
- 'bao' => 'CRM_Dedupe_BAO_Rule',
- 'localizable' => 0,
- 'html' => [
- 'type' => 'Text',
- ],
- 'add' => '1.8',
- ],
- 'rule_weight' => [
- 'name' => 'rule_weight',
- 'type' => CRM_Utils_Type::T_INT,
- 'title' => ts('Order'),
- 'description' => ts('The weight of the rule'),
- 'required' => TRUE,
- 'where' => 'civicrm_dedupe_rule.rule_weight',
- 'table_name' => 'civicrm_dedupe_rule',
- 'entity' => 'Rule',
- 'bao' => 'CRM_Dedupe_BAO_Rule',
- 'localizable' => 0,
- 'html' => [
- 'type' => 'Text',
- ],
- 'add' => '1.8',
- ],
- ];
- CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']);
- }
- return Civi::$statics[__CLASS__]['fields'];
- }
-
- /**
- * Return a mapping from field-name to the corresponding key (as used in fields()).
- *
- * @return array
- * Array(string $name => string $uniqueName).
- */
- public static function &fieldKeys() {
- if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) {
- Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields()));
- }
- return Civi::$statics[__CLASS__]['fieldKeys'];
- }
-
- /**
- * Returns the names of this table
- *
- * @return string
- */
- public static function getTableName() {
- return self::$_tableName;
- }
-
- /**
- * Returns if this table needs to be logged
- *
- * @return bool
- */
- public function getLog() {
- return self::$_log;
- }
-
- /**
- * Returns the list of fields that can be imported
- *
- * @param bool $prefix
- *
- * @return array
- */
- public static function &import($prefix = FALSE) {
- $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'dedupe_rule', $prefix, []);
- return $r;
- }
-
- /**
- * Returns the list of fields that can be exported
- *
- * @param bool $prefix
- *
- * @return array
- */
- public static function &export($prefix = FALSE) {
- $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'dedupe_rule', $prefix, []);
- return $r;
- }
-
- /**
- * Returns the list of indices
- *
- * @param bool $localize
- *
- * @return array
- */
- public static function indices($localize = TRUE) {
- $indices = [];
- return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices;
- }
-
-}
+class CRM_Dedupe_DAO_Rule extends CRM_Dedupe_DAO_DedupeRule {}
* @package CRM
* @copyright CiviCRM LLC https://civicrm.org/licensing
*
- * Generated from xml/schema/CRM/Dedupe/RuleGroup.xml
- * DO NOT EDIT. Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:114051c4c9bd7fd1e7854de7fc110884)
*/
/**
- * Database access object for the RuleGroup entity.
+ * Database access object for the DedupeRuleGroup entity.
*/
-class CRM_Dedupe_DAO_RuleGroup extends CRM_Core_DAO {
- const EXT = 'civicrm';
- const TABLE_ADDED = '1.8';
-
- /**
- * Static instance to hold the table name.
- *
- * @var string
- */
- public static $_tableName = 'civicrm_dedupe_rule_group';
-
- /**
- * Should CiviCRM log any modifications to this table in the civicrm_log table.
- *
- * @var bool
- */
- public static $_log = FALSE;
-
- /**
- * Unique dedupe rule group id
- *
- * @var int
- */
- public $id;
-
- /**
- * The type of contacts this group applies to
- *
- * @var string
- */
- public $contact_type;
-
- /**
- * The weight threshold the sum of the rule weights has to cross to consider two contacts the same
- *
- * @var int
- */
- public $threshold;
-
- /**
- * Whether the rule should be used for cases where usage is Unsupervised, Supervised OR General(programatically)
- *
- * @var string
- */
- public $used;
-
- /**
- * Name of the rule group
- *
- * @var string
- */
- public $name;
-
- /**
- * Label of the rule group
- *
- * @var string
- */
- public $title;
-
- /**
- * Is this a reserved rule - a rule group that has been optimized and cannot be changed by the admin
- *
- * @var bool
- */
- public $is_reserved;
-
- /**
- * Class constructor.
- */
- public function __construct() {
- $this->__table = 'civicrm_dedupe_rule_group';
- parent::__construct();
- }
-
- /**
- * Returns localized title of this entity.
- *
- * @param bool $plural
- * Whether to return the plural version of the title.
- */
- public static function getEntityTitle($plural = FALSE) {
- return $plural ? ts('Rule Groups') : ts('Rule Group');
- }
-
- /**
- * Returns all the column names of this table
- *
- * @return array
- */
- public static function &fields() {
- if (!isset(Civi::$statics[__CLASS__]['fields'])) {
- Civi::$statics[__CLASS__]['fields'] = [
- 'id' => [
- 'name' => 'id',
- 'type' => CRM_Utils_Type::T_INT,
- 'title' => ts('Rule Group ID'),
- 'description' => ts('Unique dedupe rule group id'),
- 'required' => TRUE,
- 'where' => 'civicrm_dedupe_rule_group.id',
- 'table_name' => 'civicrm_dedupe_rule_group',
- 'entity' => 'RuleGroup',
- 'bao' => 'CRM_Dedupe_BAO_RuleGroup',
- 'localizable' => 0,
- 'html' => [
- 'type' => 'Number',
- ],
- 'readonly' => TRUE,
- 'add' => '1.8',
- ],
- 'contact_type' => [
- 'name' => 'contact_type',
- 'type' => CRM_Utils_Type::T_STRING,
- 'title' => ts('Contact Type'),
- 'description' => ts('The type of contacts this group applies to'),
- 'maxlength' => 12,
- 'size' => CRM_Utils_Type::TWELVE,
- 'where' => 'civicrm_dedupe_rule_group.contact_type',
- 'table_name' => 'civicrm_dedupe_rule_group',
- 'entity' => 'RuleGroup',
- 'bao' => 'CRM_Dedupe_BAO_RuleGroup',
- 'localizable' => 0,
- 'html' => [
- 'type' => 'Select',
- ],
- 'pseudoconstant' => [
- 'table' => 'civicrm_contact_type',
- 'keyColumn' => 'name',
- 'labelColumn' => 'label',
- 'condition' => 'parent_id IS NULL',
- ],
- 'add' => '1.8',
- ],
- 'threshold' => [
- 'name' => 'threshold',
- 'type' => CRM_Utils_Type::T_INT,
- 'title' => ts('Threshold'),
- 'description' => ts('The weight threshold the sum of the rule weights has to cross to consider two contacts the same'),
- 'required' => TRUE,
- 'where' => 'civicrm_dedupe_rule_group.threshold',
- 'table_name' => 'civicrm_dedupe_rule_group',
- 'entity' => 'RuleGroup',
- 'bao' => 'CRM_Dedupe_BAO_RuleGroup',
- 'localizable' => 0,
- 'html' => [
- 'type' => 'Text',
- ],
- 'add' => '1.8',
- ],
- 'used' => [
- 'name' => 'used',
- 'type' => CRM_Utils_Type::T_STRING,
- 'title' => ts('Length'),
- 'description' => ts('Whether the rule should be used for cases where usage is Unsupervised, Supervised OR General(programatically)'),
- 'required' => TRUE,
- 'maxlength' => 12,
- 'size' => CRM_Utils_Type::TWELVE,
- 'where' => 'civicrm_dedupe_rule_group.used',
- 'table_name' => 'civicrm_dedupe_rule_group',
- 'entity' => 'RuleGroup',
- 'bao' => 'CRM_Dedupe_BAO_RuleGroup',
- 'localizable' => 0,
- 'html' => [
- 'type' => 'Radio',
- ],
- 'pseudoconstant' => [
- 'callback' => 'CRM_Core_SelectValues::getDedupeRuleTypes',
- ],
- 'add' => '4.3',
- ],
- 'name' => [
- 'name' => 'name',
- 'type' => CRM_Utils_Type::T_STRING,
- 'title' => ts('Name'),
- 'description' => ts('Name of the rule group'),
- 'maxlength' => 64,
- 'size' => CRM_Utils_Type::BIG,
- 'where' => 'civicrm_dedupe_rule_group.name',
- 'table_name' => 'civicrm_dedupe_rule_group',
- 'entity' => 'RuleGroup',
- 'bao' => 'CRM_Dedupe_BAO_RuleGroup',
- 'localizable' => 0,
- 'add' => '2.1',
- ],
- 'title' => [
- 'name' => 'title',
- 'type' => CRM_Utils_Type::T_STRING,
- 'title' => ts('Title'),
- 'description' => ts('Label of the rule group'),
- 'maxlength' => 255,
- 'size' => CRM_Utils_Type::HUGE,
- 'where' => 'civicrm_dedupe_rule_group.title',
- 'table_name' => 'civicrm_dedupe_rule_group',
- 'entity' => 'RuleGroup',
- 'bao' => 'CRM_Dedupe_BAO_RuleGroup',
- 'localizable' => 0,
- 'html' => [
- 'type' => 'Text',
- ],
- 'add' => '4.1',
- ],
- 'is_reserved' => [
- 'name' => 'is_reserved',
- 'type' => CRM_Utils_Type::T_BOOLEAN,
- 'title' => ts('Reserved?'),
- 'description' => ts('Is this a reserved rule - a rule group that has been optimized and cannot be changed by the admin'),
- 'where' => 'civicrm_dedupe_rule_group.is_reserved',
- 'table_name' => 'civicrm_dedupe_rule_group',
- 'entity' => 'RuleGroup',
- 'bao' => 'CRM_Dedupe_BAO_RuleGroup',
- 'localizable' => 0,
- 'html' => [
- 'type' => 'CheckBox',
- ],
- 'add' => '4.1',
- ],
- ];
- CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']);
- }
- return Civi::$statics[__CLASS__]['fields'];
- }
-
- /**
- * Return a mapping from field-name to the corresponding key (as used in fields()).
- *
- * @return array
- * Array(string $name => string $uniqueName).
- */
- public static function &fieldKeys() {
- if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) {
- Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields()));
- }
- return Civi::$statics[__CLASS__]['fieldKeys'];
- }
-
- /**
- * Returns the names of this table
- *
- * @return string
- */
- public static function getTableName() {
- return self::$_tableName;
- }
-
- /**
- * Returns if this table needs to be logged
- *
- * @return bool
- */
- public function getLog() {
- return self::$_log;
- }
-
- /**
- * Returns the list of fields that can be imported
- *
- * @param bool $prefix
- *
- * @return array
- */
- public static function &import($prefix = FALSE) {
- $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'dedupe_rule_group', $prefix, []);
- return $r;
- }
-
- /**
- * Returns the list of fields that can be exported
- *
- * @param bool $prefix
- *
- * @return array
- */
- public static function &export($prefix = FALSE) {
- $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'dedupe_rule_group', $prefix, []);
- return $r;
- }
-
- /**
- * Returns the list of indices
- *
- * @param bool $localize
- *
- * @return array
- */
- public static function indices($localize = TRUE) {
- $indices = [];
- return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices;
- }
-
-}
+class CRM_Dedupe_DAO_RuleGroup extends CRM_Dedupe_DAO_DedupeRuleGroup {}
*
* Generated from xml/schema/CRM/Event/Event.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:c5779073cea9c6b4a959070ec0e95b6e)
+ * (GenCodeChecksum:e0bee3952ac39a53952b6a7979a79e13)
*/
/**
'entity' => 'Event',
'bao' => 'CRM_Event_BAO_Event',
'localizable' => 0,
- 'FKClassName' => 'CRM_Dedupe_DAO_RuleGroup',
+ 'FKClassName' => 'CRM_Dedupe_DAO_DedupeRuleGroup',
'html' => [
'type' => 'Select',
'label' => ts("Dedupe Rule"),
--- /dev/null
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved. |
+ | |
+ | This work is published under the GNU AGPLv3 license with some |
+ | permitted exceptions and without any warranty. For full license |
+ | and copyright information, see https://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+
+namespace Civi\Api4;
+
+/**
+ * DedupeRule entity.
+ *
+ * This api exposes CiviCRM (dedupe) rules.
+ *
+ * @see https://docs.civicrm.org/user/en/latest/organising-your-data/contacts/
+ * @package Civi\Api4
+ */
+class DedupeRule extends Generic\DAOEntity {
+
+}
if ($name === 'SmsProvider') {
return 'CRM_SMS_DAO_Provider';
}
+ // Entity was renamed to CRM_Dedupe_DAO_DedupeRule for APIv4
+ if ($name === 'Rule') {
+ return 'CRM_Dedupe_DAO_DedupeRule';
+ }
+ // Entity was renamed to CRM_Dedupe_DAO_DedupeRuleGroup for APIv4
+ if ($name === 'RuleGroup') {
+ return 'CRM_Dedupe_DAO_DedupeRuleGroup';
+ }
+ // Entity was renamed to CRM_Dedupe_DAO_DedupeException for APIv4
+ if ($name === 'Exception') {
+ return 'CRM_Dedupe_DAO_DedupeException';
+ }
// FIXME: DAO names should follow CamelCase convention
if ($name === 'Im' || $name === 'Acl' || $name === 'Pcp') {
$name = strtoupper($name);
<?xml version="1.0" encoding="utf-8" ?>
<table>
<base>CRM/Dedupe</base>
- <class>Exception</class>
+ <class>DedupeException</class>
<name>civicrm_dedupe_exception</name>
<comment>Dedupe exceptions</comment>
<add>3.3</add>
<?xml version="1.0" encoding="utf-8" ?>
<table>
<base>CRM/Dedupe</base>
- <class>Rule</class>
+ <class>DedupeRule</class>
<name>civicrm_dedupe_rule</name>
<comment>Dedupe rules for use by rule groups</comment>
<add>1.8</add>
<?xml version="1.0" encoding="utf-8" ?>
<table>
<base>CRM/Dedupe</base>
- <class>RuleGroup</class>
+ <class>DedupeRuleGroup</class>
<name>civicrm_dedupe_rule_group</name>
<comment>Dedupe rule groups</comment>
<add>1.8</add>
<tables xmlns:xi="http://www.w3.org/2001/XInclude">
-<xi:include href="RuleGroup.xml" parse="xml" />
-<xi:include href="Rule.xml" parse="xml" />
-<xi:include href="Exception.xml" parse="xml" />
+<xi:include href="DedupeRuleGroup.xml" parse="xml" />
+<xi:include href="DedupeRule.xml" parse="xml" />
+<xi:include href="DedupeException.xml" parse="xml" />
</tables>