You are here

function taxonomy_xml_set_term_relations in Taxonomy import/export via XML 6.2

Same name and namespace in other branches
  1. 5.2 taxonomy_xml.module \taxonomy_xml_set_term_relations()
  2. 5 taxonomy_xml.module \taxonomy_xml_set_term_relations()
  3. 6 taxonomy_xml.module \taxonomy_xml_set_term_relations()
  4. 7 taxonomy_xml.process.inc \taxonomy_xml_set_term_relations()

Given a list of terms, set the related-terms and structure, and save again

Helper function for bulk processes.

The terms are currently indexed by either URI or name. The reference arrays refer to either the URI or name. Scan the current array for the objects (terms) being linked to.

Input would look (in part) like this:

$terms = array( '#123' => ( 'name' => 'hotels', 'tid' => 23, 'predicates' => ( 'See Also' => ['#135', 'camping'] 'Broader Term' => ['accomodation'] ) ) '#135' => ( 'name' => 'motels', 'tid' => 35 ) '#145' => ( 'name' => 'camping', 'tid' => 37 ) 'accomodation' => ( 'name' => 'accomodation', 'tid' => 11 ) )

The process will read the 'predicates', figure out what they mean, figure out which other term is being referenced, and create properties on the term object.

And will return the term objects with appropriate Drupal attributes

'#123' => ( 'name' => 'hotels', 'nid' => 23, 'parent' => 11, 'relations' => array(35, 37), )

Note that the key need have no relation with the nid, and may be a full string, which will work just as well. The above shows an example of both, although that would be rare in the one import.

Relationships cannot be created if the target term is not included in the $terms list itself. If we are adding to an existing vocab, doing a partial merge, the target terms should have already been retrieved from the database and included in the complete list.

Parameters

An indexed array of existing taxonomy term objects, possibly referring: to each other by id. It's possible for the same term to be in the list twice, under different keys, (indexed by tid, name or URL) but these should be HANDLES on the same object by reference, so changes will stick.

4 calls to taxonomy_xml_set_term_relations()
taxonomy_xml_csv_parse in ./csv_format.inc
Scan the input CSV file and create a taxonomy structure out of it.
taxonomy_xml_mesh_parse in ./mesh_format.inc
Reads a XML file and creates the term definitions found in it.
taxonomy_xml_rdf_parse in ./rdf_format.inc
Read in RDF taxonomies and vocabularies. Create vocabs and terms as needed.
taxonomy_xml_tcs_parse in ./tcs_format.inc
Reads a TCS file and creates the term definitions found in it.

File

./taxonomy_xml.module, line 1319
This module makes it possible to import and export taxonomies as XML documents.

Code

