You are here

nodereference_count.module in Nodereference Count 6

Same filename and directory in other branches
  1. 7 nodereference_count.module

Defines a field type for counting the references to a node.

File

nodereference_count.module
View source
<?php

/**
 * @file
 * Defines a field type for counting the references to a node.
 */

/**
 * Implementation of hook_field_info().
 */
function nodereference_count_field_info() {
  return array(
    'nodereference_count' => array(
      'label' => t('Nodereference count'),
      'description' => t('Store nodereference count data in the database.'),
    ),
  );
}

/**
 * Generate list of available nodereference fields to count.
 *
 * @param $field
 *   the field array passed from hook_field_settings().
 * @return
 *   an array of nodereference fields that are available to count.
 */
function nodereference_count_field_options($field) {
  $field_types = content_fields();
  $field_options = array();
  foreach ($field_types as $field_type) {
    if ($field_type['type'] == 'nodereference' && $field_type['referenceable_types'][$field['type_name']] === $field['type_name']) {
      $field_options[$field_type['field_name']] = $field_type['field_name'];
    }
  }
  return $field_options;
}

/**
 * Implementation of hook_field_settings().
 */
function nodereference_count_field_settings($op, $field) {
  switch ($op) {
    case 'form':
      $options = nodereference_count_field_options($field);
      $form = array();
      $form['referenceable_fields'] = array(
        '#type' => 'checkboxes',
        '#title' => t('Nodereference fields that can be counted'),
        '#description' => t('The nodereference fields that reference this content type.'),
        '#multiple' => TRUE,
        '#default_value' => is_array($field['referenceable_fields']) ? $field['referenceable_fields'] : array(),
        '#options' => array_map('check_plain', $options),
      );
      if (empty($options)) {
        $form['referenceable_fields_empty'] = array(
          '#prefix' => '<p>',
          '#value' => t('There are no nodereference fields to count. Add a node reference field to another content type, allowing it to reference this content type, if you wish to count the number of references to a node of this type.'),
          '#suffix' => '</p>',
        );
      }
      else {
        $form['referenceable_fields_count_by_status'] = array(
          '#type' => 'radios',
          '#title' => t('Count by node status'),
          '#multiple' => FALSE,
          '#default_value' => !empty($field['referenceable_fields_count_by_status']) ? $field['referenceable_fields_count_by_status'] : '',
          '#options' => array(
            'all' => t('Count all nodes'),
            'published' => t('Count published nodes'),
          ),
        );
      }
      return $form;
    case 'save':
      return array(
        'referenceable_fields',
        'referenceable_fields_count_by_status',
      );
    case 'database columns':
      return array(
        'value' => array(
          'type' => 'int',
          'not null' => FALSE,
          'sortable' => TRUE,
          'views' => TRUE,
        ),
      );
  }
}

/**
 * Implementation of cck hook_field().
 */
function nodereference_count_field($op, &$node, $field, &$node_field, $teaser, $page) {
  switch ($op) {
    case 'insert':
    case 'update':
      $node_field[0]['value'] = nodereference_count_get_count($field, $node->nid);
      break;
  }
}

/**
 * Get the count of nodereferences to a node.
 *
 * @param $field
 *   the field array for this field instance
 * @param $nid
 *   the nid of the node being referenced
 * @return
 *   a count of the number of references to the node
 */
