Create taxonomy tree programmatically including vocabulary creation

Sometimes setting up all the taxonomy tree manually it's a complex task, especially if you have hundreds of terms to create, re-parent.

Bellow is a code snippet I created to manage a fast and clear taxonomy term creation.

It can support unlimited nested tree as the job is done recursively.

Comments in snippet are self-explanatory.

/**
 * Example of how to use functionality.
 *
 * Usually it will be called in hook_install() or hook_update()
 */
function _taxonomy_tree_generate_example() {
  $trees = array(
    'Article Type' => array(
      'News',
      'Reviews',
    ),
    'Category' => array(
      'Level 1' => array(
        'Level 2 1',
        'Level 2 2' => array(
          'Level 3 1',
          'Level 3 2',
          'Level 3 3' => array(
            'Level 4 1',
            'Level 4 2'
          ),
        ),
        'Level 2 3',
      ),
      'Events',
      'Destinations' => array(
        'More terms',
      ),
      'And so on...',
    ),
  );

  taxonomy_tree_generate($trees);
}


/**
 * Loop through the Vocabularies and generate all the taxonomy tree.
 */
function taxonomy_tree_generate($trees) {
  foreach ($trees as $vocabulary_name => $vocabulary_tree) {
    _taxonomy_tree_generate_create_vocabulary_tree($vocabulary_name, $vocabulary_tree);
  }
}

/**
 * Initial preparing for processing the nested array.
 */
function _taxonomy_tree_generate_create_vocabulary_tree($vocabulary_name, $vocabulary_tree) {
  // Create vocabulary if it doesn't exist.
  $vocabulary = _taxonomy_tree_generate_get_vocabulary_by_name($vocabulary_name);
  if (!isset($vocabulary->vid)) {
    if (!function_exists('pathauto_cleanstring')) {
      module_load_include('inc', 'pathauto');
    }
    $vocabulary = new stdClass();
    $vocabulary->name = $vocabulary_name;
    if (function_exists('pathauto_cleanstring')) {
      $vocabulary_machine_name = pathauto_cleanstring($vocabulary_name);
    }
    else {
      $vocabulary_machine_name = drupal_strtolower(preg_replace('/[^a-z\d ]/i', '', check_plain($vocabulary_name)));
    }
    $vocabulary->machine_name = str_replace('-', '_', $vocabulary_machine_name);
    taxonomy_vocabulary_save($vocabulary);
  }

  // Test purpose, delete all terms before create them.
  //foreach (taxonomy_get_tree($vocabulary->vid) as $term) {
  //  taxonomy_term_delete($term->tid);
  //}
  // Create taxonomy tree recursively.
  _taxonomy_tree_generate_create_one_level_tree($vocabulary_tree, $vocabulary);
}

/**
 * Create taxonomy tree recursively.
 */
function _taxonomy_tree_generate_create_one_level_tree($vocabulary_tree, $vocabulary, $parent = 0) {
  $weight = 0;
  foreach ($vocabulary_tree as $parent_term_name => $term_name) {
    // If one level.
    if (!is_array($term_name)) {
      $term = _taxonomy_tree_generate_create_term($term_name, $vocabulary, $parent, $weight);
    }
    // If two levels or more.
    else {
      $term = _taxonomy_tree_generate_create_term($parent_term_name, $vocabulary, $parent, $weight);
      // Go deeper.
      _taxonomy_tree_generate_create_one_level_tree($term_name, $vocabulary, $term->tid);
    }
    ++$weight;
  }

  return;
}

function _taxonomy_tree_generate_create_term($name, $vocabulary, $parent, $weight) {
  $term = taxonomy_get_term_by_name($name, $vocabulary->machine_name);
  if (empty($term)) {
    $term = new stdClass();
    $term->name = $name;
    $term->vid = $vocabulary->vid;
  }
  else {
    $term = reset($term);
  }
  // Even if term exist already, we want to push our parent and weight.
  $term->parent = $parent;
  $term->weight = $weight;
  taxonomy_term_save($term);

  return $term;
}

/**
 * Get vocabulary object by vocabulary name.
 */
function _taxonomy_tree_generate_get_vocabulary_by_name($vocabulary_name) {
  $query = db_select('taxonomy_vocabulary', 'tv');
  $query->fields('tv', array(
    'machine_name',
    'vid',
  ));
  $query->condition('tv.name', $vocabulary_name, '=');
  $vocabulary = $query->execute()->fetchObject();
  return $vocabulary;
}

 

********************************** ************************* ************************ **************** ****************** *********** ************** ************* ************ *************