You are here

function taxonomy_xml_set_term_relations in Taxonomy import/export via XML 7

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.2 taxonomy_xml.module \taxonomy_xml_set_term_relations()
  4. 6 taxonomy_xml.module \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 formats/csv_format.inc
Scan the input CSV file and create a taxonomy structure out of it.
taxonomy_xml_mesh_parse in formats/mesh_format.inc
Reads a XML file and creates the term definitions found in it.
taxonomy_xml_rdf_parse in formats/rdf_format.inc
Read in RDF taxonomies and vocabularies. Create vocabs and terms as needed.
taxonomy_xml_tcs_parse in formats/tcs_format.inc
Reads a TCS file and creates the term definitions found in it.

File

./taxonomy_xml.process.inc, line 784
The workhorse processes for importing taxonomies.

Code

function taxonomy_xml_set_term_relations(&$terms) {
  watchdog('taxonomy_xml', __FUNCTION__ . " Now connecting all known term relations and hierarchy links between this group of %count related terms. ", array(
    '%count' => count($terms),
  ), WATCHDOG_INFO);
  $relationship_predicates = array(
    TAXONOMY_XML_PARENT,
    TAXONOMY_XML_CHILD,
    TAXONOMY_XML_RELATED,
  );
  foreach ($terms as $guid => &$term) {
    if (empty($term)) {
      watchdog('taxonomy_xml', "An empty term '%guid' was in the array for relinking. This should not have happened, fix the input upstream. Ignoring.", array(
        '%guid' => $guid,
      ), WATCHDOG_NOTICE);
      continue;
    }

    // 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;
    }

    // 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) {
        if (in_array($predicate, $relationship_predicates)) {

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

            // The target itself here is either the id,
            // or a full representation of the item it points to.
            // If the second then the list is expected to be keyed
            // by identifier.
            $targetid = is_string($target) ? $target : $targetix;

            // #dpm(t("Term %termname references %targetid as a %predicate", array('$termname' => $term->name, '%targetid' => $targetid, '%predicate' => $predicate )));
            // Here we first try to find the referred term
            // in the list of recently-made terms.
            if (!isset($terms[$targetid])) {
              watchdog('taxonomy_xml', "Referenced term %targetid seems unknown so far, need to try a bigger lookup for it", array(
                '%targetid' => $targetid,
              ), WATCHDOG_DEBUG);

              // 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($targetid, $term->vid)) {
                $terms[$targetid] = $found_term;
                $found_term_names[] = l($found_term->name, "term/{$term->tid}") . ' ' . l('#', $targetid);
              }
              else {
                if ($found_term = taxonomy_xml_get_term_by_guid($target, $term->vid)) {
                  $terms[$target] = $found_term;
                  $found_term_names[] = l($found_term->name, "term/{$term->tid}") . ' ' . l('#', $target);
                }
                else {
                  watchdog('taxonomy_xml', "When processing '%term_name', we so far have no knowledge of the referenced term - (%predicate) '%target' !targetid. It should be imported later and linked in.", array(
                    '%term_name' => $term->name,
                    '%predicate' => $predicate,
                    '%target' => $target,
                    '!targetid' => l($targetid, $targetid),
                  ), WATCHDOG_DEBUG);
                  $found_term_names[] = $target;
                }
              }
            }
            else {

              // Already know about it in the current list.
              $found_term_names[] = l($terms[$targetid]->name . " (#" . $terms[$targetid]->tid . ")", 'taxonomy/term/' . $terms[$targetid]->tid);
            }
          }

          // ...each referred term.
          watchdog('taxonomy_xml', '%predicate relations of !term_name are !targets', array(
            '!term_name' => l($term->name, 'taxonomy/term/' . $term->tid),
            '%predicate' => $predicate,
            '!targets' => implode(', ', $found_term_names),
          ), WATCHDOG_INFO);
        }
        else {
          watchdog('taxonomy_xml', "{$predicate} is not a " . implode(',', $relationship_predicates), array(), WATCHDOG_DEBUG);
        }
      }

      // ...each type of predicate.
    }

    // ...has predicates.
    watchdog('taxonomy_xml', __FUNCTION__ . " Prepared the list of all required referree terms. Now establishing the actual links.", array(), WATCHDOG_INFO);

    // 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])) {

      // Link in 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->tid);
              if (in_array($term->tid, array_keys($ancestors))) {
                watchdog('taxonomy_xml', "Not setting %name as a descendant of itself. Avoiding a potential infinite loop.", array(
                  '%name' => $term->name,
                ), 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, "taxonomy/term/{$term->tid}/edit"),
                '%tid' => $term->tid,
                '!parent' => l($parent->name, "taxonomy/term/{$parent->tid}/edit"),
                '%ptid' => $parent->tid,
                '!source' => $_taxonomy_xml_current_doc,
              )));

              // SPECIALNESS D7.
              // When an unparented term placeholder was created earlier,
              // it was given parent=0 (root) by default.
              // If that happened, and we NOW are setting a real parent
              // - that old relationship
              // is to be anulled.
              // This may not always be intentional - but is likely the case
              // and produces the structure we actually expect.
              if (isset($term->parent[0]) && $term->parent[0] == 0) {
                unset($term->parent[0]);
              }

              // Note that core sometimes calls the array 'parent' and
              // sometimes 'parents'. It's just confused!
              // To save, we use the singular.
              $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");}
    // 'related' or 'synonyms' is no longer supported in D7
    // @todo ressurect it with the fieldable terms
    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)) {

      // It may be premature to save this term if we don't know its parent yet,
      // The system will default to parent=0, which causes bad structure later on
      if (!isset($term->parent)) {
        watchdog('taxonomy_xml', __FUNCTION__ . " About to save a term with no parent, this could be a problem later. <pre>!term</pre>", array(
          '!term' => print_r($term, 1),
        ), WATCHDOG_INFO);
      }
      taxonomy_term_save($term);
    }
  }

  // .. each term.
}