need help understanding taxonomy term sorting behavior, and how to override
I've got a hierarchical taxonomy vocabulary with 24 terms currently, but eventually this will grow to include many more. Currently I'm using the Term Display module to list my terms on each node. The terms which are musical instruments grouped in families (woodwinds, brass, strings, etc.) must be listed in a specific standard order. When viewing my term list page (Administer › Content management › Categories) for this vocab, I have all the terms listed as I want them by giving each parent term (instrument family) and then each child term (individual instruments) a specific weight. It looks something the tree recreated below. I've added the assigned weights in brackets after each term.
Woodwinds [weight 0]
-- Flute [weight 0]
-- Piccolo [weight 1]
-- Alto Flute [weight 2]
Brass [weight 1]
Percussion [weight 2]
-- Pitched Percussion [weight 0]
---- Xylophone [weight 0]
---- Glockenspiel [weight 1]
---- Chimes [weight 2]
-- Unpitched Percussion [weight 1]
---- Temple Blocks [weight 0]
-- Gamelan [weight 2]
Harp & Keyboards [weight 3]
-- Harp [weight 0]
-- Piano [weight 1]
-- Organ [weight 2]
Strings [weight 4]
-- Violin [weight 0]
---- Violin 1 [weight 0]
---- Violin 2 [weight 1]
-- Viola [weight 1]
-- Cello [weight 2]
-- Bass [weight 3]
Electronic Tape [weight 5]
Now it seems to me that intuitively, the ordering of the terms as listed in this tree-like arrangement should be preserved in any listing on a node, but is not case. Instead drupal seems to sort by the individual term weights and then alphabetically. For example on the node that has the most instruments listed they appear in this order:
Flute, Harp, Temple Blocks, Violin 1, Xylophone, [all weight 0]
Glockenspiel, Piccolo, Viola, Violin 2, [all weight 1]
Alto Flute, Cello, Chimes, [all weight 2]
Bass [weight 3]
This is all perfectly logical, but is there a way around it. Obviously I could theoretically assign the appropriate individual term weights to each instrument term, but eventually I will have more than the 21 levels of weights provided by the taxonomy module. Any ideas on how to get the terms in the tree order?

