view views_calc_table.inc @ 3:5635080385bd tip

Merge patch with 1.x-dev
author Franck Deroche <franck@defr.org>
date Fri, 07 Aug 2009 15:20:12 +0200
parents cedf71edacf5 b0a976e17cc7
children
line wrap: on
line source
<?php
// $Id: views_calc_table.inc,v 1.17 2009/06/13 17:05:38 karens Exp $
/**
 * @file
 * Copied from the table style plugin.
 */

/**
 * Style plugin to render each item as a row in a table.
 *
 * @ingroup views_style_plugins
 */
class views_calc_table extends views_plugin_style_table {
 
  function option_definition() {
    $options = parent::option_definition();

    $options['detailed_values'] = array('default' => 0);
    return $options;
  }
   
  /**
   * Render the given style.
   */
  function options_form(&$form, &$form_state) {
    parent::options_form($form, $form_state);
    $form['#theme'] = 'views_calc_ui_table';

    $form['detailed_values'] = array(
      '#title' => t('Show details'),
      '#type' => 'select',
      '#options' => array(0 => t('Yes'), 1 => t('No')),
      '#default_value' => $this->options['detailed_values'],
      '#description' => t("Select 'Yes' to show detailed values followed by column calculations, 'No' to surpress details and show only calculated column totals."),
    );
    
    $handlers = $this->display->handler->get_handlers('field');
    $columns = $this->sanitize_columns($this->options['columns']);
        
    foreach ($columns as $field => $column) {
      $safe = str_replace(array('][', '_', ' '), '-', $field);
      $id = 'edit-style-options-columns-' . $safe;
      $form['info'][$field]['justification'] = array(
        '#type' => 'select',
        '#default_value' => isset($this->options['info'][$field]['justification']) ? $this->options['info'][$field]['justification'] : 'views_calc_justify_none',
        '#options' => array(
          'views_calc_justify_none' => t('None'), 
          'views_calc_justify_left' => t('Left'), 
          'views_calc_justify_right' => t('Right'), 
          'views_calc_justify_center' => t('Center'),
          ),
        '#process' => array('views_process_dependency'),
        '#dependency' => array($id => array($field)),
      );
      $form['info'][$field]['has_calc'] = array(
        '#type' => 'checkbox',
        '#title' => t('Display calculation'),
        '#default_value' => isset($this->options['info'][$field]['has_calc']) ? $this->options['info'][$field]['has_calc'] : 0,
        '#process' => array('views_process_dependency'),
        '#dependency' => array($id => array($field)),
      );
      
      $options = _views_calc_calc_options();
      $form['info'][$field]['calc'] = array(
        '#type' => 'select',
        '#options' => $options,
        '#default_value' => isset($this->options['info'][$field]['calc']) ? $this->options['info'][$field]['calc'] : array(),
        '#process' => array('views_process_dependency'),
        '#dependency' => array('edit-style-options-info-'. $safe .'-has-calc' => array(TRUE)),
        '#multiple' => TRUE,
      );
    }
  }

  /**
   * TODO
   * figure out what changes are needed so Views field groups will work.
   */
  function pre_render($results) {
    parent::pre_render($results);
    
    // If there are no calc fields, do nothing.
    if (!$calc_fields = $this->get_calc_fields()) {
      return;
    }
    // If we're not getting a summary row, do nothing.
    if (!empty($this->view->views_calc_calculation)) {
      return;
    }
    $this->view->totals = array();
    $this->view->sub_totals = array();
      
    // Subtotals and pager totals require a list of the specific
    // values to include.
    $paged = FALSE;
    if (!empty($this->view->pager) 
    && !empty($this->view->pager['use_pager']) 
    && !empty($this->view->pager['items_per_page'])) {
      $nids = array();
      foreach ($this->view->result as $delta => $value) {
        $nids[] = $value->nid;
      }
      // Add sub_total rows to the results.
      foreach ($calc_fields as $calc => $field) {
        $this->view->sub_totals[] = $this->do_calculation($calc, TRUE, $nids);
      }
    }
    
    // Add grand totals to the results.
    foreach ($calc_fields as $calc => $field) {
      $this->view->totals[] = $this->do_calculation($calc, FALSE);
    }
  }

  function do_calculation($calc, $sub_total, $nids = array()) {
    // TODO Looks like we have problems unless we
    // force a non-page display, need to keep an eye on this.
    if ($summary_view = views_get_view($this->view->name)) {
      $summary_view->set_display($this->view->current_display);
      $summary_view->set_arguments($this->view->args);
      $summary_view->pager['items_per_page'] = 0;
      $summary_view->views_calc_calculation = $calc;
      $summary_view->views_calc_nids = $nids;
      $summary_view->views_calc_sub_total = $sub_total;
      $summary_view->is_cacheable = FALSE;
      $summary_view->preview();
      return array_shift($summary_view->result);
    }
    return '';
  }

