Commit 0e0b0662 authored by Jordan Karlov's avatar Jordan Karlov Committed by Jordan Karlov
Browse files

Issue #3239548 by JordiK: Views pager ignores group aggregation

parent dc82c0c5
Loading
Loading
Loading
Loading
+50 −29
Original line number Diff line number Diff line
@@ -237,8 +237,8 @@ class Table extends ViewsTable {
      '#title' => $this->t('Column aggregation row applies to'),
      '#type' => 'radios',
      '#options' => [
        1 => $this->t('the page shown, if a pager is enabled'),
        0 => $this->t('the entire result set (CAUTION: To enable this, create a copy of this view display, disable the pager there, set the <i>machine name</i> to <i>DISPLAY_NAME_no_pager</i> (e.g. page_1_no_pager) and give it a different path.)'),
        0 => $this->t('the entire result set'),
        1 => $this->t('the page shown, if a pager is enabled or the result subset, if offset is defined'),
      ],
      '#description' => $this->t('If your view does not have a pager, then the two options are equivalent.'),
      '#default_value' => $this->options['column_aggregation']['totals_per_page'],
@@ -325,30 +325,8 @@ class Table extends ViewsTable {
      return;
    }
    $functions = $this->collectAggregationFunctions();
    $show_global_totals_with_pager = empty($this->options['column_aggregation']['totals_per_page']) && !empty($this->view->total_rows);

    if ($show_global_totals_with_pager) {
      $this->view->is_temp_views_aggregator = TRUE;
      $args = $this->view->args;
    $display_id = $this->view->current_display;

      $view_displays = $this->view->displayHandlers->getInstanceIds();
      if (in_array($display_id . '_no_pager', $view_displays)) {
        $clone = $this->view->createDuplicate();
        $clone->is_temp_views_aggregator = TRUE;
        $clone->executeDisplay($display_id . '_no_pager', $args);

        // First apply the row filters (if any), then aggregate the columns.
        // Only interested in column aggregation, so only 'column' group needed.
        $column_group = ['column' => []];
        foreach ($clone->result as $num => $row) {
          $column_group['column'][$num] = $row;
        }
        $totals = $clone->style_plugin->executeAggregationFunctions($column_group, $functions);
        $clone->postExecute();
        $clone->destroy();
      }
    }
    // Because we are going to need the View results AFTER token replacement,
    // we render the result set here. This is NOT duplication of CPU time,
    // because self::renderFields(), if called for a second time, will do
@@ -360,7 +338,6 @@ class Table extends ViewsTable {
    // Apply the row filters first, then aggregate the groups.
    $this->applyRowFilters();
    $groups = $this->aggregateGroups();
    $with_pager = FALSE;
    $values = $this->executeAggregationFunctions($groups, $functions);
    unset($groups['column']);

@@ -411,6 +388,50 @@ class Table extends ViewsTable {
      $this->fixCounterFields();
    }

    // Update the view pager if we removed it in hook_view_pre_build.
    $pager = $this->view->display_handler->getOption('pager');
    $result_count = count($this->view->result);
    if ($pager['type'] != 'none') {
      if (isset($this->view->original_pager[$display_id])) {
        $original_pager = $this->view->original_pager[$display_id]['pager'];
        $original_pager_options = $original_pager['options'];
        $items_per_page = $original_pager_options['items_per_page'];
        $offset = $original_pager_options['offset'];
        $current_page = $this->view->pager->getCurrentPage();
        $this->view->total_rows = $result_count;
        $this->view->pager->setItemsPerPage($items_per_page);
        $this->view->pager->setOffset($offset);
        $this->view->pager->total_items = $result_count - $offset;
        $this->view->setItemsPerPage($items_per_page);
        $this->view->setOffset($offset);
        $this->view->pager->updatePageInfo();

        $start_row = $offset + ($current_page * $items_per_page);
        $total_items = count($this->view->result);
        $items = $items_per_page;
        if ($start_row + $items_per_page > $total_items) {
          $items = $total_items - $start_row;
        }
        $this->view->result = array_slice($this->view->result, $start_row, $items, TRUE);
      }
    }
    else {
      // No pager, but offset is defined.
      if ($pager['options']['offset'] > 0) {
        // Remove the offset rows from the results.
        $offset = $pager['options']['offset'];
        $this->view->result = array_slice($this->view->result, $offset, $result_count, TRUE);
      }
    }
    // If we need page totals, calculate only 'column' group.
    if ($this->options['column_aggregation']['totals_per_page']) {
      $column_group = ['column' => []];
      foreach ($this->view->result as $num => $row) {
        $column_group['column'][$num] = $row;
      }
      $totals = $this->executeAggregationFunctions($column_group, $functions);
    }

    // Set the totals after eventual sorting has finished.
    if (empty($this->view->totals)) {
      // If not already set above, write the column aggregation result row on
@@ -420,11 +441,11 @@ class Table extends ViewsTable {
    }

    // If we have a pager enabled with option set to show total
    // for whole resultset insteead of page - overwrite the
    // page totals as last step.
    // for the page only - overwrite the page totals as last step.
    if (isset($totals)) {
      $this->view->totals = $this->setTotalsRow($totals);
    }

    // Aggregate the results per group and show them in a separate row,
    // without compression.
    if ($group_aggregation_results == 1) {
+26 −1
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ use Drupal\Component\Utility\Html;
use Drupal\Core\Url;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\bootstrap\Bootstrap;
use Drupal\views\ViewExecutable;

require_once __DIR__ . '/views_aggregator_functions.inc';

@@ -126,7 +127,7 @@ function template_preprocess_views_aggregator_results_table(&$vars) {
  // column aggregation function to one of the first fields in the View.
  if (!empty($vars['totals']) && !empty($totals_position[3])) {
    $totals = array_column($vars['totals'], '#markup');
    // This value will be transferred to the template in view.theme.inc
    // This value will be transferred to the template in view.theme.inc.
    $view->style_plugin->options['caption'] = implode(' ', $totals);
  }

@@ -181,3 +182,27 @@ function template_preprocess_views_aggregator_results_table(&$vars) {
  $vars['attributes_array']['id'] = Html::getUniqueId('views_aggregator_datatable');
  template_preprocess_views_view_table($vars);
}

/**
 * Implements hook_views_pre_build().
 *
 * Store away and remove pagers, so aggregation can be executed correctly.
 */
function views_aggregator_views_pre_build(ViewExecutable $view) {
  // Fire only on a display which has a table with aggregation.
  $display_style_plugin_id = $view->style_plugin->getPluginId();
  if ($display_style_plugin_id == 'views_aggregator_plugin_style_table') {
    $pager = $view->display_handler->getOption('pager');

    // Enable all rows to be shown and store the original pager.
    if ($pager['type'] != 'none' || ($pager['type'] == 'none' && $pager['options']['offset'] > 0)) {
      $view->original_pager = [
        $view->current_display => [
          'pager' => $pager,
        ],
      ];
      $view->setItemsPerPage(0);
      $view->setOffset(0);
    }
  }
}