function nodereference_count_get_count($field, $nid) {

  // We want to allow aggregate counts from multiple fields referencing this node
  // so we get the list of fields to count from the field array and then loop through
  // creating a sql statement for each field.
  $referencing_fields = $field['referenceable_fields'];
  $queries = array();
  $count_by_status = $field['referenceable_fields_count_by_status'];
  foreach ($referencing_fields as $referencing_field) {
    if ($referencing_field) {
      $db_info = content_database_info(content_fields($referencing_field));
      $table = $db_info['table'];
      $column = $db_info['columns']['nid']['column'];

      // Not sure a placeholder would work for the nid in this context.
      // It should be safe regardless as it is getting the value from the node object in CCK or hook_nodeapi().
      if ($count_by_status == 'published') {
        $queries[] = "SELECT COUNT(DISTINCT content.vid) FROM {" . $table . "} AS content, {node} AS node WHERE " . $column . " = " . $nid . " AND node.vid = content.vid AND node.status = 1";
      }
      else {
        $queries[] = "SELECT COUNT(DISTINCT nid) FROM {" . $table . "} WHERE " . $column . " = " . $nid;
      }
    }
  }

  // There is probably a better way to do this.  For now we create a set of subqueries
  // getting a count for each referencing field and adding those together.
  // For the most common case of a single referencing field being counted we pass the query directly.
  $query_count = count($queries);
  if ($query_count == 1) {
    $sql = $queries[0];
  }
  else {
    $sql = "SELECT ";
    for ($i = 0; $i < $query_count; $i++) {
      $sql .= $i > 0 ? " + " : "";
      $sql .= "(" . $queries[$i] . ")";
    }
    $sql .= " AS nr_count";
  }
  return db_result(db_query($sql));
}

/**
 * Update the count of nodereferences to a node when not updating via CCK.
 *
 * @param $field
 *   the field array for this field instance
 * @param $nid
 *   the nid of the node being referenced
 * @param $vid
 *   the vid of the node being referenced
 */
function nodereference_count_update_count($field, $nid, $vid = NULL) {
  $vid = nodereference_count_get_vid($nid, $vid);
  if (empty($vid)) {
    return FALSE;
  }
  $count = nodereference_count_get_count($field, $nid);
  $db_info = content_database_info($field);
  $table = $db_info['table'];
  $column = $db_info['columns']['value']['column'];
  $update = new stdClass();
  $update->vid = $vid;
  $update->nid = $nid;
  $update->{$column} = $count;
  drupal_write_record($table, $update, 'vid');
  content_clear_type_cache();
}

/**
 * Retrieve the current vid of a node.
 *
 * @param $nid
 *   the nid of the node being referenced
 * @param $vid
 *   the vid of the node being referenced
 */
function nodereference_count_get_vid($nid, $vid = NULL) {
  static $vids = array();
  if (empty($vid)) {
    if (empty($vids[$nid])) {
      $query = 'SELECT vid FROM {node} WHERE nid = %d';
      $vids[$nid] = db_result(db_query($query, $nid));
    }
  }
  else {
    $vids[$nid] = $vid;
  }
  return $vids[$nid];
}

/**
 * Implementation of hook_nodeapi().
 *
 * Update the nodereference count of any referenced nodes
 * on insert, update, or delete.
 */
function nodereference_count_nodeapi(&$node, $op) {
  switch ($op) {
    case 'presave':

      // Load the original node so we can update the count on previously referenced nodes.
      $node->original = node_load($node->nid);
      break;
    case 'insert':
    case 'update':
    case 'delete':

      // Get a list of fields for this content type and filter down to only the nodereference fields.
      $content_type = content_types($node->type);
      $nodereference_fields = array();
      foreach ($content_type['fields'] as $field) {
        if ($field['type'] == 'nodereference') {
          $nodereference_fields[$field['field_name']] = $field;
        }
      }

      // If there are no nodereferences for this content type then there is nothing to count.
      if (empty($nodereference_fields)) {
        return;
      }

      // Get a list of any nodereference count fields that are counting the nodereference fields.
      $all_fields = content_fields();
      $nodereference_count_fields = array();
      foreach ($all_fields as $count_field) {
        if ($count_field['type'] == 'nodereference_count') {
          foreach ($nodereference_fields as $nodereference_field) {
            $counted_field = $count_field['referenceable_fields'][$nodereference_field['field_name']];
            if (isset($counted_field) && $counted_field === $nodereference_field['field_name']) {
              $nodereference_count_fields[] = array(
                'nodereference' => $nodereference_field['field_name'],
                'count' => $count_field,
              );
            }
          }
        }
      }

      // If there are no nodereferences being counted from this content type then there is nothing to count.
      if (empty($nodereference_count_fields)) {
        return;
      }

      // Loop through the array of counted fields and update the counts.
      foreach ($nodereference_count_fields as $nodereference_count_field) {
        $nodereferences = $node->{$nodereference_count_field}['nodereference'];
        $original_nodereferences = $node->original->{$nodereference_count_field}['nodereference'];
        foreach ($nodereferences as $index => $nodereference) {

          // If the reference has changed, update the count on the previous node.
          if (!empty($original_nodereferences[$index]['nid']) && $nodereference['nid'] != $original_nodereferences[$index]['nid']) {
            nodereference_count_update_count($nodereference_count_field['count'], $original_nodereferences[$index]['nid']);
          }

          // If there is no value in the nodereference field there is nothing to count.
          if (!empty($nodereference['nid'])) {
            nodereference_count_update_count($nodereference_count_field['count'], $nodereference['nid']);
          }
        }
      }
      break;
  }
}

