The current version (1.284) of taxonomy.module has had taxonomy_term_path removed at some point, apparently when hook_link_alter was introduced, as mentioned in the 4.8 conversion guide.

The immediate workaround for affected modules is simple (in contrib, taxonomy_context and categories are affected, custom local modules and themes are probably more significant): just replace

$path = taxonomy_term_path($term);

by

$path = "taxonomy/term/$term->tid";

However, in the long run, I think it is better (more future-proof) to have taxonomy generate its links than to rely on an undocumented (is it really ?) hard-coded path. That way, in case some radical URL scheme change is required, all it takes is one change in taxonomy.module, not changes in every dependent module.

The constant string is currently used in core by:

  • forum.module : it used to be the only function in core relying on taxonomy_term_path, if I'm not mistaken (http://drupal.org/node/18260#comment-96789)
  • legacy.module
  • path.module : uses it only in its help strings for users
  • and of course taxonomy.module itself

However, the reimplementation of the function in taxonomy.module should probably not use the older method that didn't allow for hook_link_alter to work.

Last but not least, if it is decided to keep the function removed, that removal should be documented in the 4.8 conversion guide.

Comments

chx’s picture

with hook_link_alter and preg you could replace the taxonomy path

probably that can could be the reason for the removal.

chx’s picture

but still, the change should be documented, with appropriate code examples (do not look at me, I am currently cooking the pre CCK patch)

fgm’s picture

As we discussed the whole point is for modules to obtain a taxonomy path as it should be by default. It can be altered by a module using hook_link_alter if applicable, or left unchanged to has a resilient link system.

robertdouglass’s picture

Status: Active » Needs review

isn't there a difference between a path and a link? Anyway, unaware as I was of the history of the issue, I wrote myself a helper function for generating taxonomy paths.

/**
 * Taxonomy REST API: Build a path to a taxonomy listing page or feed
 *
 * @param $terms
 *   a single numeric term id, or a single $term object
 *   or an array of numeric term ids or $term objects
 * @param $op
 *   a string "AND" or "OR"
 * @param $depth
 *   depth of the taxonomy tree that is to be returned. 0 equals the entire tree
 * @param $feed
 *   TRUE for RSS feed, FALSE for page
 */
function taxonomy_get_path($terms, $op = 'AND', $depth = 0, $feed = FALSE) {
  // prepare $tids associative array $tid => $term accounting for the possibility
  // that $terms is either a tid, a $term, an array of tids, or an array of $term objects
  if (is_array($terms) && count($array) > 0) {
    foreach ($terms as $term) {
      if (is_numeric($term)) {
        $term = taxonomy_get_term($term);
      }
      if (is_object($term)) {
        $tids[$term->tid] = $term;
      }
    }
  }
  else {
    if (is_numeric($terms)) {
      $tids[$terms] = taxonomy_get_term($terms);
    }
    elseif (is_object($terms)) {
      $tids[$terms->tid] = $terms;
    }
  }

  // build the $path
  $path = 'taxonomy/term';
  $op = strtolower($op);
  switch ($op) {
    case 'and':
      $path .= '/'. implode(',', array_keys($tids));
      break;

    case 'or':
      $path .= '/'. implode('+', array_keys($tids));
      break;
  }

  $feed = ($feed) ? 'feed' : 'page';
  $path .= "/$depth/$feed";
  return $path;
}

I'm setting this to patch (code needs review) even though this obviously isn't a patch because you can read it as is and use copy and paste if it is deemed worthy of taxonomy module. I'll provide a patch to Drupal to make use of it if people like the function.

robertdouglass’s picture

Slight aesthetic improvements; 'page' is never explicitely stated and depth is omitted when zero.

/**
 * Taxonomy REST API: Build a path to a taxonomy listing page
 *
 * @param $terms
 *   a single numeric term id, or a single $term object
 *   or an array of numeric term ids or $term objects
 * @param $op
 *   a string "AND" or "OR"
 * @param $depth
 *   depth of the taxonomy tree that is to be returned. 0 equals the entire tree
 * @param $feed
 *   TRUE for RSS feed, FALSE for page
 */
function taxonomy_get_path($terms, $op = 'AND', $depth = 0, $feed = FALSE) {
  // prepare $tids associative array $tid => $term accounting for the possibility
  // that $terms is either a tid, a $term, an array of tids, or an array of $term objects
  if (is_array($terms) && count($array) > 0) {
    foreach ($terms as $term) {
      if (is_numeric($term)) {
        $term = taxonomy_get_term($term);
      }
      if (is_object($term)) {
        $tids[$term->tid] = $term;
      }
    }
  }
  else {
    if (is_numeric($terms)) {
      $tids[$terms] = taxonomy_get_term($terms);
    }
    elseif (is_object($terms)) {
      $tids[$terms->tid] = $terms;
    }
  }

  // build the $path
  $path = 'taxonomy/term';
  $op = strtolower($op);
  switch ($op) {
    case 'and':
      $path .= '/'. implode(',', array_keys($tids));
      break;

    case 'or':
      $path .= '/'. implode('+', array_keys($tids));
      break;
  }
  
  if ($depth > 0) {
    $path .= "/$depth";
  }

  if ($feed) {
    if ($depth > 0) {
      $path .= '/feed';
    }
    else {
      $path .= '0/feed'
    }
  }
  return $path;
}
robertdouglass’s picture

