Skip to content

Commit

Permalink
replace hardcoded SQL queries with querybuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
monishdeb committed Oct 28, 2017
1 parent ae3183f commit 641a960
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 94 deletions.
184 changes: 91 additions & 93 deletions CRM/Mailing/BAO/Mailing.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,21 +122,20 @@ public static function getMailRecipients($mailingObj) {

$mailingGroup = new CRM_Mailing_DAO_MailingGroup();
$recipientsGroup = $excludeSmartGroupIDs = $includeSmartGroupIDs = array();
$sql = " SELECT GROUP_CONCAT(entity_id SEPARATOR \",\") as group_ids, group_type
FROM civicrm_mailing_group
WHERE mailing_id = %1 AND entity_table = 'civicrm_group'
GROUP BY group_type
";
$dao = CRM_Core_DAO::executeQuery($sql, array(1 => array($mailingID, 'Int')));
$dao = CRM_Utils_SQL_Select::from('civicrm_mailing_group')
->select('GROUP_CONCAT(entity_id SEPARATOR ",") as group_ids, group_type')
->where('mailing_id = #mailing_id AND entity_table = "civicrm_group"')
->groupBy('group_type')
->param('#mailing_id', $mailingID)
->execute();
while ($dao->fetch()) {
$recipientsGroup[$dao->group_type] = explode(',', $dao->group_ids);
}

// there is no need to proceed further if no mailing group is selected to include recipients,
// but before return clear the mailing recipients populated earlier since as per current params no group is selected
if (empty($recipientsGroup['Include'])) {
$sql = " DELETE FROM civicrm_mailing_recipients WHERE mailing_id = %1 ";
CRM_Core_DAO::executeQuery($sql, array(1 => array($mailingID, 'Integer')));
CRM_Core_DAO::executeQuery(" DELETE FROM civicrm_mailing_recipients WHERE mailing_id = %1 ", array(1 => array($mailingID, 'Integer')));
return;
}

Expand All @@ -145,13 +144,11 @@ public static function getMailRecipients($mailingObj) {
// get all the saved searches AND hierarchical groups
// and load them in the cache
foreach ($recipientsGroup as $groupType => $groupIDs) {
$sql = sprintf("
SELECT *
FROM civicrm_group g
WHERE id IN (%s) AND ( saved_search_id != 0 OR saved_search_id IS NOT NULL OR children IS NOT NULL )
", implode(',', $groupIDs));

$groupDAO = CRM_Core_DAO::executeQuery($sql);
$groupDAO = CRM_Utils_SQL_Select::from('civicrm_group')
->where('id IN (#groupIDs)')
->where('saved_search_id != 0 OR saved_search_id IS NOT NULL OR children IS NOT NULL')
->param('#groupIDs', $groupIDs)
->execute();
while ($groupDAO->fetch()) {
if ($groupDAO->cache_date == NULL) {
CRM_Contact_BAO_GroupContactCache::load($groupDAO);
Expand All @@ -174,31 +171,30 @@ public static function getMailRecipients($mailingObj) {
ENGINE=HEAP"
);
if (!empty($recipientsGroup['Exclude'])) {
$sql = sprintf("
INSERT INTO %s (contact_id)
SELECT DISTINCT gc.contact_id
FROM civicrm_group_contact gc
WHERE gc.status = 'Added' AND gc.id IN (%s)", $excludeTempTablename, implode(',', $recipientsGroup['Exclude']));
$mailingGroup->query($sql);
CRM_Utils_SQL_Select::from('civicrm_group_contact')
->select('DISTINCT contact_id')
->where('status = "Added" AND id IN (#groups)')
->param('#groups', $recipientsGroup['Exclude'])
->insertInto($excludeTempTablename, array('contact_id'))
->execute();

if (count($excludeSmartGroupIDs)) {
$sql = sprintf("
INSERT IGNORE INTO %s (contact_id)
SELECT c.contact_id
FROM civicrm_group_contact_cache c
WHERE c.group_id IN (%s)
", $excludeTempTablename, implode(',', $excludeSmartGroupIDs));
$mailingGroup->query($sql);
CRM_Utils_SQL_Select::from('civicrm_group_contact_cache')
->select('contact_id')
->where('group_id IN (#groups)')
->param('#groups', $excludeSmartGroupIDs)
->insertIgnoreInto($excludeTempTablename, array('contact_id'))
->execute();
}
}

if (!empty($recipientsGroup['Base'])) {
$sql = sprintf("
INSERT IGNORE INTO %s (contact_id)
SELECT DISTINCT contact_id
FROM civicrm_group_contact gc
WHERE status = 'Removed' AND group_id IN (%s)", $excludeTempTablename, implode(',', $recipientsGroup['Base']));
$mailingGroup->query($sql);
CRM_Utils_SQL_Select::from('civicrm_group_contact')
->select('DISTINCT contact_id')
->where('status = "Removed" AND group_id IN (#groups)')
->param('#groups', $recipientsGroup['Base'])
->insertIgnoreInto($excludeTempTablename, array('contact_id'))
->execute();
}

$email = CRM_Core_DAO_Email::getTableName();
Expand All @@ -221,35 +217,39 @@ public static function getMailRecipients($mailingObj) {
);
// Get the group contacts, but only those which are not in the
// exclusion temp table.
$sql = sprintf("REPLACE INTO %s (contact_id, email_id)
SELECT $contact.id as contact_id, $email.id as email_id
FROM $email
INNER JOIN $contact ON $email.contact_id = $contact.id
INNER JOIN civicrm_group_contact gc ON gc.contact_id = $contact.id
INNER JOIN civicrm_mailing_group mg ON gc.group_id = mg.entity_id AND mg.search_id IS NULL
LEFT JOIN %s temp ON $contact.id = temp.contact_id
WHERE gc.group_id IN (%s) AND gc.status = 'Added' AND %s
GROUP BY $email.id, $contact.id
%s", $includedTempTablename, $excludeTempTablename, implode(',', $recipientsGroup['Include']), implode(' AND ', $includeFilters), $order_by);
$mailingGroup->query($sql);
CRM_Utils_SQL_Select::from($email)
->select("$contact.id as contact_id, $email.id as email_id")
->join($contact, " INNER JOIN $contact ON $email.contact_id = $contact.id ")
->join('gc', " INNER JOIN civicrm_group_contact gc ON gc.contact_id = $contact.id ")
->join('mg', " INNER JOIN civicrm_mailing_group mg ON gc.group_id = mg.entity_id AND mg.search_id IS NULL ")
->join('temp', " LEFT JOIN $excludeTempTablename temp ON $contact.id = temp.contact_id ")
->where('gc.group_id IN (#groups) AND gc.status = "Added"')
->where($includeFilters)
->groupBy(array("$email.id", "$contact.id"))
->replaceInto($includedTempTablename, array('contact_id', 'email_id'))
->param('#groups', $recipientsGroup['Include'])
->execute();

if (count($includeSmartGroupIDs)) {
unset($includeFilters["gc.status = 'Added'"]);
$sql = sprintf("REPLACE INTO %s (contact_id, email_id)
SELECT $contact.id as contact_id, $email.id as email_id
FROM $contact
INNER JOIN civicrm_email ON civicrm_email.contact_id = $contact.id
INNER JOIN civicrm_group_contact_cache gc ON gc.contact_id = $contact.id
LEFT JOIN %s temp ON temp.contact_id = $contact.id
WHERE gc.group_id IN (%s) AND %s %s ", $includedTempTablename, $excludeTempTablename, implode(',', $includeSmartGroupIDs), implode(' AND ', $includeFilters), $order_by);
$mailingGroup->query($sql);
CRM_Utils_SQL_Select::from($contact)
->select("$contact.id as contact_id, $email.id as email_id")
->join($email, " INNER JOIN $email ON $email.contact_id = $contact.id ")
->join('gc', " INNER JOIN civicrm_group_contact_cache gc ON gc.contact_id = $contact.id ")
->join('temp', " LEFT JOIN $excludeTempTablename temp ON temp.contact_id = $contact.id ")
->where('gc.group_id IN (#groups)')
->where($includeFilters)
->orderBy($order_by)
->replaceInto($includedTempTablename, array('contact_id', 'email_id'))
->param('#groups', $includeSmartGroupIDs)
->execute();
}

// Construct the filtered search queries.
$query = " SELECT search_id, search_args, entity_id
FROM civicrm_mailing_group
WHERE search_id IS NOT NULL AND mailing_id = {$mailingID}";
$dao = CRM_Core_DAO::executeQuery($query);
$dao = CRM_Utils_SQL_Select::from('civicrm_mailing_group')
->select('search_id, search_args, entity_id')
->where('search_id IS NOT NULL AND mailing_id = #mailingID')
->param('#mailingID', $mailingID)
->execute();
while ($dao->fetch()) {
$customSQL = CRM_Contact_BAO_SearchCustom::civiMailSQL($dao->search_id,
$dao->search_args,
Expand All @@ -260,35 +260,35 @@ public static function getMailRecipients($mailingObj) {
}

list($aclFrom, $aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause();
$aclWhere = $aclWhere ? "WHERE {$aclWhere}" : '';

// clear all the mailing recipients before populating
$sql = " DELETE FROM civicrm_mailing_recipients WHERE mailing_id = %1 ";
CRM_Core_DAO::executeQuery($sql, array(1 => array($mailingID, 'Integer')));
CRM_Core_DAO::executeQuery(" DELETE FROM civicrm_mailing_recipients WHERE mailing_id = %1 ", array(1 => array($mailingID, 'Integer')));

$selectClause = array('%1', 'i.contact_id', "i.email_id");
$select = "SELECT " . implode(', ', $selectClause);
$selectClause = array('#mailingID', 'i.contact_id', "i.email_id");
// CRM-3975
$groupBy = $groupJoin = '';
$orderBy = "i.contact_id, i.email_id";
$orderBy = array("i.contact_id", "i.email_id");

$query = CRM_Utils_SQL_Select::from('civicrm_contact contact_a')->join('i', " INNER JOIN {$includedTempTablename} i ON contact_a.id = i.contact_id ");
if ($mailingObj->dedupe_email) {
$orderBy = "MIN(i.contact_id), MIN(i.email_id)";
$groupJoin = " INNER JOIN civicrm_email e ON e.id = i.email_id";
$groupBy = " GROUP BY e.email ";
$select = CRM_Contact_BAO_Query::appendAnyValueToSelect($selectClause, 'e.email');
}

$sql = " INSERT INTO civicrm_mailing_recipients ( mailing_id, contact_id, email_id )
{$select}
FROM civicrm_contact contact_a
INNER JOIN {$includedTempTablename} i ON contact_a.id = i.contact_id
{$groupJoin}
{$aclFrom}
{$aclWhere}
$groupBy
ORDER BY {$orderBy}
";
CRM_Core_DAO::executeQuery($sql, array(1 => array($mailingID, 'Integer')));
$orderBy = array("MIN(i.contact_id)", "MIN(i.email_id)");
$query = $query->join('e', " INNER JOIN civicrm_email e ON e.id = i.email_id ")->groupBy("i.email_id");
if (CRM_Utils_SQL::supportsFullGroupBy()) {
$k = array_search("i.email_id", $selectClause);
$selectClause[$k] = "ANY_VALUE(i.email_id)";
}
}

$query = $query->select($selectClause)->orderBy($orderBy);
if (!CRM_Utils_System::isNull($aclFrom)) {
$query = $query->from('acl', $aclFrom);
}
if (!CRM_Utils_System::isNull($aclWhere)) {
$query = $query->where($aclWhere);
}

$query->insertInto('civicrm_mailing_recipients', array('mailing_id', 'contact_id', 'email_id'))
->param('#mailingID', $mailingID)
->execute();

// if we need to add all emails marked bulk, do it as a post filter
// on the mailing recipients table
Expand Down Expand Up @@ -321,19 +321,18 @@ public static function getLocationFilterAndOrderBy($email_selection_method, $loc
$location_filter = "($email.location_type_id != $location_type_id)";
// If there is more than one email that doesn't match the location,
// prefer the one marked is_bulkmail, followed by is_primary.
$order_by = "ORDER BY $email.is_bulkmail, $email.is_primary";
$orderBy = array("$email.is_bulkmail", "$email.is_primary");
break;

case 'location-only':
$location_filter = "($email.location_type_id = $location_type_id)";
// If there is more than one email of the desired location, prefer
// the one marked is_bulkmail, followed by is_primary.
$order_by = "ORDER BY $email.is_bulkmail, $email.is_primary";
$orderBy = array("$email.is_bulkmail", "$email.is_primary");
break;

case 'location-prefer':
$location_filter = "($email.is_bulkmail = 1 OR $email.is_primary = 1 OR $email.location_type_id = $location_type_id)";

// ORDER BY is more complicated because we have to set an arbitrary
// order that prefers the location that we want. We do that using
// the FIELD function. For more info, see:
Expand All @@ -343,17 +342,17 @@ public static function getLocationFilterAndOrderBy($email_selection_method, $loc
// types are left out, so they will be assigned the value 0. That
// means, they will all be equally tied for first place, with our
// location being last.
$order_by = "ORDER BY FIELD($email.location_type_id, $location_type_id), $email.is_bulkmail, $email.is_primary";
$orderBy = array("FIELD($email.location_type_id, $location_type_id)", "$email.is_bulkmail", "$email.is_primary");
break;

case 'automatic':
// fall through to default
default:
$location_filter = "($email.is_bulkmail = 1 OR $email.is_primary = 1)";
$order_by = "ORDER BY $email.is_bulkmail";
$orderBy = array("$email.is_bulkmail");
}

return array($location_filter, $order_by);
return array($location_filter, $orderBy);
}

/**
Expand Down Expand Up @@ -519,7 +518,7 @@ public static function getRecipients(
AND $mg.mailing_id = {$mailing_id}
AND X_$job_id.contact_id IS null
GROUP BY $email.id, $contact.id
$order_by";
ORDER BY " . implode(', ', $order_by);

if ($mode == 'sms') {
$phoneTypes = CRM_Core_OptionGroup::values('phone_type', TRUE, FALSE, FALSE, NULL, 'name');
Expand Down Expand Up @@ -578,7 +577,7 @@ public static function getRecipients(
AND $mg.mailing_id = {$mailing_id}
AND X_$job_id.contact_id IS null
GROUP BY $email.id, $contact.id
$order_by";
ORDER BY " . implode(', ', $order_by);

if ($mode == 'sms') {
$query = "REPLACE INTO I_$job_id (phone_id, contact_id)
Expand Down Expand Up @@ -639,8 +638,7 @@ public static function getRecipients(
AND $location_filter
AND civicrm_email.on_hold = 0
AND X_$job_id.contact_id IS null
$order_by
";
ORDER BY " . implode(', ', $order_by);
if ($mode == 'sms') {
$smartGroupInclude = "
REPLACE INTO I_$job_id (phone_id, contact_id)
Expand Down Expand Up @@ -703,7 +701,7 @@ public static function getRecipients(
AND $mg.mailing_id = {$mailing_id}
AND X_$job_id.contact_id IS null
GROUP BY $email.id, $contact.id
$order_by";
ORDER BY " . implode(', ', $order_by);
if ($mode == "sms") {
$query = "REPLACE INTO I_$job_id (phone_id, contact_id)
SELECT DISTINCT $phone.id as phone_id,
Expand Down
32 changes: 31 additions & 1 deletion CRM/Utils/SQL/Select.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class CRM_Utils_SQL_Select implements ArrayAccess {

private $mode = NULL;
private $insertInto = NULL;
private $insertVerb = 'INSERT INTO ';
private $insertIntoFields = array();
private $selects = array();
private $from;
Expand Down Expand Up @@ -403,6 +404,34 @@ public function insertInto($table, $fields = array()) {
return $this;
}

/**
* Wrapper function of insertInto fn but sets insertVerb = "INSERT IGNORE INTO "
*
* @param string $table
* The name of the other table (which receives new data).
* @param array $fields
* The fields to fill in the other table (in order).
* @return CRM_Utils_SQL_Select
*/
public function insertIgnoreInto($table, $fields = array()) {
$this->insertVerb = "INSERT IGNORE INTO ";
return $this->insertInto($table, $fields);
}

/**
* Wrapper function of insertInto fn but sets insertVerb = "REPLACE INTO "
*
* @param string $table
* The name of the other table (which receives new data).
* @param array $fields
* The fields to fill in the other table (in order).
*/
public function replaceInto($table, $fields = array()) {
$this->insertVerb = "REPLACE INTO ";
return $this->insertInto($table, $fields);
}


/**
* @param array $fields
* The fields to fill in the other table (in order).
Expand Down Expand Up @@ -550,10 +579,11 @@ public function escapeString($value) {
public function toSQL() {
$sql = '';
if ($this->insertInto) {
$sql .= 'INSERT INTO ' . $this->insertInto . ' (';
$sql .= $this->insertVerb . $this->insertInto . ' (';
$sql .= implode(', ', $this->insertIntoFields);
$sql .= ")\n";
}

if ($this->selects) {
$sql .= 'SELECT ' . $this->distinct . implode(', ', $this->selects) . "\n";
}
Expand Down

0 comments on commit 641a960

Please sign in to comment.