*/
protected $addPaging = TRUE;
+ protected $isForceGroupBy = FALSE;
+
+ protected $groupConcatTested = FALSE;
+
/**
* An attribute for checkbox/radio form field layout
*
protected $sql;
+ /**
+ * An instruction not to add a Group By.
+ *
+ * This is relevant where the group by might be otherwise added after the code that determines the group by array.
+ *
+ * e.g. where stat fields are being added but other settings cause it to not be desirable to add a group by
+ * such as in pivot charts when no row header is set
+ *
+ * @var bool
+ */
+ protected $noGroupBy = FALSE;
+
/**
* SQL being run in this report as an array.
*
$this->_columns[$tableName][$fieldGrp][$fieldName]['name'] = $name;
}
+ if (!isset($this->_columns[$tableName][$fieldGrp][$fieldName]['table_name'])) {
+ $this->_columns[$tableName][$fieldGrp][$fieldName]['table_name'] = $tableName;
+ }
+
// set dbAlias = alias.name, unless already set
if (!isset($this->_columns[$tableName][$fieldGrp][$fieldName]['dbAlias'])) {
$this->_columns[$tableName][$fieldGrp][$fieldName]['dbAlias']
$select = $this->addStatisticsToSelect($field, $tableName, $fieldName, $select);
}
else {
- $select = $this->addBasicFieldToSelect($tableName, $fieldName, $field, $select);
+
+ $selectClause = $this->getSelectClauseWithGroupConcatIfNotGroupedBy($tableName, $fieldName, $field);
+ if ($selectClause) {
+ $select[] = $selectClause;
+ }
+ else {
+ $select = $this->addBasicFieldToSelect($tableName, $fieldName, $field, $select);
+ }
}
}
}
* @return bool
*/
public function selectClause(&$tableName, $tableKey, &$fieldName, &$field) {
+ if (!empty($field['pseudofield'])) {
+ $alias = "{$tableName}_{$fieldName}";
+ $this->_columnHeaders["{$tableName}_{$fieldName}"]['title'] = CRM_Utils_Array::value('title', $field);
+ $this->_columnHeaders["{$tableName}_{$fieldName}"]['type'] = CRM_Utils_Array::value('type', $field);
+ $this->_columnHeaders["{$tableName}_{$fieldName}"]['dbAlias'] = CRM_Utils_Array::value('dbAlias', $field);
+ $this->_selectAliases[] = $alias;
+ return ' 1 as ' . $alias;
+ }
return FALSE;
}
$this->groupBy();
$this->orderBy();
- // order_by columns not selected for display need to be included in SELECT
- $unselectedSectionColumns = $this->unselectedSectionColumns();
- foreach ($unselectedSectionColumns as $alias => $section) {
- $this->_select .= ", {$section['dbAlias']} as {$alias}";
+ foreach ($this->unselectedOrderByColumns() as $alias => $field) {
+ $clause = $this->getSelectClauseWithGroupConcatIfNotGroupedBy($field['table_name'], $field['name'], $field);
+ if (!$clause) {
+ $clause = "{$field['dbAlias']} as {$alias}";
+ }
+ $this->_select .= ", $clause ";
}
if ($applyLimit && empty($this->_params['charts'])) {
if (!empty($orderByField)) {
$this->_orderByFields[$orderByField['tplField']] = $orderByField;
- $orderBys[] = "{$orderByField['dbAlias']} {$orderBy['order']}";
+ if ($this->groupConcatTested) {
+ $orderBys[$orderByField['tplField']] = "{$orderByField['tplField']} {$orderBy['order']}";
+ }
+ else {
+ // Not sure when this is preferable to using tplField (which has
+ // definitely been tested to work in cases then this does not.
+ // in caution not switching unless report has been tested for
+ // group concat functionality.
+ $orderBys[$orderByField['tplField']] = "{$orderByField['dbAlias']} {$orderBy['order']}";
+ }
// Record any section headers for assignment to the template
if (!empty($orderBy['section'])) {
$this->assign('sections', $this->_sections);
}
+ /**
+ * Determine unselected columns.
+ *
+ * @return array
+ */
+ public function unselectedOrderByColumns() {
+ return array_diff_key($this->_orderByFields, $this->getSelectColumns());
+ }
+
/**
* Determine unselected columns.
*
return $defaults;
}
+ /**
+ * Get the select clause for a field, wrapping in GROUP_CONCAT if appropriate.
+ *
+ * Full group by mode dictates that a field must either be in the group by function or
+ * wrapped in a aggregate function. Here we wrap the field in GROUP_CONCAT if it is not in the
+ * group concat.
+ *
+ * @param string $tableName
+ * @param string $fieldName
+ * @param string $field
+ * @return string
+ */
+ protected function getSelectClauseWithGroupConcatIfNotGroupedBy($tableName, &$fieldName, &$field) {
+ if ($this->groupConcatTested && (!empty($this->_groupByArray) || $this->isForceGroupBy)) {
+ if ((empty($field['statistics']) || in_array('GROUP_CONCAT', $field['statistics']))) {
+ $label = CRM_Utils_Array::value('title', $field);
+ $alias = "{$tableName}_{$fieldName}";
+ $this->_columnHeaders["{$tableName}_{$fieldName}"]['title'] = $label;
+ $this->_selectAliases[] = $alias;
+ if (empty($this->_groupByArray[$tableName . '_' . $fieldName])) {
+ return "GROUP_CONCAT(DISTINCT {$field['dbAlias']}) as $alias";
+ }
+ return "({$field['dbAlias']}) as $alias";
+ }
+ }
+ }
+
}
'Contribution',
);
+ protected $groupConcatTested = TRUE;
+
/**
* This report has been optimised for group filtering.
*
'payment_instrument_id' => array('title' => ts('Payment Method')),
'receive_date' => array('title' => ts('Date Received')),
),
+ 'group_bys' => array(
+ 'contribution_id' => array(
+ 'name' => 'id',
+ 'required' => TRUE,
+ 'title' => ts('Contribution'),
+ ),
+ ),
'grouping' => 'contri-fields',
),
'civicrm_contribution_soft' => array(
'fields' => array(
'card_type_id' => array(
'title' => ts('Credit Card Type'),
- 'dbAlias' => 'GROUP_CONCAT(financial_trxn_civireport.card_type_id SEPARATOR ",")',
),
),
'filters' => array(
parent::__construct();
}
- public function preProcess() {
- parent::preProcess();
- }
-
- public function select() {
- $this->_columnHeaders = array();
-
- parent::select();
- }
-
- public function orderBy() {
- parent::orderBy();
-
- // please note this will just add the order-by columns to select query, and not display in column-headers.
- // This is a solution to not throw fatal errors when there is a column in order-by, not present in select/display columns.
- foreach ($this->_orderByFields as $orderBy) {
- if (!array_key_exists($orderBy['name'], $this->_params['fields']) &&
- empty($orderBy['section']) && (strpos($this->_select, $orderBy['dbAlias']) === FALSE)
- ) {
- $this->_select .= ", {$orderBy['dbAlias']} as {$orderBy['tplField']}";
- }
- }
- }
-
/**
* Set the FROM clause for the report.
*/
$this->appendAdditionalFromJoins();
}
- public function groupBy() {
- $groupBy = array("{$this->_aliases['civicrm_contact']}.id", "{$this->_aliases['civicrm_contribution']}.id");
- $this->_groupBy = CRM_Contact_BAO_Query::getGroupByFromSelectColumns($this->_selectClauses, $groupBy);
- }
-
/**
* @param $rows
*
$this->addToDeveloperTab($sql);
CRM_Core_DAO::executeQuery($sql);
- // 5. Re-construct order-by to make sense for final query on temp3 table
- $orderBy = '';
- if (!empty($this->_orderByArray)) {
- $aliases = array_flip($this->_aliases);
- $orderClause = array();
- foreach ($this->_orderByArray as $clause) {
- list($alias, $rest) = explode('.', $clause);
- // CRM-17280 -- In case, we are ordering by custom fields
- // modify $rest to match the alias used for them in temp3 table
- $grp = new CRM_Core_DAO_CustomGroup();
- $grp->table_name = $aliases[$alias];
- if ($grp->find()) {
- list($fld, $order) = explode(' ', $rest);
- foreach ($this->_columns[$aliases[$alias]]['fields'] as $fldName => $value) {
- if ($value['name'] == $fld) {
- $fld = $fldName;
- }
- }
- $rest = "{$fld} {$order}";
- }
- $orderClause[] = $aliases[$alias] . "_" . $rest;
- }
- $orderBy = (!empty($orderClause)) ? "ORDER BY " . implode(', ', $orderClause) : '';
- }
-
// 6. show result set from temp table 3
$rows = array();
- $sql = "SELECT * FROM civireport_contribution_detail_temp3 {$orderBy}";
+ $sql = "SELECT * FROM civireport_contribution_detail_temp3 $this->_orderBy";
$this->buildRows($sql, $rows);
// format result set.