StatusFileSize
new1.46 KB

missing semicolon in that last version.

dries’s picture

Status: Needs review » Needs work

I don't think the current system is broken. Robert's function is interesting and might pave the path for a views API.

Can this function be re-used internally? Let the taxonomy module eat its own dog food?

Ideally, we'd also have a function that does the inverse. Given a path, it builds a query.

robertdouglass’s picture

Nice suggetions, Dries. I'm on vacation now, will address these issues when I return. I think the "eat its own dogfood" part belongs with this patch, whereas the inverse bit should be separate. Agree?

robertdouglass’s picture

StatusFileSize
new1.46 KB

taxonomy_term_path had been reintroduced, but now I've replaced it with my proposed function. Taxonomy_term_path only got called once in all of Drupal core. I've integrated its functionality into my function and removed it. Now taxonomy_get_path is called where taxonomy_term_path used to. There is a slight overhead increase (an extra if statement), but otherwise no difference for the previously existing cases. Now, however, the full power of our taxonomy REST API is exposed.

robertdouglass’s picture

StatusFileSize
new3.03 KB

wrong file.

robertdouglass’s picture

Status: Needs work » Needs review
fgm’s picture

Robert,

There is also the case of the places where the hardcoded string is (was) used, which I mentioned in the initial description of the problem. Maybe it would make sense to patch them too ?

robertdouglass’s picture

you mean these?

# forum.module : it used to be the only function in core relying on taxonomy_term_path, if I'm not mistaken (http://drupal.org/node/18260#comment-96789)
# legacy.module
# path.module : uses it only in its help strings for users

Yes, that would be necessary as well. I'll wait for committer feedback before doing that, though.

robertdouglass’s picture

@fgm in the meantime, the function needs some rigorous testing, if you feel up to it.

fgm’s picture

The code looks great to me. However:

  • there's duplication in the beginning, that might be refactored. Not sure whether it is worth it, though ?
  • taxonomy_term_path included a path generation logic taking into account vocabularies using module_invoke if the vocabulary of the term (single term in that function, not an array as in yours) happened not to be "taxonomy": this case is not taken into account in the version you suggest
robertdouglass’s picture

taxonomy_term_path included a path generation logic taking into account vocabularies using module_invoke if the vocabulary of the term (single term in that function, not an array as in yours) happened not to be "taxonomy": this case is not taken into account in the version you suggest

It's there, but just for the exact case of one single term object:

+    elseif (is_object($terms)) {
+      $vocabulary = taxonomy_get_vocabulary($term->vid);
+      if ($vocabulary->module != 'taxonomy' && $path = module_invoke($vocabulary->module, 'term_path', $term)) {
+        return $path;
+      }
+      $tids[$terms->tid] = $terms;
+    }

I suppose it is in the wrong place. I included it there as a hack to guarantee backwards compatability, but it should really be applied even-handedly. It's just that it would change the API for hook_term_path altogether and make it hook_terms_path instead, and all modules would then have to ready themselves for all the cases that this function handles. I'd be fine with that and would assist in the transition. Dries?

robertdouglass’s picture

Title: taxonomy_term_path removed » Taxonomy API: taxonomy_term_path => taxonomy_get_patch

changing the title to reflect the current nature of the patch

Bèr Kessels’s picture

FWIW, i added this in helpers module. In a new file/module called helpers_taxonomy.module.

webchick’s picture

Title: Taxonomy API: taxonomy_term_path => taxonomy_get_patch » Taxonomy API: taxonomy_term_path => taxonomy_get_path
Version: x.y.z » 6.x-dev

patching file modules/taxonomy/taxonomy.module
Hunk #1 succeeded at 47 with fuzz 1 (offset 16 lines).
Hunk #2 FAILED at 63.
Hunk #3 succeeded at 893 (offset 105 lines).
1 out of 3 hunks FAILED -- saving rejects to file modules/taxonomy/taxonomy.module.rej

webchick’s picture

Status: Needs review » Needs work
nancydru’s picture

I assume this will not be in D6.

pancho’s picture

Version: 6.x-dev » 7.x-dev

I guess not, since this is a major API change. Would be great though early in D7.

xano’s picture

Subscribing.

catch’s picture

Version: 7.x-dev » 8.x-dev
dave reid’s picture

Would it possibly be easier to replace this whole logic with #320331: Turn custom_url_rewrite_inbound and custom_url_rewrite_outbound into hooks? We could replace form_term_path with forum_url_rewrite_outbound and forum_url_rewrite_inbound(). Other modules would do the same.

nancydru’s picture

With all the many, many places I use taxonomy_term_path, I'm not sure I like that idea.

dave reid’s picture

I think you misunderstand. Usually you call taxonomy_term_path inside of url() or l(). If this were converted to hook_url_alters then it would be called automatically by url() and you would just simply remove the taxonomy_term_path() call.

nancydru’s picture

Well, I'm all for making life easier. Thanks for educating an old girl.

dropcube’s picture

Version: 8.x-dev » 7.x-dev
Status: Needs work » Fixed

For taxonomy links and modules taxonomy_term_path() and hook_term_path() have been replaced by hook_url_alter_outbound().

http://drupal.org/node/224333#hook_url_outbound_alter

Issue: #320331: Turn custom_url_rewrite_inbound and custom_url_rewrite_outbound into hooks

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.