You are here

matrix.module in Matrix field 6

Same filename and directory in other branches
  1. 8.2 matrix.module
  2. 5 matrix.module
  3. 6.2 matrix.module
  4. 7.2 matrix.module

Defines simple matrix field types.

File

matrix.module
View source
<?php

/**
 * @file
 * Defines simple matrix field types.
 */

/**
 * Implementation of hook_field_info().
 */
function matrix_field_info() {
  return array(
    'matrix' => array(
      'label' => t('Matrix Field'),
      'description' => t('Creates a grid of form fields.'),
    ),
  );
}

/**
 * Implementation of hook_field_settings().
 */
function matrix_field_settings($op, $field) {
  switch ($op) {
    case 'form':
      $form = array();
      $form['size'] = array(
        '#type' => 'textfield',
        '#size' => 5,
        '#title' => t('Size of textfields'),
        '#default_value' => !empty($field['size']) ? $field['size'] : 5,
      );
      $form['rows'] = array(
        '#type' => 'textarea',
        '#title' => t('Headers in Row'),
        '#default_value' => $currfield['extra']['questions'],
        '#description' => t('List down left side headers. One per line.') . '<br />',
        '#cols' => 60,
        '#rows' => 5,
        '#weight' => -2,
        '#required' => TRUE,
        '#default_value' => isset($field["rows"]) ? $field["rows"] : '',
      );
      $form['cols'] = array(
        '#type' => 'textarea',
        '#title' => t("Headers in Column"),
        '#default_value' => $currfield['extra']['questions'],
        '#description' => t('List down top row headers. One per line.') . '<br />',
        '#cols' => 60,
        '#rows' => 5,
        '#weight' => -2,
        '#required' => TRUE,
        '#default_value' => isset($field["cols"]) ? $field["cols"] : '',
      );
      return $form;
    case 'save':
      $values[] = 'rows';
      $values[] = 'cols';
      $values[] = 'size';
      return $values;
  }
}

/**
 * Implementation of hook_content_is_empty().
 * Since this module does not allow multiple values, this function serves no purpose.
 */
function matrix_content_is_empty($item, $field) {
  return FALSE;
}

/**
 * Implementation of hook_field().
 */
function matrix_field($op, &$node, $field, &$items, $teaser, $page) {
  $rows = trim($field['rows']);
  $cols = trim($field['cols']);
  $rows = explode("\n", $rows);
  $cols = explode("\n", $cols);
  switch ($op) {
    case 'load':
      $result = db_query("SELECT value, row, col FROM {node_field_matrix_data} WHERE vid = %d AND field_name = '%s'", $node->vid, $field['field_name']);
      $values = array();
      while ($data = db_fetch_object($result)) {
        $values['data'][$data->row][$data->col] = $data->value;
      }
      $values['rows_header'] = $rows;
      $values['cols_header'] = $cols;
      $additions = array(
        $field['field_name'] => $values,
      );
      return $additions;
    case 'update':
      db_query("DELETE FROM {node_field_matrix_data} WHERE vid = %d and field_name= '%s'", $node->vid, $field['field_name']);
    case 'update':
    case 'insert':
      $i = 0;
      foreach ($rows as $row) {
        if ($row) {
          $j = 0;
          foreach ($cols as $col) {
            if ($col) {
              db_query("INSERT INTO {node_field_matrix_data} (nid, vid, field_name, row, col, value)\n                        VALUES (%d, %d, '%s', %d, %d, '%s')", $node->nid, $node->vid, $field['field_name'], $i, $j, $items[0][$i][$j]);
              $j++;
            }
          }
          $i++;
        }
      }
      break;
  }
}

/**
 * Implementation of hook_widget_info().
 */
function matrix_widget_info() {
  return array(
    'matrix' => array(
      'label' => 'Text fields in a matrix form',
      'field types' => array(
        'matrix',
      ),
      'multiple values' => CONTENT_HANDLE_CORE,
    ),
  );
}

/**
 * Implementation of hook_widget().
 */
function matrix_widget(&$form, &$form_state, $field, $items, $delta = 0) {
  $element = array(
    '#type' => $field['widget']['type'],
    '#default_value' => $items['data'],
  );
  return $element;
}