  function query() {
    parent::query();
    
    // If we're not getting a summary row, do nothing.
    if (empty($this->view->views_calc_calculation)) {
      return;
    }
    // If there are no calc fields, do nothing.
    if (!$calc_fields = $this->get_calc_fields()) {
      return;
    }

    if (!empty($this->view->views_calc_sub_total)) {
      $this->query_sub_total();
    }
    else {
      $this->query_total();
    }
  }
  
  /**
   * 
   */
  function query_sub_total() {
    // Create summary rows.
    $calc_fields = $this->get_calc_fields();
    $calc = $this->view->views_calc_calculation;
    $fields = $calc_fields[$calc];
    
    // Empty out any fields that have been added to the query,
    // we don't need them for the summary totals.
    $this->view->query->fields = array();
    // Clear out any sorting and grouping, it can create unexpected results
    // when Views adds aggregation values for the sorts.
    $this->view->query->orderby = array();
    $this->view->query->groupby = array();

    foreach ($this->view->field as $field) {
      $query_field = substr($field->field, 0, 3) == 'cid' ? $field->definition['calc'] : $field->table .'.'. $field->field;
      $query_alias = $field->field_alias;
      // Bail if we have a broken handler.
      if ($query_alias == 'unknown') {
        continue;
      }
      if (in_array($field->field, $fields)) {
        // Calculated fields.
        $this->view->query->add_field(NULL, "$calc($query_field)", $query_alias);
        $this->view->query->add_table($field->table, NULL, NULL, $field->table);
      }
      else {
        // Empty fields that have no calculations.
        $this->view->query->add_field(NULL, "MAX('')", $query_alias);
      }
      // Add a dummy field for the groupby.
      $this->view->query->add_field(NULL, "MAX('". $calc ."')", "TOTAL_". $calc);
    }
    // TODO This won't work right with relationships, need a fix here.
    if (!empty($this->view->views_calc_nids)) {
      $this->view->query->add_where(NULL, "node.nid IN (%s)", implode(',', $this->view->views_calc_nids));
    }
  }
  
  /**
   * The grand total can be computed using GROUPBY without regard
   * to pager values.
   */
  function query_total() {
    // Create summary rows.
    $calc_fields = $this->get_calc_fields();
    $calc = $this->view->views_calc_calculation;
    $fields = $calc_fields[$calc];
    
    // Empty out any fields that have been added to the query,
    // we don't need them for the summary totals.
    $this->view->query->fields = array();
    // Clear out any sorting and grouping, it can create unexpected results
    // when Views adds aggregation values for the sorts.
    $this->view->query->orderby = array();
    $this->view->query->groupby = array();

    foreach ($this->view->field as $field) {
      $query_field = substr($field->field, 0, 3) == 'cid' ? $field->definition['calc'] : $field->table .'.'. $field->field;
      $query_alias = $field->field_alias;
      // Bail if we have a broken handler.
      if ($query_alias == 'unknown') {
        continue;
      }
      $this->view->query->add_table($field->table, NULL, NULL, $field->table);
      if (!empty($fields) && in_array($field->field, $fields)) {
        // Calculated fields.
        $this->view->query->add_field(NULL, "$calc($query_field)", $query_alias);
      }
      else {
        // Empty fields that have no calculations.
        $this->view->query->add_field(NULL, "MAX('')", $query_alias);
      }
      // Add a dummy field for the groupby.
      $this->view->query->add_field(NULL, "MAX('". $calc ."')", "TOTAL_". $calc);
    }
  }

  function get_calc_fields() {
    $options  = $this->view->style_plugin->options;
    $handler  = $this->view->style_plugin;
    $fields   = $this->view->field;
    $columns  = $handler->sanitize_columns($options['columns'], $fields);
    $calcs = array_keys(_views_calc_calc_options());
    
    $calc_fields = array();
    foreach ($columns as $field => $column) {
      if ($field == $column && empty($fields[$field]->options['exclude'])) {
        if ($options['info'][$field]['has_calc']) {
          foreach ($calcs as $calc) {
            if (isset($this->options['info'][$field]['calc'][$calc])) {
              $calc_fields[$calc][] = $field;
            }
          }
        }
      }
    }
    return $calc_fields;
  }

  function render_grouping($records, $grouping_field = '') {
    $sets = parent::render_grouping($records, $grouping_field);
    // If we have more than one set, we'll try recalculate results
    // baded on the nodes in the group.
    $calc_fields = $this->get_calc_fields();
    if (count($sets) > 1 && $calc_fields) {
      $this->group_totals = array();
      foreach($sets as $value => $set) {
        $nids = array();
        foreach($set as $k => $v) {
          $nids[] = $v->nid;
        }
        foreach($calc_fields as $calc => $field) {
          $this->view->group_totals[$value][] = $this->do_calculation($calc, TRUE, $nids);
        }
      }
    }
    return $sets;
  }
}