You are right that hierarchy
You are right that hierarchy is not reflected in the order of display.
It might help a bit, if you use a different vocabulary for each family. This will also mean that and etc. are not listed as instruments.
BTW. Shouldn’t Piano be in Pitched Percussion?
___________________
It’s in the detaιls…
I agree that breaking each
I agree that breaking each family down into it's own vocabulary might make organizing things easier in some respects, but then I would have different fields listed as you suggest: i.e. woodwinds, strings, percussion, etc. when I really want everything listed in just one field in each node under the heading of "Instruments:". Even if there were a way to combine the separate vocabularies under an added field label, I would still like to find a way to override the built-in sort behavior. I can imagine that there might be a lot of applications for it.
Thanks for the reply.
(Piano has been traditionally classified as a percussion instrument, but in orchestral scores, it is grouped with other keyboard instruments as well as with the harp. It is the orchestral score order I am trying to reflect in my listing.)
possible approach
I haven't had time to investigate this fully, and my php knowledge is at a very basic stage, but here is what I've found. The taxonomy_get_tree function returns my vocabulary listed above with all the terms in the order I want them. The problem is that it returns all of the terms in the vocab, not just the the terms associated with a particular node. Does anyone have a clue about how to combine the sorting behavior of taxonomy_get_tree with the node-relevance filtering of taxonomy_node_get_terms?
Anthony
--
anthonymosakowski.com
intersection
As both these functions return arrays, you can use;
$result = array_intersect(taxonomy_get_tree(...), taxonomy_node_get_terms(...));This will preserve the order of the first array.
I don’t think this is the most efficient solution :-(
___________________
It’s in the detaιls…
demonstration portfolio
tried, but no luck
Hey Zeta,
if you're still around, thanks for the tip: letting me know about the array_intersect function. It seems like it might be the way to go, but so far I haven't had success. Here's what I've got:
<?php$vid = 3;
$node = node_load(arg(1));
$treeterms = taxonomy_get_tree($vid, 0, 0);
$taxterms = taxonomy_node_get_terms_by_vocabulary($node->nid, $vid);
$iterms = array_intersect($treeterms, $taxterms);
$links = array();
foreach($iterms as $iterm) {
$links[] = l($iterm->name, taxonomy_term_path($iterm));
}
return '<strong>' . check_plain($vocabulary->name) .':</strong> '. implode(', ', $links) .'<br>';
?>
I know that each array does contain a list of terms, because if I change the $links definition to just output each array separately rather than the intersection of both, I get a list of terms. However, the definition above results in a long list of identical error messages:
recoverable fatal error: Object of class stdClass could not be converted to string in ...includes/common.inc(1352) : eval()'d code on line 12.This isn't entirely clear to me. Is it saying the two arrays are somehow incompatible for intersection?
Anthony
--
anthonymosakowski.com
Comparisons
What version of Drupal are you using?
Is that the full error message?
If so, then I think this is caused by lack of keys. Try using array_intersect_assoc($treeterms, $taxterms). I’m not optimistic about this option, but no harm in trying.
More likely, I think, is the comparison performed by array_intersect, needs strings: So instead use array_uintersect($treeterms, $taxterms, 'tax_term_cmp'). This will compare terms using a function;
<?phptax_term_cmp($term_1, $term_2) {
return $term_1->tid - $term_2->tid;
}
?>
___________________
It’s in the detaιls…
demonstration portfolio
will try these
I'm using Drupal 5.7, and the error message I quoted above is complete. There are are many, too many to count: I have to scroll all the way down the page to see anything other than my node title, but the node content does get displayed, just not the taxonomy terms I have set up my code to output.
I'll give your suggestions a try and report back. There's no way to learn about how to do these things other than by doing them. When I read about arrays on php.net, I found that the contents of arrays are always converted to strings, so I'm not sure why there is any issue here, but obviously something is not matching up. And probably I didn't quite understand what I read. Thanks again.
Anthony
--
anthonymosakowski.com
what about this?
Haven't had a chance to try your suggestions yet, but I did a little more "light reading" on php.net and found the array_key function, so I added this code:
<?php
print 'Treeterm keys: ';
print_r(array_keys($treeterms));
print '<br>';
print 'Taxterm keys: ';
print_r(array_keys($taxterms));
print '<br>';
?>
which output this on my page:
Treeterm keys: Array ( [0] => 0 [1] => 1 [2] => 2 [3] => 3 [4] => 4 [5] => 5 [6] => 6 [7] => 7 [8] => 8 [9] => 9 [10] => 10 [11] => 11 [12] => 12 [13] => 13 [14] => 14 [15] => 15 [16] => 16 [17] => 17 [18] => 18 [19] => 19 [20] => 20 [21] => 21 [22] => 22 [23] => 23 [24] => 24 )Taxterm keys: Array ( [0] => 2 [1] => 62 [2] => 31 [3] => 32 [4] => 59 [5] => 4 [6] => 27 [7] => 30 [8] => 63 [9] => 11 [10] => 28 [11] => 29 [12] => 61 )
It shows that keys are present in both arrays but don't match, (or that the taxonomy_get_tree function assigns some sort of arbitrary key at any rate) does that mean anything?
Anthony
--
anthonymosakowski.com
Yes, it means...
I'm less optimistic about the first option, and more optimistic about the second.
When I said I meant keys other than the default 1, 2, 3… – every array has some sort of key, sometimes they carry significant information. It seems that the second array, at least, uses the tid as a key.
It would be more useful to print output of;
<?phpprint '<pre>Treeterm keys: ';
print_r($treeterms);
print 'Taxterm keys: ';
print_r($taxterms);
print '</pre>';
?>
___________________
It’s in the detaιls…
demonstration portfolio
success at last
Hi Zeta,
I finally had some time to work this all out and now have some code that does what I want:
<?php// PHP snippet to print Instrumenation taxonomy terms in tree order. (version May 11, 2008)
$vid = 3; // Taxonomy ID for Instrumentation
$treeterms = taxonomy_get_tree($vid, 0, 0); // Gets list of ALL Instrument terms in tree order
$node = node_load(arg(1)); // Gets the number of the current node.
$nodeterms = taxonomy_node_get_terms_by_vocabulary($node->nid, $vid); // Gets Instruments assigned to the current node only.
$node_term_cmp = create_function('$term_1,$term_2', 'return $term_1->tid - $term_2->tid;'); // Callback function for uinstersect.
$iterms = array_uintersect($treeterms, $nodeterms, $node_term_cmp); // Matches terms between the two lists, keeping the tree order.
$links = array();
foreach($iterms as $iterm) {
$links[] = l($iterm->name, taxonomy_term_path($iterm));
}
return '<strong>Instrumentation:</strong> '. implode(', ', $links) .'<br>';
?>
I ended up finding out about and using create_function() to define the callback function. At first I just used function, but for some reason that version of my snippet would only work if I included in the body of a node (otherwise it would complain that the function had already been declared), and I am trying to get this code into a cck field eventually. The above snippet works in a cck text field, but has to be copied and pasted into each node manually which is a pain. I also have it in a block, but the block placement isn't as precise as I'd like it to be. Thanks again for your help in figuring out how to get the terms sorted in tree-order.
Anthony
--
anthonymosakowski.com