/**
 * Implementation of hook_theme().
 */
function matrix_theme() {
  return array(
    'matrix_field_settings' => array(
      'arguments' => array(
        'element' => NULL,
      ),
    ),
    'matrix_formatter_default' => array(
      'arguments' => array(
        'element' => NULL,
      ),
    ),
    'matrix_table_form' => array(
      'arguments' => array(
        'form' => NULL,
      ),
    ),
  );
}

/**
 * Theme the matrix elements into a table
 */
function theme_matrix_table_form($form) {
  $rows = array();
  $header = $form['header']['#value'];
  foreach ($form as $row_key => $fields) {
    if (!empty($form['first_col']['#value'][$row_key])) {
      $row = array();
      $row[] = $form['first_col']['#value'][$row_key];
      foreach ($fields as $col_key => $field) {
        if ($field['#type'] == 'textfield') {
          $row[] = drupal_render($form[$row_key][$col_key]);
        }
      }
      $rows[] = $row;
    }
  }
  drupal_render($form['header']);
  drupal_render($form['first_col']);
  $output = drupal_render($form);
  $output .= '<div id="edit-' . $form['#field_name'] . '-wrapper">';
  $output .= '<label>' . $form['#title'] . ': </label>';
  $output .= theme('table', $header, $rows, array(
    'class' => 'matrix',
  ));
  $output .= '<div class="description">' . $form['#description'] . '</div>';
  $output .= '</div>';
  return $output;
}

/**
 * Implementation of hook_elements().
 */
function matrix_elements() {
  $elements['matrix'] = array(
    '#input' => TRUE,
    '#columns' => array(
      'data',
    ),
    '#process' => array(
      'matrix_process',
    ),
  );
  return $elements;
}

/**
 * Process the matrix type element before displaying the field.
 *
 * Build the form element. When creating a form using FAPI #process,
 * note that $element['#value'] is already set.
 *
 * The $fields array is in $form['#field_info'][$element['#field_name']].
 */
function matrix_process($element, $edit, $form_state, $form) {
  $field = $form['#field_info'][$element['#field_name']];
  if ($field['rows'] && $field['cols']) {
    $rows = trim($field['rows']);
    $cols = trim($field['cols']);
    $rows = explode("\n", $rows);
    $cols = explode("\n", $cols);
  }
  $processed_element = array(
    '#tree' => TRUE,
    '#weight' => $field['widget']['weight'],
    '#theme' => 'matrix_table_form',
    '#prefix' => '<div class="form-item matrix_field">',
    '#suffix' => '</div>',
    '#parents' => $element['#parents'],
    '#title' => $element['#title'],
    '#description' => $element['#description'],
    '#required' => $element['#required'],
    '#field_name' => $element['#field_name'],
    '#type_name' => $element['#type_name'],
    '#delta' => $element['#delta'],
    '#columns' => $element['#columns'],
  );
  if ($cols) {
    $header = array_merge(array(
      '',
    ), $cols);
  }
  $first_col = $rows;
  $rows = (array) $rows;
  $i = 0;
  foreach ($rows as $row) {
    if ($row) {
      $j = 0;
      foreach ($cols as $col) {
        if ($col) {
          $processed_element[$i][$j] = array(
            '#type' => 'textfield',
            '#size' => $field['size'],
            '#default_value' => isset($edit[$i][$j]) ? $edit[$i][$j] : $element['#default_value'][$i][$j],
            '#attributes' => array(
              'class' => "matrix-column-{$i} matrix-row-{$j} matrix-cell-{$i}-{$j}",
            ),
          );
          $j++;
        }
      }
      $i++;
    }
  }
  $processed_element['header'] = array(
    '#value' => $header,
  );
  $processed_element['first_col'] = array(
    '#value' => $first_col,
  );
  return $processed_element;
}

/**
 * Implementation of hook_field_formatter_info().
 */
function matrix_field_formatter_info() {
  return array(
    'default' => array(
      'label' => t('Data grid (default)'),
      'field types' => array(
        'matrix',
      ),
    ),
  );
}

