Skip to content

Commit f69d4ee

Browse files
CRM-17123 further removal of damaging OR query.
Note that in testing this I checked 1) searching in search builder with groups as a criteria (checked that correct groups show & only 'Added' ones) 2) Exporting - groups show per above when selected as an export field 3) Groups tab on a contact correctly shows added & removed groups 4) Api calls per tests 5) Manage groups - clicking through shows correct status for all members and it is possible to alter the group criteria to include 'Removed' and they show with the correct status
1 parent 485a3a1 commit f69d4ee

File tree

4 files changed

+63
-25
lines changed

4 files changed

+63
-25
lines changed

CRM/Contact/BAO/GroupContact.php

+1-5
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ public static function getGroupList($contactId = 0, $visibility = FALSE) {
331331
* @return array|int $values
332332
* the relevant data object values for the contact or the total count when $count is TRUE
333333
*/
334-
public static function &getContactGroup(
334+
public static function getContactGroup(
335335
$contactId,
336336
$status = NULL,
337337
$numGroupContact = NULL,
@@ -386,10 +386,6 @@ public static function &getContactGroup(
386386

387387
$from = CRM_Contact_BAO_Query::fromClause($tables);
388388

389-
//CRM-16945: seems hackish but as per CRM-16483 of using group criteria for Search Builder it is mandatory
390-
//to include group_contact_cache clause when group table is present, so following code remove duplicacy
391-
$from = str_replace("OR civicrm_group.id = civicrm_group_contact_cache.group_id", 'AND civicrm_group.saved_search_id IS NULL', $from);
392-
393389
$where .= " AND $permission ";
394390

395391
if ($onlyPublicGroups) {

CRM/Contact/BAO/Query.php

+45-14
Original file line numberDiff line numberDiff line change
@@ -872,9 +872,20 @@ public function selectClause($apiEntity = NULL) {
872872
}
873873
elseif ($name === 'groups') {
874874
$this->_useGroupBy = TRUE;
875-
$this->_select[$name] = "GROUP_CONCAT(DISTINCT(civicrm_group.title)) as groups";
875+
// Duplicates will be created here but better to sort them out in php land.
876+
$this->_select[$name] = "
877+
CONCAT_WS(',',
878+
GROUP_CONCAT(DISTINCT IF(civicrm_group_contact.status = 'Added', civicrm_group_contact.group_id, '')),
879+
GROUP_CONCAT(DISTINCT civicrm_group_contact_cache.group_id)
880+
)
881+
as groups";
876882
$this->_element[$name] = 1;
877-
$this->_tables['civicrm_group'] = 1;
883+
$this->_tables['civicrm_group_contact'] = 1;
884+
$this->_tables['civicrm_group_contact_cache'] = 1;
885+
$this->_pseudoConstantsSelect["{$name}"] = array(
886+
'pseudoField' => "groups",
887+
'idCol' => "groups",
888+
);
878889
}
879890
elseif ($name === 'notes') {
880891
// if note field is subject then return subject else body of the note
@@ -1370,6 +1381,7 @@ public function query($count = FALSE, $sortByChar = FALSE, $groupContacts = FALS
13701381
$this->_paramLookup['group'][0][1] = key($value);
13711382
}
13721383

1384+
// Presumably the lines below come into manage groups screen.
13731385
// make sure there is only one element
13741386
// this is used when we are running under smog and need to know
13751387
// how the contact was added (CRM-1203)
@@ -2517,16 +2529,6 @@ public static function fromClause(&$tables, $inner = NULL, $right = NULL, $prima
25172529
);
25182530
}
25192531

2520-
// add group_contact and group_contact_cache table if group table is present
2521-
if (!empty($tables['civicrm_group'])) {
2522-
if (empty($tables['civicrm_group_contact'])) {
2523-
$tables['civicrm_group_contact'] = " LEFT JOIN civicrm_group_contact ON civicrm_group_contact.contact_id = contact_a.id AND civicrm_group_contact.status = 'Added' ";
2524-
}
2525-
if (empty($tables['civicrm_group_contact_cache'])) {
2526-
$tables['civicrm_group_contact_cache'] = " LEFT JOIN civicrm_group_contact_cache ON civicrm_group_contact_cache.contact_id = contact_a.id ";
2527-
}
2528-
}
2529-
25302532
// add group_contact and group table is subscription history is present
25312533
if (!empty($tables['civicrm_subscription_history']) && empty($tables['civicrm_group'])) {
25322534
$tables = array_merge(array(
@@ -2641,7 +2643,7 @@ public static function fromClause(&$tables, $inner = NULL, $right = NULL, $prima
26412643
continue;
26422644

26432645
case 'civicrm_group':
2644-
$from .= " $side JOIN civicrm_group ON (civicrm_group.id = civicrm_group_contact.group_id OR civicrm_group.id = civicrm_group_contact_cache.group_id) ";
2646+
$from .= " $side JOIN civicrm_group ON civicrm_group.id = civicrm_group_contact.group_id ";
26452647
continue;
26462648

26472649
case 'civicrm_group_contact':
@@ -4259,8 +4261,9 @@ public static function getPrimaryCondition($value) {
42594261
public static function getQuery($params = NULL, $returnProperties = NULL, $count = FALSE) {
42604262
$query = new CRM_Contact_BAO_Query($params, $returnProperties);
42614263
list($select, $from, $where, $having) = $query->query();
4264+
$groupBy = ($query->_useGroupBy) ? 'GROUP BY contact_a.id' : '';
42624265

4263-
return "$select $from $where $having";
4266+
return "$select $from $where $groupBy $having";
42644267
}
42654268

42664269
/**
@@ -5687,6 +5690,10 @@ public function convertToPseudoNames(&$dao, $return = FALSE, $usedForAPI = FALSE
56875690

56885691
if (is_object($dao) && property_exists($dao, $value['idCol'])) {
56895692
$val = $dao->$value['idCol'];
5693+
if ($key == 'groups') {
5694+
$dao->groups = $this->convertGroupIDStringToLabelString($dao, $val);
5695+
return;
5696+
}
56905697

56915698
if (CRM_Utils_System::isNull($val)) {
56925699
$dao->$key = NULL;
@@ -6069,4 +6076,28 @@ protected function prepareOrderBy($sort, $sortByChar, $sortOrder, $additionalFro
60696076
return array($order, $additionalFromClause);
60706077
}
60716078

6079+
/**
6080+
* Convert a string of group IDs to a string of group labels.
6081+
*
6082+
* The original string may include duplicates and groups the user does not have
6083+
* permission to see.
6084+
*
6085+
* @param CRM_Core_DAO $dao
6086+
* @param string $val
6087+
*
6088+
* @return string
6089+
*/
6090+
public function convertGroupIDStringToLabelString(&$dao, $val) {
6091+
$groupIDs = explode(',', $val);
6092+
6093+
// The pseudoConstant function does not actually cache.
6094+
static $allGroups;
6095+
if (!$allGroups) {
6096+
$allGroups = CRM_Core_PseudoConstant::group();
6097+
}
6098+
// Note that groups that the user does not have permission to will be excluded (good).
6099+
$groups = array_intersect_key($allGroups, array_flip($groupIDs));
6100+
return implode(', ', $groups);
6101+
}
6102+
60726103
}

CRM/Contact/Selector.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1189,7 +1189,7 @@ private static function &_getColumnHeaders() {
11891189
/**
11901190
* @return CRM_Contact_BAO_Query
11911191
*/
1192-
public function &getQuery() {
1192+
public function getQuery() {
11931193
return $this->_query;
11941194
}
11951195

tests/phpunit/CRM/Contact/BAO/QueryTest.php

+16-5
Original file line numberDiff line numberDiff line change
@@ -292,16 +292,27 @@ public function testGroupClause() {
292292

293293
// Refresh the cache for test purposes. It would be better to alter to alter the GroupContact add function to add contacts to the cache.
294294
CRM_Contact_BAO_GroupContactCache::remove($groupID, FALSE);
295-
$query = new CRM_Contact_BAO_Query(
295+
296+
$sql = CRM_Contact_BAO_Query::getQuery(
296297
array(array('group', 'IN', array($groupID), 0, 0)),
297298
array('contact_id')
298299
);
299300

300-
$sql = $query->query();
301-
$queryString = implode(' ', $sql);
302-
$dao = CRM_Core_DAO::executeQuery($queryString);
301+
$dao = CRM_Core_DAO::executeQuery($sql);
303302
$this->assertEquals(3, $dao->N);
304-
$this->assertFalse(strstr(implode(' ', $sql), ' OR '));
303+
$this->assertFalse(strstr($sql, ' OR '));
304+
305+
$sql = CRM_Contact_BAO_Query::getQuery(
306+
array(array('group', 'IN', array($groupID), 0, 0)),
307+
array('contact_id' => 1, 'group' => 1)
308+
);
309+
310+
$dao = CRM_Core_DAO::executeQuery($sql);
311+
$this->assertEquals(3, $dao->N);
312+
$this->assertFalse(strstr($sql, ' OR '), 'Query does not include or');
313+
while ($dao->fetch()) {
314+
$this->assertTrue(($dao->groups == $groupID || $dao->groups == $groupID . ',' . $groupID), $dao->groups . ' includes ' . $groupID);
315+
}
305316
}
306317

307318
}

0 commit comments

Comments
 (0)