You are here

class FrxCrosstab in Forena Reports 7.4

Hierarchy

Expanded class hierarchy of FrxCrosstab

3 string references to 'FrxCrosstab'
forena_forena_controls in ./forena.module
Self register controls with forena.
FrxCrosstab::generate in renderers/FrxCrosstab.inc
Generate the template from the configuration.
FrxSVGGraph::generateCrossTab in renderers/FrxSVGGraph.inc
Generate a crosstab table.

File

renderers/FrxCrosstab.inc, line 2

View source
class FrxCrosstab extends FrxRenderer {
  public $templateName = 'Crosstab';
  private $headers = array();
  private $dim_columns = array();
  private $group_columns = array();
  private $dim_headers = array();
  private $group_headers = array();
  private $weight;

  /**
   * Render the crosstab
   */
  public function render() {
    $variables = $this
      ->mergedAttributes();
    $attributes = $this
      ->replacedAttributes();
    if (!empty($variables['hidden']) && $this->format != 'csv' && $this->format != 'xls') {
      return '';
    }
    $path = isset($variables['path']) ? $variables['path'] : '*';
    if (!$path) {
      $path = "*";
    }
    $group = $variables['group'];
    $dim = $variables['dim'];
    $sum = (array) @$variables['sum'];

    // Get the current context
    $data = Frx::Data()
      ->currentContext();

    // Generate the data nodes.
    if (is_object($data)) {
      if (method_exists($data, 'xpath')) {
        $nodes = $data
          ->xpath($path);
      }
      else {
        $nodes = $data;
      }
    }
    else {
      $nodes = (array) $data;
    }

    // Group the data.
    $data = $this->frxReport
      ->group($nodes, $group, $sum);
    $this->dim_headers = array();
    $this->dim_rows = array();
    $this->dim_columns = array();
    $this->group_columns = array();
    $this->group_headers = array();
    $dim_values = array();
    $rows = array();
    foreach ($data as $gk => $group_rows) {
      $row_copy = array_values($group_rows);
      $dims = $this->frxReport
        ->group($group_rows, $dim);
      $rows[$gk] = $group_rows[0];
      foreach ($dims as $dk => $r) {
        $dims = array_values($r);
        $dim_values[$dk] = $dk;
        $dim_rows[$gk][$dk] = $r[0];
      }
    }

    // Default controling attributes
    $this
      ->defaultHeaders($dim_values);
    $hrow = array();
    foreach ($this->group_headers as $col) {
      $cell = $col;
      if (count($this->dim_columns) > 1) {
        $cell['rowspan'] = 2;
      }
      $hrow[] = $cell;
    }

    // Add the dimension headers.
    foreach ($dim_values as $dk) {
      foreach ($this->dim_headers as $i => $col) {
        $cell = $col;
        $cell['data'] = $dk;
        if (count($this->dim_columns) > 1) {
          $cell['data'] = $i ? $col['data'] : $dk . ' ' . $col['data'];
        }
        $hrow[] = $cell;
      }
    }
    $trows = array();
    foreach ($rows as $k => $row) {
      Frx::Data()
        ->push($row, '_group');
      $trow = array();

      // Base group
      foreach ($this->group_columns as $col) {
        $cell = $col;
        foreach ($col as $key => $v) {
          $cell[$key] = $this->teng
            ->replace($v);
        }
        $trow[] = $cell;
      }
      Frx::Data()
        ->pop();

      // Dimensions
      $dim_data = $dim_rows[$k];
      foreach ($dim_values as $dk) {
        $dim_row = isset($dim_data[$dk]) ? $dim_data[$dk] : array();
        frx::Data()
          ->push($dim_row, '_dim');
        foreach ($this->dim_columns as $col) {
          $cell = $col;
          foreach ($col as $k => $v) {
            $cell[$k] = $this->teng
              ->replace($v);
          }
          $trow[] = $cell;
        }
        frx::Data()
          ->pop();
      }
      $trows[] = $trow;
    }
    $class = 'crosstab-table';
    if (isset($attributes['class'])) {
      $class .= ' ' . $attributes['class'];
    }
    $vars = array(
      'header' => $hrow,
      'rows' => $trows,
      'attributes' => array(
        'class' => array(
          $class,
        ),
      ),
    );
    $output = theme('table', $vars);
    return $output;
  }

