Taxonomy_term_count_nodes gives stupid results when child terms have same nodes
The Drupal core function in taxonomy.module, taxonomy_term_count_nodes, has no check in it to remove duplicate nodes, which can happen in situations where a nodde belongs to both a child and a parent. In my opinion there should be a different function taxonomy_term_and_children_count_nodes or a flag in the function to say if nodes belonging to children terms should be counted at all, and if they are, there should be proper checking to remove duplicates.
Here's the Drupal taxonomy.module original, and follows will be my hack with a flag, which doesn't do what I request above yet but does give the option to opt out of counting nodes belonging to children terms:
<?php
/**
* Count the number of published nodes classified by a term.
*
* @param $tid
* The term's ID
*
* @param $type
* The $node->type. If given, taxonomy_term_count_nodes only counts
* nodes of $type that are classified with the term $tid.
*
* @return int
* An integer representing a number of nodes.
* Results are statically cached.
*/
function taxonomy_term_count_nodes($tid, $type = 0) {
static $count;
if (!isset($count[$type])) {
// $type == 0 always evaluates TRUE if $type is a string
if (is_numeric($type)) {
$result = db_query(db_rewrite_sql('SELECT t.tid, COUNT(n.nid) AS c FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 GROUP BY t.tid'));
}
else {
$result = db_query(db_rewrite_sql("SELECT t.tid, COUNT(n.nid) AS c FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 AND n.type = '%s' GROUP BY t.tid"), $type);
}
while ($term = db_fetch_object($result)) {
$count[$type][$term->tid] = $term->c;
}
}
foreach (_taxonomy_term_children($tid) as $c) {
$children_count += taxonomy_term_count_nodes($c, $type);
}
return $count[$type][$tid] + $children_count;
}
?>
And the Agaric opt-out of counting child term nodes version:
<?php
/**
* Count the number of published nodes classified by a term.
*
* @param $tid
* The term's ID
*
* @param $type
* The $node->type. If given, taxonomy_term_count_nodes only counts
* nodes of $type that are classified with the term $tid.
*
* @param $children
* Optional flag to indicate whether to count the nodes of child terms.
* TRUE or FALSE, defaults to TRUE.
*
* @return int
* An integer representing a number of nodes.
* Results are statically cached.
*/
function taxonomy_term_count_nodes($tid, $type = 0, $children = TRUE) {
static $count;
if (!isset($count[$children][$type][$tid])) {
if (is_numeric($type)) {
$result = db_query(db_rewrite_sql('SELECT t.tid, COUNT(n.nid) AS c FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 GROUP BY t.tid'));
}
else {
$result = db_query(db_rewrite_sql("SELECT t.tid, COUNT(n.nid) AS c FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 AND n.type = '%s' GROUP BY t.tid"), $type);
}
while ($term = db_fetch_object($result)) {
$count[$children][$type][$term->tid] = $term->c;
}
}
if ($children) {
foreach (_taxonomy_term_children($tid) as $c) {
$children_count += taxonomy_term_count_nodes($c, $type);
}
return $count[$children][$type][$tid] + $children_count;
}
else { // best practice to use else or not? unnecessary with return above
return $count[$children][$type][$tid]
}
}
?>
While doing this I think I found a bug in the static caching: it checks if $count[$type]
is set, when the query that runs if it is not set, and what is ultimately returnedd, is ddependent not just on the node type but the term tid.
Comments
As used in a contributed
As used in a contributed module, place_taxonomy, to list people (profiles) and actions (another content type) in each country:
Before:
Country/Region People Actions
Argentina 34 6
BENIN 2
After:
Country/Region People Actions
Argentina 8 1
BENIN 1
Since I always have to look
Since I always have to look it up, here's how to apply the patch:
cd /var/www/agaricroot/
sudo wget http://agaricdesign.com/sites/agaricdesign.com/files/taxonomy_term_count_nodes_children_0.patch
sudo patch -p0 < taxonomy_term_count_nodes_children_0.patch
I have better solution
This solution not needed to decide, that I want counts without child, but it's recognize duplicates.
function taxonomy_term_count_nodes($tid, $type = 0) {
// $type == 0 always evaluates TRUE if $type is a string
if (is_numeric($type)) {
$result = db_query(db_rewrite_sql('SELECT t.tid, n.nid FROM {term_node} t INNER JOIN {node} n ON t.vid = n.vid WHERE n.status = 1 AND t.tid = %d'), $tid);
}
else {
$result = db_query(db_rewrite_sql("SELECT t.tid, n.nid AS c FROM {term_node} t INNER JOIN {node} n ON t.vid = n.vid WHERE n.status = 1 AND n.type = '%s' AND t.tid = %d"), $type, $tid);
}
$count = array();
while ($term = db_fetch_object($result)) {
$count[] = $term->nid;
}
foreach (_taxonomy_term_children($tid) as $c) {
$child = _uc_nbb_display_term_count_nodes_best($c, $type);
}
if (is_array($child)) {
return $count = array_merge($count, $child);
}
// Doing until don't have a child
else {
array_unique($count);
return count($count);
}
}
function _taxonomy_term_children($tid) {
$result = db_query('SELECT tid FROM {term_hierarchy} WHERE parent = %d', $tid);
while ($term = db_fetch_object($result)) {
$children[] = $term->tid;
}
return isset($children) ? $children : array();
}
I don't understeand, why Drupal programmers constructs queries thats selects all data from tables and not just that you want as in my example. Is that way have any pros?
Post new comment