/**
 * Implementation of hook_content_is_empty().
 */
function nodereference_count_content_is_empty($item, $field) {
  return empty($item['value']);
}

/**
 * Implementation of hook_token_list().
 */
function nodereference_count_token_list($type = 'all') {
  if ($type == 'field' || $type == 'all') {
    $tokens = array();
    $tokens['nodereference_count']['count'] = t('Number of node references.');
    return $tokens;
  }
}

/**
 * Implementation of hook_token_values().
 */
function nodereference_count_token_values($type, $object = NULL) {
  if ($type == 'field') {
    $item = $object[0];
    $tokens['count'] = $item['value'];
    return $tokens;
  }
}

/**
 * Implementation of hook_theme().
 */
function nodereference_count_theme() {
  return array(
    'nodereference_count_formatter_default' => array(
      'arguments' => array(
        'element' => NULL,
      ),
    ),
    'nodereference_count_formatter_nonzero' => array(
      'arguments' => array(
        'element' => NULL,
      ),
    ),
  );
}

/**
 * Implementation of hook_field_formatter_info().
 */
function nodereference_count_field_formatter_info() {
  return array(
    'default' => array(
      'label' => t('Default'),
      'field types' => array(
        'nodereference_count',
      ),
      'multiple values' => CONTENT_HANDLE_CORE,
    ),
    'nonzero' => array(
      'label' => t('Non Zero'),
      'field types' => array(
        'nodereference_count',
      ),
      'multiple values' => CONTENT_HANDLE_CORE,
    ),
  );
}

/**
 * Theme function for 'default' field formatter.
 */
function theme_nodereference_count_formatter_default($element) {
  return $element['#item']['value'];
}

/**
 * Theme function for 'nonzero' field formatter.
 */
function theme_nodereference_count_formatter_nonzero($element) {
  $count = $element['#item']['value'];
  if ($count == 0) {
    return;
  }
  return $count;
}

/**
 * Implementation of hook_widget_info().
 */
function nodereference_count_widget_info() {
  return array(
    'nodereference_count_widget' => array(
      'label' => t('default'),
      'field types' => array(
        'nodereference_count',
      ),
    ),
  );
}

Functions

Namesort descending Description
nodereference_count_content_is_empty Implementation of hook_content_is_empty().
nodereference_count_field Implementation of cck hook_field().
nodereference_count_field_formatter_info Implementation of hook_field_formatter_info().
nodereference_count_field_info Implementation of hook_field_info().
nodereference_count_field_options Generate list of available nodereference fields to count.
nodereference_count_field_settings Implementation of hook_field_settings().
nodereference_count_get_count Get the count of nodereferences to a node.
nodereference_count_get_vid Retrieve the current vid of a node.
nodereference_count_nodeapi Implementation of hook_nodeapi().
nodereference_count_theme Implementation of hook_theme().
nodereference_count_token_list Implementation of hook_token_list().
nodereference_count_token_values Implementation of hook_token_values().
nodereference_count_update_count Update the count of nodereferences to a node when not updating via CCK.
nodereference_count_widget_info Implementation of hook_widget_info().
theme_nodereference_count_formatter_default Theme function for 'default' field formatter.
theme_nodereference_count_formatter_nonzero Theme function for 'nonzero' field formatter.