Hi,

I needed a way to count nodes per term on a system with multiple language (http://drupal.org/project/i18n) and domain access (http://drupal.org/project/domain)

For example,
- domain1.com/english: nodecount for tid 12 = 123
- domain1.com/french: nodecount for tid 12 = 619
- domain2.com/german: nodecount for tid 12 = 234

For this purpose, I took the term_node_count module and reworked it.

I'm attaching the new module, which I renamed to "mt_term_count" for personal purposes. Maybe someone else can use this as well!

I'll walk through the most important changes below (mostly MySQL queries)

Comments

Anonymous’s picture

1. Changes in term_node_count.install

Schema
a. The schema no includes columns for "gid" (domain_id) and "language" (node language via locale module)
b. The primary key now contains 3 items: 'primary key' => array('tid', 'gid', 'language')

First install: pre-filling the count table
a. adapted the query to cycle through domains & integrate with language

	// first we'll count all the number of published, current revisions for each term that has at least one
	$sql = 'SELECT domain_id FROM {domain}';
	$result = db_query($sql);
	while ($row = db_fetch_object($result)) {
	  $sql = 'INSERT INTO {mt_node_count} (tid, gid, language, node_count) (SELECT tn.tid, da.gid, n.language, COUNT(tn.tid) FROM {term_node} tn LEFT JOIN {node} n ON tn.nid=n.nid LEFT JOIN {domain_access} da ON n.nid=da.nid WHERE n.status=1 AND da.realm="domain_id" AND da.gid=%d GROUP BY tn.tid)';
    db_query($sql, $row->domain_id);  
	}

This code correctly filles the table with counts per domain/language combination, e.g. 0-en, 0-fr, 2-en, 2-de etc., for each term tid.

Anonymous’s picture

2. Changes in term_node_count.module

a. The function term_node_count_update needs two extra arguments, $node->language and $node->domains, which are already available in _nodeapi hook.
So change to all function calls to term_node_count_update($tids_to_update, $node->language, $node->domains);

b. That function then cycles through each given $node->domains as $gid (domain_id) and $node->language.

We use a query of the type INSERT INTO ... ON DUPLICATE KEY UPDATE .... Thanks to the primary key triplet (gid, language, tid), we can safely update the counts correctly. Using only 1 query.

/**
 * Updates terms in mt_node_count given an array of tids.
 */
function mt_node_count_update($tids_to_update, $language, $domains) {
	// go through each tid provided, count the number of nodes attached
	// to it and update the mt_node_count table
	if (!empty($tids_to_update)) {
		$sql = 'INSERT INTO mt_node_count (tid, language, gid, node_count) VALUES (%d, "%s", %d, %d) ON DUPLICATE KEY UPDATE node_count=%d';
		foreach ($domains as $gid => $domain) {
      $counts = mt_node_count_count_nodes($tids_to_update, $language, $gid);
		  foreach ($counts as $tid => $count) {
        db_query($sql, $tid, $language, $gid, $count, $count);
      }		  
		}
	}
}

c. The function mt_node_count_count_nodes also needs two extra arguments, the only change we make is the query.The new query counts by language & given domain.

		$sql = "SELECT tn.tid AS tid, COUNT(tn.tid) AS count FROM {term_node} tn LEFT JOIN {node} n ON tn.nid=n.nid LEFT JOIN {domain_access} da ON n.nid=da.nid WHERE n.status=1 AND tn.tid IN (%s) AND da.realm='domain_id' AND n.language = '%s' AND da.gid = %d GROUP BY tn.tid";
		$result = db_query($sql, $tids_list, $language, $gid);

What is powerful about above changes, it works from any source domain, and any source language: the counts are always updated correctly.

Anonymous’s picture

3. Changes in term_node_count.views.inc

a. We want to filter our views by domain counts, so we simply add this (depends on domain.module):

  $data['mt_node_count']['gid'] = array(
    'title' => t('Term Node Count Domain ID'),
    'help' => t('The unique id key for the active domain.'),
    'field' => array(
      'click sortable' => TRUE,
      'handler' => 'domain_views_handler_field_domain_id',
    ),
    // Information for accepting a domain_id as a filter
    'filter' => array(
      'handler' => 'domain_views_handler_filter_domain_domain_id',
    ),
    // Information for sorting on a domain_id.
    'sort' => array(
      'handler' => 'views_handler_sort',
    ),
  );

-------------

Felt like writing up my changes, for others to learn from, and perhaps all of this can be an optional admin.setting for the term_node_count module.

jp.stacey’s picture

Issue tags: +i18n, +languages, +multidomain

Hi morningtime,

This is good work. I think it'd be more useful for people if it were in patch format - especially as it's so few changes. A patch also summarizes everything you've put in above, in a format people can apply to their own copies of the module.

To do this, I've (a) changed your function names to term_node_count_* to avoid lots of erroneous patch lines and (b) rather reluctantly repeated the incorrect indentation of the original module. That way if a new version of tnc is released, then this patch will hopefully (almost) still apply cleanly.

Please find a patch attached, that should contain all relevant changes. I'm not 100% convinced it's getting its totals right yet, but ultimately the patch format means I can go back and look at what might be going wrong!

(This work was sponsored by Torchbox.)

jp.stacey’s picture

Patch attached.

jp.stacey’s picture

I can't get the node counting to work properly using this forked module as it stands (patch in #863460-5: Domain Access & i18n language specific counts.) There are a few bugs, including: it won't return the right stats when different revisions have different taxonomy terms; it doesn't count nodes for domain X which are sent to all affiliates and hence should count for domain Y too.

The following patch does the counting correctly, but it leads to duplicate entries in taxonomy-based views, one for each language and one for "language non-specific" (taxonomy-based views don't have any content negotiation argument in the same way that node views do.)

The only way I can see of taking the duplicates out is to remove i18n support and just do domains. I'll upload another patch shortly which hopefully tries this.

jp.stacey’s picture

Here's another patch, omitting i18n. This seems to work fine purely for domains. i18n is going to be trickier as it needs a views filter for taxonomies equivalent to Content Language Negotiation.

yngens’s picture

subscribe

jp.stacey’s picture

The original patch didn't take into account the fact that domain_access has duplicate records for nodes which are shared to all affiliates (i.e. across all domains). This was returning incorrect counts.

New patch attached.

jp.stacey’s picture

Another bug to fix: sometimes, the root (ID=0) domain is submitted as ID=-1 during node save: patch re-rolled and attached.

jp.stacey’s picture

Re-rolled patch attached; the lack of initialization of $saved_tids in term_node_cound_nodeapi can sometimes lead to "unsupported operand" errors in the 'update' phase

(It's not 100% clear to us how 'update' happens without 'presave' beforehand, but it could be something to do with our client site's rules configuration.)