The Taxonomy hierarchy depth Judo Throw

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
jefkin's picture

Hi,

This bit applies to drupal 6, but possibly to later drupals as well,

More than once, clients have been upset at me, that clicking on a parent term for a taxonomy hierarchy wouldn't search it's children. See it's always the developer's fault, never the client's expectations.

Well the Hierarchy I'm working with for our current client is a Province / Canton / Township hierarchy. And boy do they want to be able to search the entire Province via the Taxonomy.

In our case, the Province level contains the Canton levels, and they contain the Township levels. So everything is in this 3 tiered setup

I created a small bit of Drupal Judo to make it happen. how?

First of all the underlying core functionality I want to modify is the:

/taxonomy/term/#tid

on my client's site,

/taxonomy/term/315 was returning 'No results found', because the search items were in sub-levels, not in the top 'Province' level.

Taxonomy, which, though it is a core module, isn't the best documented bit of core, has already done most of the work for you.

The core defines the menu entries.

modules/taxonomy/taxonomy.module

<?php
...
153   $items['taxonomy/term/%'] = array(
154     'title' => 'Taxonomy term',
155     'page callback' => 'taxonomy_term_page',
156     'page arguments' => array(2),
157     'access arguments' => array('access content'),
158     'type' => MENU_CALLBACK,
159     'file' => 'taxonomy.pages.inc',
160   );
...
?>

Which tells us enough to find our judo maneuver victim, so in core we find our function:

modules/taxonomy/taxonomy.page.inc

<?php
...
/**
10  * Menu callback; displays all nodes associated with a term.
11  */
12 function taxonomy_term_page($str_tids = '', $depth = 0, $op = 'page') {
13   $terms = taxonomy_terms_parse_string($str_tids);
14   if ($terms['operator'] != 'and' && $terms['operator'] != 'or') {
15     drupal_not_found();
...
?>

As you can see, it provides a handling of a secondary argument beyond the Taxonomy term tids, for depth.

Being an experimenter can be a help sometimes, so on my browser, I add '/2' to the end of the search url, which is not perceiving the depths.

So, /taxonomy/term/315 becomes /taxonomy/term/315/2

And we get the results we're looking for, both Canton and Township level entries now show up.

So I need to get in front of the call to that function taxonomy_term_page(), with a bit of code, we can do it nicely.

PCT_hierarchy.module

<?php
...
177 /*
178 ** PCT_hierarcy_menu_alter() -- hook_menu_alter: Allows us to override the
179 **                              handling of menu items, even those provided
180 **    by other modules, or even core.
181 **
182 **  @param assoc $items - reference to Drupal menu items.
183 **  @return null
184 **  @side-effects - Can signifigantly alter menu callbacks and other drupal
185 **                  menu functions.
186 */
187 function PCT_hierarchy_menu_alter(&$items)
188 {
189   // here we hijack the default core handling of taxonomy/term/#TID by
190   //  A) pointing to a new callback function name
191   //  B) pointing our file name at our own file
192   //  C) Resetting our file directory path to this module's instead taxonomy
193   //
194   $callback = 'PCT_hierarchy_term_page';
195   $file     = 'PCT_hierarchy.term.page.inc';
196   $path     = drupal_get_path('module', 'PCT_hierarchy');
197
198   $items
['taxonomy/term/%']['page callback']  = $callback;
199   $items['taxonomy/term/%']['file']           = $file;
200   $items['taxonomy/term/%']['file path']      = $path;
201 }
...
?>

What are we doing here? We're stealing the thunder of a core module, Pulling the sheet right out from under it, so we can intercept those arguments passed in.

That way we can add the depth we need for our taxonomy.

PCT_hierarchy.term.page.inc

<?php
...
/*
10 ** PCT_hierarchy_term_page() -- Menu callback for the taxonomy module
11 **                              hijacking.
12 ** 
13 */
14 function PCT_hierarchy_term_page($str_tids = '', $depth = 0, $op = 'page')
15 {
16   // technically we probably don't need this, but for clarity, so that you,
17   // dear reader, know that we are piggy-backing off of the core taxonomy
18   // module.
19   //
20   module_load_include('module', 'taxonomy', 'taxonomy');
21
22   $terms 
= taxonomy_terms_parse_string($str_tids);
23
24  
if ($tids = @$terms['tids']) // if we understand the data
25   {
26     if ($tid = array_shift($tids)) // get first (any) term
27     {
28       if ($term = taxonomy_get_term($tid)) // load the term
29       {
30         if ($vid  = @$term->vid) // term vid
31         {
32           if (_PCT_hierarchy_location() == $vid) // our location vid
33           {
34             $depth += 2// Bump our depth up to get children
35           }
36         }
37       }
38     }
39   }
40   // Returning control to our poor innocent victim module, who won't suspect
41   // a thing!  Here, we must include the pages location.
42   //
43   module_load_include('inc', 'taxonomy', 'taxonomy.pages');
44
45  
return taxonomy_term_page($str_tids, $depth, $op);
46 }
...
?>

The only function I'm not explaining is the call to _PCT_hierarchy_location() which is the internal function our module is using, which is just wrapping a call to variable_get().

And there, like judo, we're using the strength of our opponent against him, by passing control back to the 'real' underlying Taxonomy function: taxonomy_term_page. He (core taxonomy) doesn't even know he's not being asked for /taxonomy/term/315/2.

Hope this can help make you and your clients happy too.

Jeff

Comments

thanks

bennash's picture

Thanks, this is very helpful!