/**
 * Theme function for 'default' text field formatter.
 * This gets called for each row, but we only want to render all the data at once
 * so we use a staticly cached variable to ignore subsquent calls.
 * @param $element The whole $node object, but containing specific information relating to the delta of this element.
 * @return HTML.
 */
function theme_matrix_formatter_default($element) {
  $field_info = $element['#node']->{$element}['#field_name'];
  if (!is_array($field_info['cols_header']) || !is_array($field_info['data'])) {
    return;
  }
  $header = $field_info['cols_header'];

  // Translatable headers
  foreach ($header as $i => $h) {
    $header[$i] = t($h);
  }
  array_unshift($header, '');
  static $rendered;

  //since we are rendering the whole thing in one go, we don't want to rerender for each fow
  $nid = $element['#node']->nid;
  if ($rendered[$nid][$element['#field_name']] != TRUE) {
    $rendered[$nid][$element['#field_name']] = TRUE;

    //replace blank cells with a dash
    ksort($field_info['data']);
    foreach ($field_info['data'] as $row_index => $row) {
      ksort($row);
      foreach ($row as $cell_index => $cell_value) {
        if ($cell_value == '') {
          $data[$row_index][$cell_index] = '<span class="matrix-row-' . $row_index . ' matrix-column-' . $cell_index . ' matrix-cell-' . $row_index . '-' . $cell_index . '">' . '-' . '</span>';
        }
        else {
          $data[$row_index][$cell_index] = '<span class="matrix-row-' . $row_index . ' matrix-column-' . $cell_index . ' matrix-cell-' . $row_index . '-' . $cell_index . '">' . $cell_value . '</span>';
          $show_row = $row_index;
          $has_data = TRUE;

          //this is used to hide the whole display if no data is entered what so ever.
        }
      }
      $row_label = '<b>' . array_shift($field_info['rows_header']) . '</b>';
      array_unshift($data[$row_index], $row_label);
    }

    //add blank cells if the number of rows/columns is different to the number of headers

    //this can happen when columns/rows are added to an existing content type
    $row_count = count($data[0]);
    if ($row_count < count($header)) {
      for ($i = $row_count; $i < count($header); $i++) {
        for ($j = 0; $j < count($data); $j++) {
          $data[$j][$i] = '<span class="matrix-row-' . $j . ' matrix-column-' . $i . ' matrix-cell-' . $j . '-' . $i . '">' . '-' . '</span>';
        }
      }
    }

    //strip out rows at the end of the dataset where there is no data
    foreach ($data as $row_id => $row) {
      if ($row_id <= $show_row) {
        $flushed_data[] = $row;
      }
    }
    if ($has_data) {
      return theme('table', $header, $flushed_data);
    }
    else {
      return '';
    }
  }
}

/**
 * Implmentation of hook_form_alter()
 *
 * Disable the multiple values settings.  This module won't work with them.
 */
function matrix_form_alter(&$form, $form_state, $form_id) {
  if ($form_id == 'content_field_edit_form' && $form['#field']['type'] == 'matrix') {
    $form['field']['multiple']['#type'] = 'hidden';

    //hide the multiple values form.  It does not work with this module!
  }
}

Functions

Namesort descending Description
matrix_content_is_empty Implementation of hook_content_is_empty(). Since this module does not allow multiple values, this function serves no purpose.
matrix_elements Implementation of hook_elements().
matrix_field Implementation of hook_field().
matrix_field_formatter_info Implementation of hook_field_formatter_info().
matrix_field_info Implementation of hook_field_info().
matrix_field_settings Implementation of hook_field_settings().
matrix_form_alter Implmentation of hook_form_alter()
matrix_process Process the matrix type element before displaying the field.
matrix_theme Implementation of hook_theme().
matrix_widget Implementation of hook_widget().
matrix_widget_info Implementation of hook_widget_info().
theme_matrix_formatter_default Theme function for 'default' text field formatter. This gets called for each row, but we only want to render all the data at once so we use a staticly cached variable to ignore subsquent calls.
theme_matrix_table_form Theme the matrix elements into a table