*/
protected $_groupFilter = FALSE;
+ /**
+ * Has the report been optimised for group filtering.
+ *
+ * The functionality for group filtering has been improved but not
+ * all reports have been adjusted to take care of it.
+ *
+ * This property exists to highlight the reports which are still using the
+ * slow method & allow group filtering to still work for them until they
+ * can be migrated.
+ *
+ * In order to protect extensions we have to default to TRUE - but I have
+ * separately marked every class with a groupFilter in the hope that will trigger
+ * people to fix them as they touch them.
+ *
+ * CRM-19170
+ *
+ * @var bool
+ */
+ protected $groupFilterNotOptimised = TRUE;
+
/**
* Navigation fields
*
protected $_selectAliases = array();
protected $_rollup = NULL;
+ /**
+ * Table containing list of contact IDs within the group filter.
+ *
+ * @var string
+ */
+ protected $groupTempTable = '';
+
/**
* @var array
*/
$this->assignTabs();
$this->sqlArray[] = $sql;
- foreach (array('LEFT JOIN') as $term) {
- $sql = str_replace($term, '<br />  ' . $term, $sql);
- }
- foreach (array('FROM', 'WHERE', 'GROUP BY', 'ORDER BY', 'LIMIT', ';') as $term) {
- $sql = str_replace($term, '<br /><br />' . $term, $sql);
+ foreach ($this->sqlArray as $sql) {
+ foreach (array('LEFT JOIN') as $term) {
+ $sql = str_replace($term, '<br>  ' . $term, $sql);
+ }
+ foreach (array('FROM', 'WHERE', 'GROUP BY', 'ORDER BY', 'LIMIT', ';') as $term) {
+ $sql = str_replace($term, '<br><br>' . $term, $sql);
+ }
+ $this->sqlFormattedArray[] = $sql;
+ $this->assign('sql', implode(';<br><br><br><br>', $this->sqlFormattedArray));
}
- $this->sql .= $sql . "<br />";
-
- $this->assign('sql', $this->sql);
}
/**
case 'in':
case 'notin':
- if (is_string($value) && strlen($value)) {
+ if ((is_string($value) || is_numeric($value)) && strlen($value)) {
$value = explode(',', $value);
}
if ($value !== NULL && is_array($value) && count($value) > 0) {
* @return string
*/
public function buildQuery($applyLimit = TRUE) {
+ $this->buildGroupTempTable();
$this->select();
$this->from();
$this->customDataFrom();
}
/**
- * Build where clause for groups.
+ * Build a group filter with contempt for large data sets.
+ *
+ * This function has been retained as it takes time to migrate the reports over
+ * to the new method which will not crash on large datasets.
+ *
+ * @deprecated
*
* @param string $field
* @param mixed $value
*
* @return string
*/
- public function whereGroupClause($field, $value, $op) {
-
+ public function legacySlowGroupFilterClause($field, $value, $op) {
$smartGroupQuery = "";
$group = new CRM_Contact_DAO_Group();
{$smartGroupQuery} ) ";
}
+ /**
+ * Build where clause for groups.
+ *
+ * @param string $field
+ * @param mixed $value
+ * @param string $op
+ *
+ * @return string
+ */
+ public function whereGroupClause($field, $value, $op) {
+ if ($this->groupFilterNotOptimised) {
+ return $this->legacySlowGroupFilterClause($field, $value, $op);
+ }
+ if ($op === 'notin') {
+ return " group_temp_table.id IS NULL ";
+ }
+ // We will have used an inner join instead.
+ return "1";
+ }
+
+
+ /**
+ * Create a table of the contact ids included by the group filter.
+ *
+ * This function is called by both the api (tests) and the UI.
+ */
+ public function buildGroupTempTable() {
+ if (!empty($this->groupTempTable) || empty ($this->_params['gid_value']) || $this->groupFilterNotOptimised) {
+ return;
+ }
+ $filteredGroups = (array) $this->_params['gid_value'];
+
+ $groups = civicrm_api3('Group', 'get', array(
+ 'is_active' => 1,
+ 'id' => array('IN' => $filteredGroups),
+ 'saved_search_id' => array('>' => 0),
+ 'return' => 'id',
+ ));
+ $smartGroups = array_keys($groups['values']);
+
+ $query = "
+ SELECT group_contact.contact_id as id
+ FROM civicrm_group_contact group_contact
+ WHERE group_contact.group_id IN (" . implode(', ', $filteredGroups) . ")
+ AND group_contact.status = 'Added' ";
+
+ if (!empty($smartGroups)) {
+ CRM_Contact_BAO_GroupContactCache::check($smartGroups);
+ $smartGroups = implode(',', $smartGroups);
+ $query .= "
+ UNION DISTINCT
+ SELECT smartgroup_contact.contact_id as id
+ FROM civicrm_group_contact_cache smartgroup_contact
+ WHERE smartgroup_contact.group_id IN ({$smartGroups}) ";
+ }
+
+ $this->groupTempTable = 'civicrm_report_temp_group_' . date('Ymd_') . uniqid();
+ $this->executeReportQuery("
+ CREATE TEMPORARY TABLE $this->groupTempTable
+ $query
+ ");
+ CRM_Core_DAO::executeQuery("ALTER TABLE $this->groupTempTable ADD INDEX i_id(id)");
+ }
+
+ /**
+ * Execute query and add it to the developer tab.
+ *
+ * @param string $query
+ * @param array $params
+ *
+ * @return \CRM_Core_DAO|object
+ */
+ protected function executeReportQuery($query, $params = array()) {
+ $this->addToDeveloperTab($query);
+ return CRM_Core_DAO::executeQuery($query, $params);
+ }
+
/**
* Build where clause for tags.
*
}
}
+ /**
+ * Set the base table for the FROM clause.
+ *
+ * Sets up the from clause, allowing for the possibility it might be a
+ * temp table pre-filtered by groups if a group filter is in use.
+ *
+ * @param string $baseTable
+ * @param string $field
+ * @param null $tableAlias
+ */
+ public function setFromBase($baseTable, $field = 'id', $tableAlias = NULL) {
+ if (!$tableAlias) {
+ $tableAlias = $this->_aliases[$baseTable];
+ }
+ $this->_from = $this->_from = " FROM $baseTable $tableAlias ";
+ $this->joinGroupTempTable($baseTable, $field, $tableAlias);
+ $this->_from .= " {$this->_aclFrom} ";
+ }
+
+ /**
+ * Join the temp table contacting contacts who are members of the filtered groups.
+ *
+ * If we are using an IN filter we use an inner join, otherwise a left join.
+ *
+ * @param string $baseTable
+ * @param string $field
+ * @param string $tableAlias
+ */
+ public function joinGroupTempTable($baseTable, $field, $tableAlias) {
+ if ($this->groupTempTable) {
+ if ($this->_params['gid_op'] == 'in') {
+ $this->_from = " FROM $this->groupTempTable group_temp_table INNER JOIN $baseTable $tableAlias
+ ON group_temp_table.id = $tableAlias.{$field} ";
+ }
+ else {
+ $this->_from .= "
+ LEFT JOIN $this->groupTempTable group_temp_table
+ ON $tableAlias.{$field} = group_temp_table.id ";
+ }
+ }
+ }
+
}