function taxonomy_xml_set_term_relations(&$terms) {

  #drupal_set_message(t("Now connecting all known term relations and hierarchy links between this group of %count related terms.", array('%count' => count($terms) )), 'trace');
  $relationship_predicates = array(
    TAXONOMY_XML_PARENT,
    TAXONOMY_XML_CHILD,
    TAXONOMY_XML_RELATED,
  );
  foreach ($terms as $guid => &$term) {

    // Simplify the t() calls.
    $strings = array(
      '%tid' => $term->tid,
      '%guid' => $guid,
      '!name' => l($term->name, 'admin/content/taxonomy/edit/term/' . $term->tid),
    );

    // Avoid doing this again if we are stuck in a recursive loop,
    // batch, or working with duplicate handles
    if (isset($term->taxonomy_xml_linked)) {
      continue;
    }

    // Load this to detect looping relationships
    $ancestors = taxonomy_xml_get_term_ancestors($term);

    // The predicates (relationships) array may contain actual handles on terms, term ids,
    // or it may still contain URIs representing terms not yet loaded in this phase.
    // We need to resolve those external references into term handles (or at least tids) if possible.
    if (isset($term->predicates) && is_array($term->predicates)) {
      foreach ($term->predicates as $predicate => &$targets) {
        $strings['%predicate'] = $predicate;
        if (in_array($predicate, $relationship_predicates)) {

          // Keep a list just for logging
          $found_term_names = array();
          foreach ($targets as $target_ix => &$target) {

            // $target is the identifier guid.
            // We will initialize $terms[$target]
            watchdog('taxonomy_xml', "Term %termname references %target as a %predicate", array(
              '%termname' => $term->name,
              '%target' => $target,
              '%predicate' => $predicate,
            ), WATCHDOG_DEBUG);

            // Here we first try to find the referred term in the list of recently-made terms
            if (!isset($terms[$target])) {

              #drupal_set_message(t("Referenced term %target seems unknown so far, need to try a bigger lookup for it", array('%targetid' => $targetid)));

              // taxonomy_enhancer.module, if available, may have more data about our terms. Hopefully including a GUID.
              if ($found_term = taxonomy_xml_get_term_by_guid($target, $term->vid)) {
                $terms[$target] = $found_term;
                $found_term_names[] = l($found_term->name, "taxonomy/term/{$found_term->tid}") . ' ' . l('#', $target);
              }
              else {
                if ($found_term = taxonomy_xml_get_term_by_guid($target, $term->vid)) {
                  $terms[$target] = $found_term;
                  $found_term_names[] = l($found_term->name, "taxonomy/term/{$found_term->tid}") . ' ' . l('#', $target);
                }
                else {

                  #drupal_set_message(t("We so far have no knowledge of the referenced term - ". $term->name ." '$predicate' !targetid. It should be imported later and linked in.", array('!targetid' => l($targetid, $targetid))));
                  $found_term_names[] = $target;
                }
              }
            }
            else {

              // already know about it
              $found_term_names[] = $terms[$target]->name;

              #if (empty($terms[$target]->name)) dpm($terms[$target]);

              # . (!empty($terms[$target]->tid)) ? " (taxonomy/term/{$terms[$target]->tid})" : '' ;
            }

            // Drupal is liable to implode if we ever establish a relationship
            // from a term to itself.
            // Lets never allow that, remove the relationship.
            if (isset($term->guid) && $term->guid == $target) {
              watchdog('taxonomy_xml', "Not supporting setting !name as related to itself as a %predicate. Avoiding a potential infinite loop.", $strings, WATCHDOG_WARNING);
              unset($term->predicates[$predicate][$target_ix]);
            }
          }

          // each referred term
          watchdog('taxonomy_xml', '%predicate relations of %term_name are : %targets', array(
            '%term_name' => $term->name,
            '%predicate' => $predicate,
            '%targets' => implode(', ', $found_term_names),
          ), WATCHDOG_INFO);
        }

        // Is a 'relationship' sort of predicate.

        #else {

        #    watchdog('taxonomy_xml', "$predicate is not a ". implode(',', $relationship_predicates));

        #}
      }

      // each type of predicate
    }

    // has predicates
    // Go through all and add relationships
    // Note that a modification was made by flagging $term->taxonomy_xml_relinked = TRUE;
    //
    // The linking earlier may have given us some duplicates if the source had redundant info, so filter for uniques
    if (isset($term->predicates[TAXONOMY_XML_PARENT]) && is_array($term->predicates[TAXONOMY_XML_PARENT])) {
      foreach (array_unique($term->predicates[TAXONOMY_XML_PARENT]) as $key => $othertermname) {
        if ($othertermname) {

          // Here we try to find the referred term in the list of recently-made terms
          if (isset($terms[$othertermname])) {
            $parent = $terms[$othertermname];
            if ($parent && isset($parent->tid)) {

              // Due to possible data inconsistencies (input from Freebase)
              // We need to take care to avoid infinite ancestry loops
              // Which Drupal doesn't handle.
              // It prevents that in the UI, not at the data level
              // So need to scan the ancestry tree to make sure we don't add this term
              // as a descendant of itself
              $ancestors = taxonomy_xml_get_term_ancestors($parent);
              if (in_array($term->tid, array_keys($ancestors))) {
                watchdog('taxonomy_xml', "Not setting !name as a descendant of itself. Avoiding a potential infinite loop.", $strings, WATCHDOG_WARNING);
                continue;
              }
              global $_taxonomy_xml_current_doc;
              drupal_set_message(t("!name # %tid is a child of !parent # %ptid (<a href='!source' style='font-size:x-small'>source</a>)", array(
                '!name' => l($term->name, 'admin/content/taxonomy/edit/term/' . $term->tid),
                '%tid' => $term->tid,
                '!parent' => l($parent->name, 'admin/content/taxonomy/edit/term/' . $parent->tid),
                '%ptid' => $parent->tid,
                '!source' => $_taxonomy_xml_current_doc,
              )));
              $term->parent[$parent->tid] = $parent->tid;
            }
          }
          else {

            #drupal_set_message(t("Couldn't find the parent identified as %termname for %name # %tid", array('%termname' => $othertermname, '%name' => $term->name, '%tid' => $term->tid) ));
          }
        }
      }
      $term->taxonomy_xml_relinked = TRUE;
    }

    #else{drupal_set_message(" $name ". $term->tid ." has no parent term");}

    // Run through children and set their parent to this.
    //
    // On the first run-through, it's premature to try and set children,
    // the link is supposed to be set from child -> parent.
    // But - if updating an existing parent, or if the child terms WERE successfully
    // found already and have tids, then DO go and set them as children.
    // This is only to support later modified imports where a parent heirarchy
    // is being re-adjusted over top of known terms.
    if (isset($term->predicates[TAXONOMY_XML_CHILD]) && is_array($term->predicates[TAXONOMY_XML_CHILD])) {
      foreach (array_unique($term->predicates[TAXONOMY_XML_CHILD]) as $key => $othertermname) {
        $strings['!child_guid'] = $othertermname;

        // It would create a loop if we ever set a child that was also an ancestor
        if (in_array($term->tid, array_keys($ancestors))) {
          watchdog('taxonomy_xml', "Not supporting setting !name as related to an ancestor as a %predicate. Avoiding a potential infinite loop.", $strings, WATCHDOG_WARNING);
          unset($term->predicates[$predicate][$target_ix]);
        }

        // For each child of this parent
        watchdog('taxonomy_xml', "!name # %tid Has a child identified as !child_guid", $strings, WATCHDOG_DEBUG);
        if (!empty($othertermname) && isset($terms[$othertermname])) {
          $child_term =& $terms[$othertermname];
          if (!$child_term->tid) {

            // This was a placeholder only, not yet made.
            // SO the parent cannot force it it link back.
            continue;
          }
          $strings['!child'] = l($child_term->name, 'admin/content/taxonomy/edit/term/' . $child_term->tid);
          if (empty($child_term->parent)) {
            $child_term->parent = array();
          }

          // If it's not already set,
          if (!in_array($term->tid, $child_term->parent)) {

            // set the child to point to this parent
            $child_term->parent[$term->tid] = $term->tid;
            drupal_set_message(t("!name # %tid Has a child called !child", $strings));

            // Save it now. This may save too often, but
            // this change may have been missed otherwise, due to arbitrary processing order
            if (empty($child_term->taxonomy_xml_relinked)) {
              $save_term = (array) $child_term;
              taxonomy_save_term($save_term);
            }
          }
          else {
            watchdog('taxonomy_xml', "!name already knows it has a child called !child", $strings, WATCHDOG_DEBUG);
          }
        }
        else {
          watchdog('taxonomy_xml', "We haven't loaded child term !child_guid in this run, so not touching it. Could be if recursion is off.", $strings, WATCHDOG_DEBUG);
        }
      }
    }

    // Set the is-related link also
    if (isset($term->predicates[TAXONOMY_XML_RELATED]) && is_array($term->predicates[TAXONOMY_XML_RELATED])) {
      foreach (array_unique($term->predicates[TAXONOMY_XML_RELATED]) as $key => $othertermname) {
        if ($othertermname) {
          if (isset($terms[$othertermname])) {
            $related = $terms[$othertermname];
            $term->relations[$related->tid] = $related->tid;

            #drupal_set_message("Term ". $term->name ." ". $term->tid ." is related to $related->name ". $related->tid);
          }
          else {
            drupal_set_message(t("\n              Couldn't find the term called '%termname'\n              to link to '%name' as being related to this.\n              This relationship will be discarded. ", array(
              '%name' => $term->name,
              '%termname' => $othertermname,
              '%debug' => print_r(array_keys($terms), 1),
            )));

            #dpm(array('available terms' => array_keys($terms)));
          }
        }
      }
      $term->taxonomy_xml_relinked = TRUE;
    }
    if (!empty($term->synonyms_array)) {
      $term->synonyms = implode("\n", array_unique($term->synonyms_array));
      $term->taxonomy_xml_relinked = TRUE;
    }

    #dpm(array('Saving' => $term));
    $term->taxonomy_xml_linked = TRUE;

    // for efficiency, only re-save terms that really need it
    if (!empty($term->taxonomy_xml_relinked)) {
      $save_term = (array) $term;
      taxonomy_save_term($save_term);
      unset($term->taxonomy_xml_relinked);
    }
  }
}