Skip to content

Commit

Permalink
Merge pull request #12344 from jitendrapurohit/core-192
Browse files Browse the repository at this point in the history
dev/core#192 - Search builder fails for != smart group filter
  • Loading branch information
eileenmcnaughton authored Dec 17, 2018
2 parents 946dd2f + f30aa75 commit 4ac1772
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 12 deletions.
47 changes: 35 additions & 12 deletions CRM/Contact/BAO/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -1427,7 +1427,7 @@ public function query($count = FALSE, $sortByChar = FALSE, $groupContacts = FALS
$group->find(TRUE);

if (!isset($group->saved_search_id)) {
$tbName = "`civicrm_group_contact-{$groupId}`";
$tbName = "civicrm_group_contact";
// CRM-17254 don't retrieve extra fields if contact_id is specifically requested
// as this will add load to an intentionally light query.
// ideally this code would be removed as it appears to be to support CRM-1203
Expand Down Expand Up @@ -3012,7 +3012,7 @@ public function group($values) {
$op = strpos($op, 'IN') ? $op : ($op == '!=') ? 'NOT IN' : 'IN';
}
$groupIds = implode(',', (array) $regularGroupIDs);
$gcTable = "`civicrm_group_contact-{$groupIds}`";
$gcTable = "`civicrm_group_contact-" . uniqid() . "`";
$joinClause = array("contact_a.id = {$gcTable}.contact_id");

if (strpos($op, 'IN') !== FALSE) {
Expand All @@ -3033,12 +3033,31 @@ public function group($values) {
}

//CRM-19589: contact(s) removed from a Smart Group, resides in civicrm_group_contact table
if (count($smartGroupIDs)) {
$groupClause[] = " ( " . $this->addGroupContactCache($smartGroupIDs, NULL, "contact_a", $op) . " ) ";
$groupContactCacheClause = '';
if (count($smartGroupIDs) || empty($value)) {
$gccTableAlias = "civicrm_group_contact_cache";
$groupContactCacheClause = $this->addGroupContactCache($smartGroupIDs, $gccTableAlias, "contact_a", $op);
if (!empty($groupContactCacheClause)) {
if ($isNotOp) {
$groupIds = implode(',', (array) $smartGroupIDs);
$gcTable = "civicrm_group_contact";
$joinClause = array("contact_a.id = {$gcTable}.contact_id");
$this->_tables[$gcTable] = $this->_whereTables[$gcTable] = " LEFT JOIN civicrm_group_contact {$gcTable} ON (" . implode(' AND ', $joinClause) . ")";
if (strpos($op, 'IN') !== FALSE) {
$groupClause[] = "{$gcTable}.group_id $op ( $groupIds ) AND {$gccTableAlias}.group_id IS NULL";
}
else {
$groupClause[] = "{$gcTable}.group_id $op $groupIds AND {$gccTableAlias}.group_id IS NULL";
}
}
$groupClause[] = " ( {$groupContactCacheClause} ) ";
}
}

$and = ($op == 'IS NULL') ? ' AND ' : ' OR ';
$this->_where[$grouping][] = ' ( ' . implode($and, $groupClause) . ' ) ';
if (!empty($groupClause)) {
$this->_where[$grouping][] = ' ( ' . implode($and, $groupClause) . ' ) ';
}

list($qillop, $qillVal) = CRM_Contact_BAO_Query::buildQillForFieldValue('CRM_Contact_DAO_Group', 'id', $value, $op);
$this->_qill[$grouping][] = ts("Group(s) %1 %2", array(1 => $qillop, 2 => $qillVal));
Expand Down Expand Up @@ -3074,9 +3093,14 @@ public function getGroupsFromTypeCriteria($value) {
*
* @return string WHERE clause component for smart group criteria.
*/
public function addGroupContactCache($groups, $tableAlias = NULL, $joinTable = "contact_a", $op, $joinColumn = 'id') {
public function addGroupContactCache($groups, $tableAlias, $joinTable = "contact_a", $op, $joinColumn = 'id') {
$isNullOp = (strpos($op, 'NULL') !== FALSE);
$groupsIds = $groups;

$operator = ['=' => 'IN', '!=' => 'NOT IN'];
if (!empty($operator[$op]) && is_array($groups)) {
$op = $operator[$op];
}
if (!$isNullOp && !$groups) {
return NULL;
}
Expand Down Expand Up @@ -3106,16 +3130,15 @@ public function addGroupContactCache($groups, $tableAlias = NULL, $joinTable = "
CRM_Contact_BAO_GroupContactCache::load($group);
}
}
if ($group->N == 0) {
if ($group->N == 0 && $op != 'NOT IN') {
return NULL;
}

if (!$tableAlias) {
$tableAlias = "`civicrm_group_contact_cache_";
$tableAlias .= ($isNullOp) ? "a`" : implode(',', (array) $groupsIds) . "`";
}

$this->_tables[$tableAlias] = $this->_whereTables[$tableAlias] = " LEFT JOIN civicrm_group_contact_cache {$tableAlias} ON {$joinTable}.{$joinColumn} = {$tableAlias}.contact_id ";

if ($op == 'NOT IN') {
return "{$tableAlias}.contact_id NOT IN (SELECT contact_id FROM civicrm_group_contact_cache cgcc WHERE cgcc.group_id IN ( " . implode(',', (array) $groupsIds) . " ) )";
}
return self::buildClause("{$tableAlias}.group_id", $op, $groups, 'Int');
}

Expand Down
90 changes: 90 additions & 0 deletions tests/phpunit/CRM/Contact/BAO/GroupContactCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -409,4 +409,94 @@ protected function assertCacheRefreshed($group) {
$this->assertTrue(empty($afterGroup['refresh_date']), 'refresh date should not be set as the cache is not built');
}

/**
* Test Smart group search
*/
public function testSmartGroupSearchBuilder() {
$returnProperties = array(
'contact_type' => 1,
'contact_sub_type' => 1,
'sort_name' => 1,
'group' => 1,
);
list($group, $living, $deceased) = $this->setupSmartGroup();

$params = array(
'name' => 'Living Contacts',
'title' => 'Living Contacts',
'is_active' => 1,
'formValues' => array('is_deceased' => 0),
);
$group2 = CRM_Contact_BAO_Group::createSmartGroup($params);

//Filter on smart group with =, !=, IN and NOT IN operator.
$params = array(array('group', '=', $group2->id, 1, 0));
$query = new CRM_Contact_BAO_Query(
$params, $returnProperties,
NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTACTS,
FALSE,
FALSE, FALSE
);
$ids = $query->searchQuery(0, 0, NULL,
FALSE, FALSE, FALSE,
TRUE, FALSE
);
$expectedWhere = "civicrm_group_contact_cache.group_id IN (\"{$group2->id}\")";
$this->assertContains($expectedWhere, $query->_whereClause);
$this->_assertContactIds($query, "group_id = {$group2->id}");

$params = array(array('group', '!=', $group->id, 1, 0));
$query = new CRM_Contact_BAO_Query(
$params, $returnProperties,
NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTACTS,
FALSE,
FALSE, FALSE
);
//Assert if proper where clause is present.
$expectedWhere = "civicrm_group_contact.group_id != {$group->id} AND civicrm_group_contact_cache.group_id IS NULL OR ( civicrm_group_contact_cache.contact_id NOT IN (SELECT contact_id FROM civicrm_group_contact_cache cgcc WHERE cgcc.group_id IN ( {$group->id} ) ) )";
$this->assertContains($expectedWhere, $query->_whereClause);
$this->_assertContactIds($query, "group_id != {$group->id}");

$params = array(array('group', 'IN', array($group->id, $group2->id), 1, 0));
$query = new CRM_Contact_BAO_Query(
$params, $returnProperties,
NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTACTS,
FALSE,
FALSE, FALSE
);
$expectedWhere = "civicrm_group_contact_cache.group_id IN (\"{$group->id}\", \"{$group2->id}\")";
$this->assertContains($expectedWhere, $query->_whereClause);
$this->_assertContactIds($query, "group_id IN ({$group->id}, {$group2->id})");

$params = array(array('group', 'NOT IN', array($group->id), 1, 0));
$query = new CRM_Contact_BAO_Query(
$params, $returnProperties,
NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTACTS,
FALSE,
FALSE, FALSE
);
$expectedWhere = "civicrm_group_contact.group_id NOT IN ( {$group->id} ) AND civicrm_group_contact_cache.group_id IS NULL OR ( civicrm_group_contact_cache.contact_id NOT IN (SELECT contact_id FROM civicrm_group_contact_cache cgcc WHERE cgcc.group_id IN ( {$group->id} ) ) )";
$this->assertContains($expectedWhere, $query->_whereClause);
$this->_assertContactIds($query, "group_id NOT IN ({$group->id})");
}

/**
* Check if contact ids are fetched correctly.
*
* @param object $query
* @param string $groupWhereClause
*/
public function _assertContactIds($query, $groupWhereClause) {
$contactIds = explode(',', $query->searchQuery(0, 0, NULL,
FALSE, FALSE, FALSE,
TRUE, FALSE
));
$expectedContactIds = array();
$groupDAO = CRM_Core_DAO::executeQuery("SELECT contact_id FROM civicrm_group_contact_cache WHERE {$groupWhereClause}");
while ($groupDAO->fetch()) {
$expectedContactIds[] = $groupDAO->contact_id;
}
$this->assertEquals(sort($expectedContactIds), sort($contactIds));
}

}

0 comments on commit 4ac1772

Please sign in to comment.