  /**
   * Generate default headers from Embedded xml.
   */
  private function defaultHeaders() {
    $node = $this->reportDocNode;
    if ($node->thead && $node->thead->tr) {
      foreach ($node->thead->tr
        ->children() as $name => $cell) {
        $hcol = array();
        $hcol['data'] = forena_inner_xml($cell, $name);
        $hcol['depth'] = 1;
        foreach ($cell
          ->attributes() as $k => $v) {
          $hcol[$k] = (string) $v;
        }
        if ($name == 'th') {
          $this->group_headers[] = $hcol;
        }
        else {
          $this->dim_headers[] = $hcol;
        }
      }
    }
    if ($node->tbody && $node->tbody->tr) {
      foreach ($node->tbody->tr
        ->children() as $name => $cell) {
        $col = array();
        $col['data'] = forena_inner_xml($cell, $name);
        foreach ($cell
          ->attributes() as $k => $v) {
          $col[$k] = (string) $v;
        }
        if ($name == 'th') {
          $this->group_columns[] = $col;
        }
        else {
          $this->dim_columns[] = $col;
        }
      }
    }
  }

  /**
   * Crosstab configuration form.
   */
  public function configForm($config) {

    // Load header informationi from parent config.
    $form = parent::configForm($config);
    $this
      ->weight_sort($config['crosstab_columns']);
    $types = array(
      'heading' => t('Heading'),
      'crosstab' => t('Crosstab'),
      'value' => 'Value',
      'ignore' => t('Ignore'),
    );
    $form['crosstab_columns'] = array(
      '#theme' => 'forena_element_draggable',
      '#draggable_id' => 'FrxCrosstab-columns',
    );
    foreach ($config['crosstab_columns'] as $key => $col) {
      $ctl = array();
      $ctl['label'] = array(
        '#type' => 'textfield',
        '#size' => 30,
        '#title' => t('Label'),
        '#default_value' => $col['label'],
      );
      $ctl['contents'] = array(
        '#type' => 'textfield',
        '#size' => '30',
        '#title' => t('Data'),
        '#default_value' => $col['contents'],
      );
      $ctl['type'] = array(
        '#type' => 'radios',
        '#title' => t('Type'),
        '#default_value' => $col['type'],
        '#options' => $types,
        '#ajax' => $this
          ->configAjax(),
      );
      $ctl['weight'] = array(
        "#type" => 'weight',
        '#title' => t('Weight'),
        '#delta' => 50,
        '#default_value' => $col['weight'],
      );
      $form['crosstab_columns'][$key] = $ctl;
    }
    return $form;
  }
  public function generate($xml, &$config) {
    $config['class'] = get_class($this);
    $block = @$config['block'];
    $id = @$config['id'];
    if ($block) {
      $id = $this
        ->idFromBlock($block);
      $config['id'] = $id . '_block';
    }
    $config['class'] = @$config['class'] ? $config['class'] . ' FrxCrosstab' : 'FrxCrosstab';
    $div = $this
      ->blockDiv($config);

    // PUt on the header
    $this
      ->removeChildren($div);
    if (isset($config['header']['value'])) {
      $this
        ->addFragment($div, $config['header']['value']);
    }

    // Decide to inlcude columns
    $found_columns = $this
      ->columns($xml);
    if (!$found_columns) {
      $found_columns = $this
        ->columns($xml, '/*');
      $attrs = array();
    }
    $numeric_columns = $this->numeric_columns;
    $new_columns = @$config['crosstab_columns'] ? FALSE : TRUE;
    foreach ($found_columns as $column => $label) {
      $token = '{' . $column . '}';
      if ($new_columns) {
        $type = isset($numeric_columns[$column]) ? 'value' : 'heading';
      }
      else {
        $type = 'ignore';
      }
      if (!isset($config['crosstab_columns'][$column])) {
        $this
          ->addColumn($type, '{' . $column . '}', $column, $config);
      }
    }

    // Generate the grouping row
    $group = '';
    $dim = array();
    foreach ($config['crosstab_columns'] as $col) {
      if ($col['type'] == 'heading') {
        $group .= $col['contents'];
      }
      if ($col['type'] == 'crosstab') {
        $dim = $col['contents'];
      }
    }
    $r_id = $id . '-renderer';
    $table_frx['renderer'] = 'FrxCrosstab';
    $table_frx['group'] = $group;
    $table_frx['dim'] = $dim;
    $attrs[$id] = $r_id;

    //$attrs = array('foreach' => '*');
    $table = $this
      ->setFirstNode($div, 4, 'table', NULL, $attrs, $table_frx);
    $thead = $this
      ->setFirstNode($table, 6, 'thead');
    $throw = $this
      ->setFirstNode($thead, 8, 'tr');
    $tbody = $this
      ->setFirstNode($table, 6, 'tbody');
    $tdrow = $this
      ->setFirstNode($tbody, 8, 'tr', NULL, array(
      'id' => $id,
    ), $attrs);
    if ($config['crosstab_columns']) {
      foreach ($config['crosstab_columns'] as $key => $col) {
        if ($col['type'] !== 'ignore') {
          if ($col['type'] == 'heading') {
            $tag = 'th';
          }
          else {
            $tag = 'td';
          }
          if ($col['type'] != 'crosstab') {
            $this
              ->addNode($throw, 10, $tag, $col['label']);
            $this
              ->addNode($tdrow, 10, $tag, $col['contents']);
          }
        }
      }
    }
    if (isset($config['footer']['value'])) {
      $this
        ->addFragment($div, $config['footer']['value']);
    }
  }

