From 05a30fe2c712b4ffc9491cd88de018b26cf5446e Mon Sep 17 00:00:00 2001
From: Luigi Guevara <luigi.fox15@gmail.com>
Date: Tue, 27 May 2014 11:04:49 +0200
Subject: [PATCH] Reroll for dev branch

---
 plugins/views_plugin_query_default.inc | 126 +++++++++++++++++++++++++++------
 1 file changed, 106 insertions(+), 20 deletions(-)

diff --git a/plugins/views_plugin_query_default.inc b/plugins/views_plugin_query_default.inc
index 8a15e8d..7b2cd9e 100644
--- a/plugins/views_plugin_query_default.inc
+++ b/plugins/views_plugin_query_default.inc
@@ -1194,11 +1194,16 @@ class views_plugin_query_default extends views_plugin_query {
       }
 
       if (!empty($field['function'])) {
-        $info = $this->get_aggregation_info();
-        if (!empty($info[$field['function']]['method']) && function_exists($info[$field['function']]['method'])) {
-          $string = $info[$field['function']]['method']($field['function'], $string);
+        $group_type = $field['function'];
+        if (!empty($info[$group_type]['extender']) && class_exists($info[$group_type]['extender'])) {
+          // Only extend the query object once.
+          // NB: this only works because our extender is the only one around.
+          // See ViewsAggregateQuery for more details.
+          if (!$this->has_aggregate) {
+            $query = $query->extend($info[$group_type]['extender']);
+          }
           $placeholders = !empty($field['placeholders']) ? $field['placeholders'] : array();
-          $query->addExpression($string, $fieldname, $placeholders);
+          call_user_func(array($query, $info[$group_type]['method']), $group_type, $string, $fieldname, $placeholders);
         }
 
         $this->has_aggregate = TRUE;
@@ -1515,6 +1520,21 @@ class views_plugin_query_default extends views_plugin_query {
     $view->query->add_field(NULL, "'" . $view->name . ':' . $view->current_display . "'", 'view_name');
   }
 
+  /**
+   * Define aggregate functions and how to apply them to a query.
+   *
+   * @return
+   *   An array of information about aggregate functions, keyed by the SQL
+   *   function name. Each item may have the following properties:
+   *    - 'title': The title used in the UI.
+   *    - 'is aggregate': @todo Does not appear to be used.
+   *    - 'extender': The class of a query extender which must be added to the
+   *      query.
+   *    - 'method': The method to call on the query once it has been extended
+   *      with the above.
+   *    - 'handler': An array of handlers of each type to replace the original
+   *      handler which has this aggregate function applied.
+   */
   function get_aggregation_info() {
     // @todo -- need a way to get database specific and customized aggregation
     // functions into here.
@@ -1525,7 +1545,8 @@ class views_plugin_query_default extends views_plugin_query {
       ),
       'count' => array(
         'title' => t('Count'),
-        'method' => 'views_query_default_aggregation_method_simple',
+        'extender' => 'ViewsAggregateQuery',
+        'method' => 'addAggregateSimple',
         'handler' => array(
           'argument' => 'views_handler_argument_group_by_numeric',
           'field' => 'views_handler_field_numeric',
@@ -1535,7 +1556,8 @@ class views_plugin_query_default extends views_plugin_query {
       ),
       'count_distinct' => array(
         'title' => t('Count DISTINCT'),
-        'method' => 'views_query_default_aggregation_method_distinct',
+        'extender' => 'ViewsAggregateQuery',
+        'method' => 'addAggregateDistinct',
         'handler' => array(
           'argument' => 'views_handler_argument_group_by_numeric',
           'field' => 'views_handler_field_numeric',
@@ -1545,7 +1567,8 @@ class views_plugin_query_default extends views_plugin_query {
       ),
       'sum' => array(
         'title' => t('Sum'),
-        'method' => 'views_query_default_aggregation_method_simple',
+        'extender' => 'ViewsAggregateQuery',
+        'method' => 'addAggregateSimple',
         'handler' => array(
           'argument' => 'views_handler_argument_group_by_numeric',
           'filter' => 'views_handler_filter_group_by_numeric',
@@ -1554,7 +1577,8 @@ class views_plugin_query_default extends views_plugin_query {
       ),
       'avg' => array(
         'title' => t('Average'),
-        'method' => 'views_query_default_aggregation_method_simple',
+        'extender' => 'ViewsAggregateQuery',
+        'method' => 'addAggregateSimple',
         'handler' => array(
           'argument' => 'views_handler_argument_group_by_numeric',
           'filter' => 'views_handler_filter_group_by_numeric',
@@ -1563,7 +1587,8 @@ class views_plugin_query_default extends views_plugin_query {
       ),
       'min' => array(
         'title' => t('Minimum'),
-        'method' => 'views_query_default_aggregation_method_simple',
+        'extender' => 'ViewsAggregateQuery',
+        'method' => 'addAggregateSimple',
         'handler' => array(
           'argument' => 'views_handler_argument_group_by_numeric',
           'filter' => 'views_handler_filter_group_by_numeric',
@@ -1572,7 +1597,18 @@ class views_plugin_query_default extends views_plugin_query {
       ),
       'max' => array(
         'title' => t('Maximum'),
-        'method' => 'views_query_default_aggregation_method_simple',
+        'extender' => 'ViewsAggregateQuery',
+        'method' => 'addAggregateSimple',
+        'handler' => array(
+          'argument' => 'views_handler_argument_group_by_numeric',
+          'filter' => 'views_handler_filter_group_by_numeric',
+          'sort' => 'views_handler_sort_group_by_numeric',
+        ),
+      ),
+      'group_concat' => array(
+        'title' => t('Group concat'),
+        'extender' => 'ViewsAggregateQuery',
+        'method' => 'addAggregateSimple',
         'handler' => array(
           'argument' => 'views_handler_argument_group_by_numeric',
           'filter' => 'views_handler_filter_group_by_numeric',
@@ -1581,7 +1617,8 @@ class views_plugin_query_default extends views_plugin_query {
       ),
       'stddev_pop' => array(
         'title' => t('Standard deviation'),
-        'method' => 'views_query_default_aggregation_method_simple',
+        'extender' => 'ViewsAggregateQuery',
+        'method' => 'addAggregateSimple',
         'handler' => array(
           'argument' => 'views_handler_argument_group_by_numeric',
           'filter' => 'views_handler_filter_group_by_numeric',
@@ -1636,15 +1673,6 @@ class views_plugin_query_default extends views_plugin_query {
   }
 }
 
-function views_query_default_aggregation_method_simple($group_type, $field) {
-  return strtoupper($group_type) . '(' . $field . ')';
-}
-
-function views_query_default_aggregation_method_distinct($group_type, $field) {
-  $group_type = str_replace('_distinct', '', $group_type);
-  return strtoupper($group_type) . '(DISTINCT ' . $field . ')';
-}
-
 /**
  * Validation callback for query tags.
  */
@@ -1657,3 +1685,61 @@ function views_element_validate_tags($element, &$form_state) {
     }
   }
 }
+
+/**
+ * Query extender for aggregate expressions.
+ *
+ * Provides methods for adding aggregate expressions to query. Each method name
+ * follows the pattern addAggretateTYPE() and has the same parameters: see
+ * addAggregateSimple() for these.
+ *
+ * This all works because we're the only extender being used here.
+ * Should get_aggregation_info() be opened up to let other modules define
+ * aggregation methods (which is http://drupal.org/node/1359298), we'll need a
+ * way to make sure we only extend by each class once (for which there is proof
+ * of concept code here: http://drupal.org/node/1365210).
+ */
+class ViewsAggregateQuery extends SelectQueryExtender {
+  /**
+   * Add a simple aggregate expression to a query.
+   *
+   * @param $group_type
+   *   The grouping function name, eg 'count'.
+   *   Note that all our aggregate expression methods have this parameter but
+   *   methods that are specialized to just one SQL aggregate function will not
+   *   need to make use of it.
+   * @param $column_name
+   *   The column name to aggregate, in the form 'table.column'.
+   * @param $fieldname
+   *   The alias to give the expression, that Views will consider to be the
+   *   name of the field.
+   * @param $placeholders
+   *   The arguments for the expression.
+   */
+  function addAggregateSimple($group_type, $column_name, $fieldname, $placeholders) {
+    // Create the aggregate expression.
+    $function = strtoupper($group_type);
+    $expression = "$function($column_name)";
+    $this->query->addExpression($expression, $fieldname, $placeholders);
+    return $this;
+  }
+
+  /**
+   * Add a DISTINCT aggregate expression to a query.
+   */
+  function addAggregateDistinct($group_type, $column_name, $fieldname, $placeholders) {
+    $group_type = str_replace('_distinct', '', $group_type);
+    $expression = strtoupper($group_type) . '(DISTINCT ' . $column_name . ')';
+    $this->query->addExpression($expression, $fieldname, $placeholders);
+    return $this;
+  }
+
+  /**
+   * Add a GROUP_CONCAT() expression to a query.
+   */
+  function addAggregateGroupConcat($group_type, $column_name, $fieldname, $placeholders) {
+    $expression = "(GROUP_CONCAT($column_name))";
+    $this->query->addExpression($expression, $fieldname, $placeholders);
+    return $this;
+  }
+}
-- 
1.9.0.msysgit.0

