Skip to content

Commit

Permalink
Merge pull request #19945 from eileenmcnaughton/group_filter
Browse files Browse the repository at this point in the history
[REF] Extract portion of GroupContact load that builds the temporary table
  • Loading branch information
colemanw authored Mar 30, 2021
2 parents 5b92de4 + 43a9636 commit a89b74c
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 87 deletions.
197 changes: 111 additions & 86 deletions CRM/Contact/BAO/GroupContactCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
+--------------------------------------------------------------------+
*/

use Civi\Api4\Group;
use Civi\Api4\Query\SqlExpression;

/**
Expand Down Expand Up @@ -418,11 +419,12 @@ public static function removeContact($cid, $groupId = NULL) {
* @param bool $force
* Should we force a search through.
*
* @throws \API_Exception
* @throws \CRM_Core_Exception
* @throws \CiviCRM_API3_Exception
*/
public static function load(&$group, $force = FALSE) {
$groupID = $group->id;
$savedSearchID = $group->saved_search_id;
public static function load($group, $force = FALSE) {
$groupID = (int) $group->id;
if (array_key_exists($groupID, self::$_alreadyLoaded) && !$force) {
return;
}
Expand All @@ -435,92 +437,15 @@ public static function load(&$group, $force = FALSE) {
return;
}

$customClass = NULL;
if ($savedSearchID) {
$ssParams = CRM_Contact_BAO_SavedSearch::getSearchParams($savedSearchID);
$groupID = CRM_Utils_Type::escape($groupID, 'Integer');

$excludeClause = "NOT IN (
SELECT contact_id FROM civicrm_group_contact
WHERE civicrm_group_contact.status = 'Removed'
AND civicrm_group_contact.group_id = $groupID )";
$addSelect = "$groupID AS group_id";

if (!empty($ssParams['api_entity'])) {
$sql = self::getApiSQL($ssParams, $addSelect, $excludeClause);
}
else {
// CRM-7021 rectify params to what proximity search expects if there is a value for prox_distance
if (!empty($ssParams)) {
CRM_Contact_BAO_ProximityQuery::fixInputParams($ssParams);
}
if (isset($ssParams['customSearchID'])) {
$sql = self::getCustomSearchSQL($savedSearchID, $ssParams, $addSelect, $excludeClause);
}
else {
$sql = self::getQueryObjectSQL($savedSearchID, $ssParams, $addSelect, $excludeClause);
}
}
}

$groupContactsTempTable = CRM_Utils_SQL_TempTable::build()->setCategory('gccache')->setMemory();
$groupContactsTempTable = CRM_Utils_SQL_TempTable::build()
->setCategory('gccache')
->setMemory();
self::buildGroupContactTempTable([$groupID], $groupContactsTempTable);
$tempTable = $groupContactsTempTable->getName();
$groupContactsTempTable->createWithColumns('contact_id int, group_id int, UNIQUE UI_contact_group (contact_id,group_id)');

if (!empty($sql)) {
$contactQueries[] = $sql;
}
// lets also store the records that are explicitly added to the group
// this allows us to skip the group contact LEFT JOIN
$contactQueries[] =
"SELECT $groupID as group_id, contact_id as contact_id
FROM civicrm_group_contact
WHERE civicrm_group_contact.status = 'Added' AND civicrm_group_contact.group_id = $groupID ";

self::clearGroupContactCache($groupID);

foreach ($contactQueries as $contactQuery) {
CRM_Core_DAO::executeQuery("INSERT IGNORE INTO $tempTable (group_id, contact_id) {$contactQuery}");
}

CRM_Core_DAO::reenableFullGroupByMode();

if ($group->children) {

// Store a list of contacts who are removed from the parent group
$sqlContactsRemovedFromGroup = "
SELECT contact_id
FROM civicrm_group_contact
WHERE civicrm_group_contact.status = 'Removed'
AND civicrm_group_contact.group_id = $groupID ";
$dao = CRM_Core_DAO::executeQuery($sqlContactsRemovedFromGroup);
$removed_contacts = [];
while ($dao->fetch()) {
$removed_contacts[] = $dao->contact_id;
}

$childrenIDs = explode(',', $group->children);
foreach ($childrenIDs as $childID) {
$contactIDs = CRM_Contact_BAO_Group::getMember($childID, FALSE);
// Unset each contact that is removed from the parent group
foreach ($removed_contacts as $removed_contact) {
unset($contactIDs[$removed_contact]);
}
if (empty($contactIDs)) {
// This child group has no contact IDs so we don't need to add them to
continue;
}
$values = [];
foreach ($contactIDs as $contactID => $dontCare) {
$values[] = "({$groupID},{$contactID})";
}
$str = implode(',', $values);
CRM_Core_DAO::executeQuery("INSERT IGNORE INTO $tempTable (group_id, contact_id) VALUES $str");
}
}

// grab a lock so other processes don't compete and do the same query
$lock = Civi::lockManager()->acquire("data.core.group.{$groupID}");

if (!$lock->isAcquired()) {
// this can cause inconsistent results since we don't know if the other process
// will fill up the cache before our calling routine needs it.
Expand Down Expand Up @@ -695,7 +620,7 @@ protected static function getApiSQL(array $savedSearch, string $addSelect, strin
* @param string $excludeClause
*
* @return string
* @throws \Exception
* @throws CRM_Core_Exception
*/
protected static function getCustomSearchSQL($savedSearchID, array $ssParams, string $addSelect, string $excludeClause) {
$searchSQL = CRM_Contact_BAO_SearchCustom::customClass($ssParams['customSearchID'], $savedSearchID)->contactIDs();
Expand Down Expand Up @@ -758,4 +683,104 @@ protected static function getQueryObjectSQL($savedSearchID, array $ssParams, str
return "$select {$sqlParts['from']} {$sqlParts['where']} {$sqlParts['group_by']} {$sqlParts['having']}";
}

/**
* Build a temporary table for the contacts in the specified group.
*
* @param array $groupIDs
* Currently only one id is build but this has been written
* to make it easy to switch to multiple.
* @param CRM_Utils_SQL_TempTable $tempTableObject
*
* @throws \API_Exception
* @throws \CRM_Core_Exception
* @throws \CiviCRM_API3_Exception
*/
protected static function buildGroupContactTempTable(array $groupIDs, $tempTableObject): void {
$group = Group::get(FALSE)->addWhere('id', 'IN', $groupIDs)
->setSelect(['saved_search_id', 'children'])->execute()->first();
$groupID = (int) $group['id'];

$customClass = NULL;
if ($group['saved_search_id']) {
$ssParams = CRM_Contact_BAO_SavedSearch::getSearchParams($group['saved_search_id']);

$excludeClause = "NOT IN (
SELECT contact_id FROM civicrm_group_contact
WHERE civicrm_group_contact.status = 'Removed'
AND civicrm_group_contact.group_id = $groupID )";
$addSelect = "$groupID AS group_id";

if (!empty($ssParams['api_entity'])) {
$sql = self::getApiSQL($ssParams, $addSelect, $excludeClause);
}
else {
// CRM-7021 rectify params to what proximity search expects if there is a value for prox_distance
if (!empty($ssParams)) {
CRM_Contact_BAO_ProximityQuery::fixInputParams($ssParams);
}
if (isset($ssParams['customSearchID'])) {
$sql = self::getCustomSearchSQL($group['saved_search_id'], $ssParams, $addSelect, $excludeClause);
}
else {
$sql = self::getQueryObjectSQL($group['saved_search_id'], $ssParams, $addSelect, $excludeClause);
}
}
}

$tempTableName = $tempTableObject->getName();
$tempTableObject->createWithColumns('contact_id int, group_id int, UNIQUE UI_contact_group (contact_id,group_id)');

if (!empty($sql)) {
$contactQueries[] = $sql;
}
// lets also store the records that are explicitly added to the group
// this allows us to skip the group contact LEFT JOIN
$contactQueries[] =
"SELECT $groupID as group_id, contact_id as contact_id
FROM civicrm_group_contact
WHERE civicrm_group_contact.status = 'Added' AND civicrm_group_contact.group_id = $groupID ";

self::clearGroupContactCache($groupID);

foreach ($contactQueries as $contactQuery) {
CRM_Core_DAO::executeQuery("INSERT IGNORE INTO $tempTableName (group_id, contact_id) {$contactQuery}");
}

CRM_Core_DAO::reenableFullGroupByMode();

if ($group['children']) {

// Store a list of contacts who are removed from the parent group
$sqlContactsRemovedFromGroup = "
SELECT contact_id
FROM civicrm_group_contact
WHERE civicrm_group_contact.status = 'Removed'
AND civicrm_group_contact.group_id = $groupID ";
$dao = CRM_Core_DAO::executeQuery($sqlContactsRemovedFromGroup);
$removed_contacts = [];
while ($dao->fetch()) {
$removed_contacts[] = $dao->contact_id;
}

$childrenIDs = explode(',', $group['children']);
foreach ($childrenIDs as $childID) {
$contactIDs = CRM_Contact_BAO_Group::getMember($childID, FALSE);
// Unset each contact that is removed from the parent group
foreach ($removed_contacts as $removed_contact) {
unset($contactIDs[$removed_contact]);
}
if (empty($contactIDs)) {
// This child group has no contact IDs so we don't need to add them to
continue;
}
$values = [];
foreach ($contactIDs as $contactID => $dontCare) {
$values[] = "({$groupID},{$contactID})";
}
$str = implode(',', $values);
CRM_Core_DAO::executeQuery("INSERT IGNORE INTO $tempTableName (group_id, contact_id) VALUES $str");
}
}
}

}
2 changes: 1 addition & 1 deletion CRM/Contact/BAO/SearchCustom.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public static function details($csID, $ssID = NULL, $gID = NULL) {
* @param int $ssID
*
* @return CRM_Contact_Form_Search_Custom_Base
* @throws Exception
* @throws CRM_Core_Exception
*/
public static function customClass($csID, $ssID) {
list($customSearchID, $customSearchClass, $formValues) = self::details($csID, $ssID);
Expand Down

0 comments on commit a89b74c

Please sign in to comment.