I've written the following module for my site which automatically assigns taxonomy terms to a node based on the values of specific CCK fields.
For prices, it simply assigns an existing term depending on which price range the price falls. For locations, it either assigns an existing term, or creates and assigns a new term (if one doesn't already exist).

It's all working fine, except that it seems to remove existing non-price-or-location terms from a node. For example, a node has 'Activity' terms (skiing, swimming, hiking, etc.) already assigned. I enable my module and re-save the node, to find that there is now a '$1000-$2500' term and an 'Australia' term assigned (which is my module in all its glory), but no 'Activity' terms anymore.

Is someone able to take a look at the following code to see if they can work out why it's happening and how I can fix it?

Something else that may be relevant: my module seemed to work fine on my test site - I had existing terms assigned which stayed when I added prices and locations. It's only on the proper website that it's not working. I did use the Views Bulk Operations module to save all existing nodes (rather than doing them all one by one), and got this error when it finished successfully:
warning: Illegal offset type in /home/brat/public_html/drupal/sites/all/modules/brat/brat.module on line 52.
I couldn't see a problem, but maybe this is why it's not working...

Any help would be much appreciated!

My module:

<?php
// $Id$

/**
 * @file
 * Custom code for Brat.info
 */

/**
 * Implementation of hook_nodeapi().
 *
 * Assign taxonomy term(s) to nodes based on CCK data
 */
function brat_nodeapi($node, $op) {
  if ($node->type == 'holiday') {
    if ($op == 'presave') {

      // Setup variables
      $price_field = $node->field_price[0]['value'];
      $price_vocab = 3;
      $location_field = $node->locations[0];
      $location_vocab = 2;

      // Make taxonomy easier to edit
      $taxonomy = _brat_convert_taxonomy($node->taxonomy);

      // Assign price term
      if (!empty($price_field)) {
        _brat_assign_price($price_field, $price_vocab, $taxonomy);
      }

      // Assign location term(s)
      if (!empty($location_field['country'])) {
        _brat_assign_location($location_field, $location_vocab, $taxonomy);
      }

      // Put taxonomy back to normal
      $node->taxonomy = _brat_reset_taxonomy($taxonomy);

    }
  }
}

/**
 * Convert taxonomy to arrays of terms
 */
function _brat_convert_taxonomy($terms) {
  foreach ($terms as &$term) {

    // Convert single terms to arrays
    if (!is_array($term)) {
      $term = array($term => $term);
    }

  }
  unset($term);
  return $terms;
}

/**
 * Assign term based on price
 */
function _brat_assign_price($price, $vocab, &$taxonomy) {
  // Get term for corresponding price bracket
  if ($price <= 250) {
    $term = _brat_get_term('< $250', $vocab);
  }
  elseif (($price > 250) && ($price <= 500)) {
    $term = _brat_get_term('$250 - $500', $vocab);
  }
  elseif (($price > 500) && ($price <= 750)) {
    $term = _brat_get_term('$500 - $750', $vocab);
  }
  elseif (($price > 750) && ($price <= 1000)) {
    $term = _brat_get_term('$750 - $1,000', $vocab);
  }
  elseif (($price > 1000) && ($price <= 2500)) {
    $term = _brat_get_term('$1,000 - $2,500', $vocab);
  }
  elseif (($price > 2500) && ($price <= 5000)) {
    $term = _brat_get_term('$2,500 - $5,000', $vocab);
  }
  elseif (($price > 5000) && ($price <= 7500)) {
    $term = _brat_get_term('$5,000 - $7,500', $vocab);
  }
  elseif (($price > 7500) && ($price <= 10000)) {
    $term = _brat_get_term('$7,500 - $10,000', $vocab);
  }
  else {
    $term = _brat_get_term('> $10,000', $vocab);
  }

  // Assign term to node
  $taxonomy[$term['vid']][$term['tid']] = $term['tid'];
}

/**
 * Assign term(s) based on location
 */
function _brat_assign_location($location_field, $vocab, &$taxonomy) {
  // Get human-readable names for country and state/province
  $location['country'] = location_country_name($location_field['country']);
  $location['state'] = location_province_name(
    $location_field['country'],
    location_province_code($location_field['country'], $location_field['province'])
  );
  $location['city'] = $location_field['city'];

  foreach ($location as $type => $loc) {
    // Make sure there's a value in each field
    if (!empty($loc)) {

      // Get term (if any) from specified vocabulary
      $term = _brat_get_term($loc, $vocab);

      // Create term if it doesn't exist
      if (empty($term)) {

        // Get parent ID (if any)
        if ($type != 'country') {
          if (($type == 'city') && (!empty($location['state']))) {
            $parent = $location['state'];
          }
          else {
            $parent = $location['country'];
          }
          $parent_term = _brat_get_term($parent, $vocab);
          $parent_id = $parent_term['tid'];
        }

        // Create new term
        $term = array(
          'vid' => $vocab,
          'name' => $loc,
          'parent' => $parent_id,
        );
        taxonomy_save_term($term);
      }

      // Assign term to node
      $taxonomy[$term['vid']][$term['tid']] = $term['tid'];

    }
  }
}

/**
 * Reset taxonomy back to original format
 */
function _brat_reset_taxonomy($terms) {
  foreach ($terms as &$term) {

    // Convert 1 element arrays to single terms
    if (count($term) == 1) {
      $values = array_values($term);
      $term = $values[0];
    }

  }
  unset($term);
  return $terms;
}

/**
 * Get taxonomy term from specified vocabulary
 */
function _brat_get_term($name, $vid) {
  foreach (taxonomy_get_term_by_name($name) as $vocab) {

    // Only return term if it exists in specified vocabulary
    if ($vocab->vid == $vid) {
      $term = array(
        'vid' => $vocab->vid,
        'tid' => $vocab->tid,
      );
      break;
    }

  }
  return $term;
}
?>

Comments

sime’s picture

When you unset($term); in _brat_reset_taxonomy isn't $term still a pointer to the last term on the array? Are you actually cause this to unset the array element?

--------
Em Space is Open

Anonymous’s picture

I was just following these instructions for referencing an array in a foreach loop: http://au2.php.net/manual/en/control-structures.foreach.php

I've actually saved a few nodes manually since posting this, and have found it works as expected, so I'm thinking it might actually be the Views Bulk Operations module that mucked something up...

sime’s picture

Yeah, short of implementing your code it looks pretty sane to me. BTW, I added ?> to the end of your code to make the syntax highlighting work.

Anonymous’s picture

Thanks :)

I've posted a support request on the Views Bulk Operations issue queue as I think that module has something to do with the problem: http://drupal.org/node/368364