  /**
   * Default configuration validator. Simply validates header and footer attributes.
   * @param unknown $config
   * @return multitype:Ambigous <The, string, A, Optional>
   */
  public function configValidate(&$config) {
    $errors = $this
      ->validateTextFormats($config, array(
      'header',
      'footer',
    ));
    $dims = 0;
    if (@$config['crosstab_columns']) {
      foreach ($config['crosstab_columns'] as $col) {
        if (@$col['type'] == 'value') {
          $dims++;
        }
      }
    }
    if ($dims > 1) {
      $errors[] = t('Too many value columns.  Please select only one');
    }
    return $errors;
  }
  private function addColumn($type, $token, $label, &$config) {
    $key = trim($token, '{}');
    $this->weight++;
    $config['crosstab_columns'][$key] = array(
      'contents' => $token,
      'label' => $label,
      'type' => $type,
      'weight' => $this->weight,
    );
  }

  /**
   * Extract table configuration from the HTML
   * @see FrxRenderer::scrapeConfig()
   */
  public function scrapeConfig() {
    $this->weight = 0;
    $config = array();
    $nodes = $this->reportDocNode
      ->xpath('//table');
    if ($nodes) {
      $table = $nodes[0];
      $attrs = $this
        ->mergedAttributes($table);
    }
    $config['group'] = $group = $attrs['group'];
    $config['dim'] = $dim = $attrs['dim'];
    $this
      ->extractTemplateHTML($this->reportDocDomNode, $config, array(
      'table',
    ));
    $head_ths = $this
      ->extractXPathInnerHTML('*//thead/tr/th', $this->reportDocDomNode, FALSE);
    $head_tds = $this
      ->extractXPathInnerHTML('*//thead/tr/td', $this->reportDocDomNode, FALSE);
    $body_ths = $this
      ->extractXPathInnerHTML('*//tbody/tr/th', $this->reportDocDomNode, FALSE);
    $body_tds = $this
      ->extractXPathInnerHTML('*//tbody/tr/td', $this->reportDocDomNode, FALSE);
    $heading_cols = array_combine($head_ths, $body_ths);
    $data_cols = array_combine($head_tds, $body_tds);

    // Get the named headers
    foreach ($heading_cols as $label => $token) {
      $this
        ->addColumn('heading', $token, $label, $config);
    }

    // Get the data cells
    if ($dim) {
      $dims = (array) $dim;
      foreach ($dims as $dim) {
        $this
          ->addColumn('crosstab', $dim, trim($dim, '{}'), $config);
      }
    }
    foreach ($data_cols as $label => $token) {
      $this
        ->addColumn('value', $token, $label, $config);
    }
    return $config;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
FrxCrosstab::$dim_columns private property
FrxCrosstab::$dim_headers private property
FrxCrosstab::$group_columns private property
FrxCrosstab::$group_headers private property
FrxCrosstab::$headers private property
FrxCrosstab::$templateName public property
FrxCrosstab::$weight private property
FrxCrosstab::addColumn private function
FrxCrosstab::configForm public function Crosstab configuration form. Overrides FrxRenderer::configForm
FrxCrosstab::configValidate public function Default configuration validator. Simply validates header and footer attributes. Overrides FrxRenderer::configValidate
FrxCrosstab::defaultHeaders private function Generate default headers from Embedded xml.
FrxCrosstab::generate public function Generate the template from the configuration. Overrides FrxRenderer::generate
FrxCrosstab::render public function Render the crosstab Overrides FrxRenderer::render
FrxCrosstab::scrapeConfig public function Extract table configuration from the HTML Overrides FrxRenderer::scrapeConfig
FrxRenderer::$blockName public property
FrxRenderer::$blockParms public property
FrxRenderer::$columns public property
FrxRenderer::$dataProvider public property
FrxRenderer::$doc_types public property
FrxRenderer::$dom public property
FrxRenderer::$format public property
FrxRenderer::$frxAttributes public property
FrxRenderer::$frxReport public property @var FrxReport
FrxRenderer::$htmlAttributes public property
FrxRenderer::$id public property
FrxRenderer::$input_format public property
FrxRenderer::$name public property
FrxRenderer::$numeric_columns public property
FrxRenderer::$reportDocDomNode public property
FrxRenderer::$reportDocNode public property
FrxRenderer::$teng public property
FrxRenderer::$xmlns public property
FrxRenderer::$xpathQuery public property
FrxRenderer::addAttributes public static function Helper function for convergint methods to a standard associated array.
FrxRenderer::addFragment function Append a textual XHTML fragment to the dom. We do not use the DOMDocumentFragment optioin because they don't properly import namespaces. .
FrxRenderer::addNode function Add a node to the existing dom element with attributes
FrxRenderer::addText function Add a text node to the current dom node.
FrxRenderer::arrayAttributes public function Puts attributes back in array format prior to rendering.
FrxRenderer::blockDiv public function Generate generic div tag.
FrxRenderer::columns public function Extract a list of columns from the data context.
FrxRenderer::configAjax public function Generate ajax configuration attributes for use in template configurtion forms.
FrxRenderer::drupalRender public function Render a drupal form in a forena template
FrxRenderer::extract public function Extract a configuration var removing it from the array
FrxRenderer::extractChildSource public function Get the textual representations of html for the configuration engine.
FrxRenderer::extractSource public function Get the textual representations of html for the configuration engine.
FrxRenderer::extractTemplateHTML public function Get the textual representations of html for the configuration engine.
FrxRenderer::extractXPath public function Extracts the inner html of all nodes that match a particular xpath expression.
FrxRenderer::extractXPathInnerHTML public function Extracts the inner html of all nodes that match a particular xpath expression.
FrxRenderer::idFromBlock public function Simple function to get id from node.
FrxRenderer::initReportNode public function This function is called to give the renderer the current conetxt in report rendering. It makes sure the renderer has the current DOM nodes dom documnent, and other attributes.
FrxRenderer::innerXML public function Return the inside xml of the current node
FrxRenderer::mergedAttributes public function Standard php array containing merged attributes Enter description here ...
FrxRenderer::removeChildren public function Rmove all the children of a dom node in the current report.
FrxRenderer::removeChildrenExcept public function Removes all chidren from the dome node expect those with a tagname specified by the the $tags argurment
FrxRenderer::renderChildren public function
FrxRenderer::renderDomNode public function Recursive report renderer Walks the nodes rendering the report.
FrxRenderer::replacedAttributes public function Gives the token replaced attributes of a node.
FrxRenderer::replaceTokens public function A helper function to allow replacement of tokens from inside a renderer wihout needing to understand the object
FrxRenderer::resetTemplate public function Starting at the current report node, this function removes all child nodes. It aso removes any FRX attributes on the current as well.
FrxRenderer::setAttributes public function Set FRX attributes.
FrxRenderer::setFirstNode public function Sets the first child element to a node and returns it. IF the node
FrxRenderer::validateTextFormats public function Helper funciton for validating text_format type controls.
FrxRenderer::weight_sort public function Sort a column list by weight.
FrxRenderer::weight_sort_comp public static function
FrxRenderer::xmlToValues public function Convert XML to key value pairs. This is used in support of graping to get specific key/value pairs in an array format suitable for passing off to php libraries.
FrxRenderer::__construct public function 2