diff --git a/theme.inc b/theme.inc index 57d61ed..7db435b 100644 --- a/theme.inc +++ b/theme.inc @@ -220,11 +220,16 @@ function template_preprocess_views_calc_table(&$vars) { // Add totals. $vars['totals'] = array(); $vars['sub_totals'] = array(); - if ($view->query->pager->get_total_items() > $view->get_items_per_page() - && isset($view->sub_totals)) { + $vars['group_totals'] = array(); + if (isset($view->group_total)) { + views_calc_table_total($vars, 'group_totals', $view->group_total); + } + if ($view->get_items_per_page() && $view->query->pager->plugin_name != 'none' && + $view->query->pager->get_total_items() > $view->get_items_per_page() + && isset($view->sub_totals) && $view->last_group) { views_calc_table_total($vars, 'sub_totals', $view->sub_totals); } - if (isset($view->totals)) { + if (isset($view->totals) && $view->last_group) { views_calc_table_total($vars, 'totals', $view->totals); } @@ -342,10 +347,18 @@ function views_calc_table_total(&$vars, $key, $totals) { // when both are provided. if (empty($added_label[$calc])) { if ($key == 'sub_totals') { - $label = t("Page !Calculation", array("!Calculation" => $calc)); + $Total_and_Calc = "Page Total" . ' ' . $calc; + $label = t($Total_and_Calc, array("!Calculation" => $calc)); } else { - $label = t("Total !Calculation", array("!Calculation" => $calc)); + if ($view->group_total && $key != 'totals') { + $Total_and_Calc = "Subtotal" . ' ' . $calc; + $label = t($Total_and_Calc, array("!Calculation" => $calc)); + } + else { + $Total_and_Calc = "Total" . ' ' . $calc; + $label = t($Total_and_Calc, array("!Calculation" => $calc)); + } } $vars[$key][$calc][$column] = $label; $added_label[$calc] = TRUE; diff --git a/views-calc-table.tpl.php b/views-calc-table.tpl.php index 6d3618e..657e3f7 100644 --- a/views-calc-table.tpl.php +++ b/views-calc-table.tpl.php @@ -41,6 +41,16 @@ if (empty($rows) && empty($totals)) { + $row): ?> + + $content): ?> + + + + + + + $row): ?> $content): ?> diff --git a/views_calc.css b/views_calc.css index d272f75..0bb0f78 100644 --- a/views_calc.css +++ b/views_calc.css @@ -11,6 +11,20 @@ td.view-footer-number { border-bottom:3px #000 double; border-top:1px #000 solid; } + +td.view-groupfooter, +td.view-groupfooter-number { + font-weight:normal; + background-color:#fff; + } + +td.view-groupfooter, +td.view-groupfooter-number { + border-bottom:1px #000 solid; + border-top:1px #000 solid; + margin-bottom: 10px; + } + td.view-subfooter, td.view-subfooter-number { font-weight:normal; diff --git a/views_calc_table.inc b/views_calc_table.inc index 7e17548..c515cfd 100644 --- a/views_calc_table.inc +++ b/views_calc_table.inc @@ -122,6 +122,7 @@ class views_calc_table extends views_plugin_style_table { $this->view->totals = array(); $this->view->sub_totals = array(); + $this->view->group_totals = array(); $this->view->views_calc_fields = $calc_fields; $this->view->views_calc_calculation = FALSE; @@ -129,26 +130,33 @@ class views_calc_table extends views_plugin_style_table { // check if Subtotals are displayed if (!empty($maxitems) && ($this->view->query->pager->get_total_items() > $maxitems)) { - $ids = array(); - foreach ($this->view->result as $delta => $value) { - $ids[] = $value->{$this->view->base_field}; - } + // Add sub_total rows to the results. // We need one query per aggregation because theming needs unrenamed views field alias. // TODO Looks like we have problems unless we // force a non-page display, need to keep an eye on this. - $this->execute_summary_view($ids); + $this->execute_summary_view('subtotal'); } // Add grand totals to the results. $this->execute_summary_view(); } - function execute_summary_view($ids = array()) { + /** + * @TODO This function needs a clean up, redundant view object probably should be + * deleted. Should subquery be cached? + * + * @param $type + * Possible values are: 'group' or 'subtotal' + * @param $group_options + * Should be passed when $type is 'group' + */ + function execute_summary_view($type = FALSE, $group_options = array()) { // Clone view for local subquery. $summary_view = $this->view->clone_view(); $summary_view->set_display($this->view->current_display); // copy the query object by value not by reference! $summary_view->query = clone $this->view->query; + $summary_view->query->pager = clone $this->view->query->pager; $summary_view->set_display($this->view->current_display); // Make sure the view is completely valid. @@ -160,43 +168,48 @@ class views_calc_table extends views_plugin_style_table { return NULL; } + $is_subtotal = ($type == 'subtotal') ? TRUE : FALSE; + $is_group = ($type == 'group') ? TRUE : FALSE; // intialize summary view - $is_subtotal = !empty($ids); $summary_view->preview = TRUE; $summary_view->is_cacheable = FALSE; $summary_view->views_calc_calculation = TRUE; - $summary_view->views_calc_sub_total = $is_subtotal; - // If it's a subtotal calc, we're filtering the page elements by $ids. - $summary_view->views_calc_ids = $ids; + $summary_view->views_calc_fields = $this->view->views_calc_fields; $summary_view->build_info = $this->view->build_info; $summary_view->field = $this->view->field; - // Call the method which adds aggregation fields before rendering - // In an earlier version this was done in the overridden method query() of this class - // which is not called for some reason here. - $this->add_aggregation_fields($summary_view); + if (empty($this->view->views_calc_fields)) { + // We should do nothing if user have not choosen fields to calc. + return; + } - // We don't need any offset in the calculation queries because - // the statement does only return the records with the passed ids - $summary_view->query->offset = 0; + $subquery = clone $summary_view->build_info['query']; + if (!$is_subtotal && !$is_group) { + // All results of the calculation queries are used, so we don't page them. + // Else we will loose the global pager from the page view. + $subquery->range(); + } - // Execute and render the view in preview mode. Note that we only store - // the result if the executed view query returns any result. - $summary_view->pre_execute($this->view->args); + $summary_view->subquery = db_select($subquery, 'summary'); + $this->add_aggregation_fields($summary_view); + if ($is_group) { + foreach ($group_options as $group_field => $field_value) { + $summary_view->subquery->condition($group_field, $field_value); + } + } - // All results of the calculation queries are used, so we don't page them. - // Else we will loose the global pager from the page view. - $summary_view->set_items_per_page(0); - $summary_view->execute(); - $summary_view->post_execute(); + $result = $summary_view->subquery->execute()->fetchAll(); - if (!empty($summary_view->result)) { - if ($is_subtotal) { - $this->view->sub_totals = array_shift($summary_view->result); + if (!empty($result)) { + if ($is_group) { + $this->view->group_totals[] = array_shift($result); + } + elseif ($is_subtotal) { + $this->view->sub_totals = array_shift($result); } else { - $this->view->totals = array_shift($summary_view->result); + $this->view->totals = array_shift($result); } } @@ -207,26 +220,16 @@ class views_calc_table extends views_plugin_style_table { */ function add_aggregation_fields(&$view) { // Create summary rows. - // Empty out any fields that have been added to the query, - // we don't need them for the summary totals. - $view->query->fields = array(); - // Clear out any sorting and grouping, it can create unexpected results - // when Views adds aggregation values for the sorts. - $view->query->orderby = array(); - $view->query->groupby = array(); - - // See if this view has any calculations. - $has_calcs = TRUE; $calc_fields = $view->views_calc_fields; foreach ($calc_fields as $calc => $fields) { foreach ($view->field as $field) { // Is this a field or a property? if (!empty($field->field_info)) { - $query_field = substr($field->field, 0, 3) == 'cid' ? $field->definition['calc'] : $field->table . '.' . $field->real_field; + $table = $field->table; } else { - $query_field = substr($field->field, 0, 3) == 'cid' ? $field->definition['calc'] : $field->table_alias . '.' . $field->real_field; + $table = $field->table_alias; } if (isset($field->aliases['entity_type'])) { $query_alias = $field->aliases['entity_type']; @@ -238,29 +241,15 @@ class views_calc_table extends views_plugin_style_table { if ($query_alias == 'unknown') { continue; } - $view->query->add_table($field->table, $field->relationship, NULL, $field->table); + // aggregation functions $ext_alias = views_calc_shorten($calc . '__' . $query_alias); if (in_array($field->options['id'], $fields)) { // Calculated fields. - $view->query->add_field(NULL, $calc . '(' . $query_field . ')', $ext_alias); - $has_calcs = TRUE; + $view->subquery->addExpression($calc.'(summary.'.$query_alias.')',$ext_alias); } } } - // TODO This won't work right with relationships, need a fix here. - // Limit to specific primary ids. This is for subtotals. - if (!empty($view->views_calc_ids)) { - //$view->query->add_where(NULL, $view->base_table . "." . $view->base_field . " IN (%s)", implode(',', $view->views_calc_ids)); - $view->query->add_where(NULL, $view->base_table . "." . $view->base_field, $view->views_calc_ids); - } - - // TODO We may need to ask which field to group by. - // Some databases are going to complain if we don't have a groupby field with using aggregation. - if ($has_calcs) { - $this->view->query->add_groupby($this->view->base_table . '.' . $this->view->base_field); - } - } /** @@ -288,4 +277,72 @@ class views_calc_table extends views_plugin_style_table { } return $calc_fields; } + + + /** + * Override of the default style render, to include group totals. + */ + function render() { + if ($this->uses_row_plugin() && empty($this->row_plugin)) { + vpr('views_plugin_style_default: Missing row plugin'); + return; + } + + // Group the rows according to the grouping field, if specified. + $sets = $this->render_grouping($this->view->result, $this->options['grouping']); + + if (count($sets) > 1 ) { + $this->calc_group_totals($sets); + } + + // Render each group separately and concatenate. + $output = ''; + $group_index = 0; + $last_group_index = count($sets) - 1; + foreach ($sets as $title => $records) { + if ($this->uses_row_plugin()) { + $rows = array(); + foreach ($records as $row_index => $row) { + $this->view->row_index = $row_index; + $rows[] = $this->row_plugin->render($row); + } + } + else { + $rows = $records; + } + + $group_total = NULL; + if (isset($this->view->group_totals[$group_index])) { + $group_total = $this->view->group_totals[$group_index]; + } + $this->view->group_total = $group_total; + $this->view->last_group = ($group_index >= $last_group_index); + + $output .= theme($this->theme_functions(), array('view'=>$this->view, 'options'=>$this->options, 'rows'=>$rows, 'title'=>$title)); + $group_index++; + } + unset($this->view->row_index); + return $output; + } + + /** + * Calculates group totals. + * + * Should only be called if $sets has at least two sets in it. + */ + function calc_group_totals($sets) { + $field = $this->view->base_field; + $this->view->group_totals = array(); + + foreach ($sets as $set) { + $group_options = array(); + $row = array_shift($set); + foreach ($this->options['grouping'] as $info) { + $group_field = $this->view->field[$info['field']]->field_alias; + $group_options[$group_field] = $row->{$group_field}; + } + $this->execute_summary_view('group', $group_options); + } + } + }