Using Views 2, is it possible to display taxonomy listings in a different format/layout per vocabulary?

For instance, I have one vocabulary where it's perfectly fine to display a listing of node teasers when browsing through the taxonomy term pages, the way taxonomy is always displayed by default.
On another vocabulary though, I'd like to display just a few specific fields related to the content type, in a grid layout (for instance, similar to a page like this on Flickr).

I've created a View for this second scenario, with the Path set to media/photos/% ... but no matter what arguments I add to the View (taxonomy term, ID, etc), when I go to the URL media/photos/someterm, or media/photos/12 (term ID), I always just get a teaser listing, and my View is completely ignored. If I try out the taxonomy terms in the preview area in the Views editor, it does work and limits the results correctly. I have Pathauto installed as well, and it is set to map this taxonomy vocabulary to media/photos/[cat-raw] (have also tried [tid]).

For kicks, I tried enabling the default taxonomy_term view that comes with Views, and adjusted it to show just the specific fields I wanted in this particular case instead of node teasers. Works great for that, but it also affects every other taxonomy listing on the site (not good).

I suspect I'm just misunderstanding how to properly set up the argument placeholder in the Path area of the View or something like that... or maybe some aspect of correctly configuring the argument in the View. If anyone could nudge me in the right direction that'd be awesome :) Thanks!

-- David
davidnewkerk.com | absolutecross.com
View my Drupal lessons & guides

Comments

MidGe48’s picture

Hiya David,

If I understand correctly what you are trying to achieve, I think you could use taxonomy and have different views for different terms.

That would be one solution.

Cheers

www.ZuNOB.com

dnewkerk’s picture

Thanks. If I understand your suggestion, I don't think this method would scale very easily. For instance it might work if I only have a limited number of pre-decided terms that would rarely or never change or grow. However in this case the end-user of the site will continuously be adding new terms, and will not have access to make cloned views to support each new term. Over time there could grow to be hundreds of terms. Whatever the method would have to be automated (e.g. the taxonomy argument).

I've made a little progress since the other day. None in the sense of successfully theming taxonomy term listings for this specific vocabulary differently, but I've attempted to approach the issue from a node teaser perspective instead of using fields in Views, which might get me by in at least some circumstances.

What I've done is set up my node-photo.tpl.php like this:

<?php if (!$page) { ?>

This is the teaser

<div class="photo-teaser">
  <?php print $node->field_photo[0]['view'] ?>
  <?php print $title ?>
</div>

<?php }
// The teaser ends here, and now the full node theme begins
else { ?>

This is the full node body... etc
<?php $content ?>

<?php } ?>

So now taxonomy is still outputting node teasers just like it always does by default, but now taxonomy outputs the special teaser (only for Photo nodes) that I've customized to include nothing but the same fields I was trying to display in Views. Since only Photo nodes can use this vocabulary, it's pretty safe that no wrong node types will accidentally appear in the listing. I adjust how things should display using the node templates instead of Views, and use CSS to float and align the photo-teaser divs to make a grid layout. I'm posting my code in case this helps anyone else.

It would still be great to know how to accomplish this with Views to override how taxonomy displays this vocabulary though. It seems like it should work the way I have Views configured, but nothing I try seems to work.

-- David
davidnewkerk.com | absolutecross.com
View my Drupal lessons & guides

ergophobe’s picture

I saw the title of this and had a glimmer of hope.

Sadly, it looks like you've come to the same conclusion that I have. I don't really see how you could change this without a significant revision of the taxonomy or the views system.

In other words, you would need to change taxonomy to have a new parameter in the form /taxonomy/vid/tid or some such. That's not gonig to happen.

Better would be to allow the Views argument to allow some sort of logic as blocks do.

One other possibility would be to create a template to theme /taxonomy/term/% (not sure that's possible) and to use arg(2) to decide which view to invoke, using view_get_views()...

I'll have to look into that one.

nevets’s picture

You might want to check out Taxonomy redirect which lets you set up alternative paths per vocabulary. This allows you to make as many views as you need to handling taxonomy/term/{tid} bit with unique paths. You might make one view that lists teasers, another just titles and use Taxonomy Redirect to point each vocabulary to the desired view.

ergophobe’s picture

The problem with that is

This means the redirect only works for taxonomy links placed by the taxonomy module. It also means if you type the url in to your browser the redirect doesn't work.

That's pretty limiting. Short of the method I used below, as modified with your other suggestions, I couldn't figure out any way to get the vocabulary of a given term and then select the right view based on that.

Of course, in the vastness that is Drupal, I have no doubt that there are eight ways better than the one that I used and I suspect Earl (merlinofchaos) would do this in his sleep with six lines of code, but alas, I'm not Earl!

the site I'm building now is far and away the most complicated thing I've done with drupal and the first time I'm really mucking around with a lot of aspects.

I finally broke down and bought the Pro Drupal book though - it is frickin' awesome and is saving me so much time.

ergophobe’s picture

I haven't done it yet, but i think this should work

1. create a page-taxonomy.tpl.php template. Page template overrides are based on the path, so this assumes that your taxonomy is actually at /taxonomy - not sure how this works with aliases. If I recall right, with views you use the system path even if there are aliases.

2. create a view for each vocabulary.

3. these views can be called with view_get_views() by using the view name.

4. Within the page-taxonomy.tpl.php template, create a switch/if-then to segment by vocabulary


<?php
$default_tid = 3; // whatever you want of course

$tid = is_numeric(arg(2)) ? arg(2) : $default_tid;

$term = taxonomy_get_term($tid); // $term->vid gives the vocab; production site needs some fallbacks for invalid terms.

switch ($term->vid)
{
case 1:
$view = view_get_views('viewname', ...);
.....
break;
}

print views_build_view('page', $view, ...);
//function views_build_view($type, &$view, $args = array(), $use_pager = false, $limit = 0, $page = 0, $offset = 0, $filters = NULL)
[/code]
And so on.

Online version of Advanced help: Views help index | Views online help

Starting Views2 Documentation | groups.drupal.org

Other resources helping me get my head around the problem...

Views 2 theming | drupal.org

Views API
views_get_view offset? | drupal.org
Quick and Dirty Links Page for Drupal | Chris Charabaruk Online
Drupal: How To Create a Taxonomy Terms Overview Page for a Vocabulary | Salt Websites

nevets’s picture

While this is possible, it is considered "bad" to mix presentation with content logic. Using this approach I would at least suggest moving the logic for picking the view and content generation to hook_preprocess_page().

ergophobe’s picture

Well, I'll concede that it's an ugly hack, that's for sure, but I'm not sure I really consider this (assuming I switch it to the hook_preprocess_page method) to be mixing logic and presentation any more than than including a block or not dependent on context, or creating multiple templates for different node types. Or would you also lump those into the same category?

Or do you think this should be handled in a module? That doesn't really make sense to me, because this is about themeing different vocablularies differently, so it's really a theming matter, not logic (in other words, all the data is selected by Views).

If you mean the specific issue of handling the theming logic within .tpl.php file, that I agree with, as in...

I would at least suggest moving the logic for picking the view and content generation to hook_preprocess_page().

That definitely makes sense though and I've changed it over to use that method, as you can see below.

To me this seems like a good solution, but still wrapping my head around it all - if you have a better solution and a few minutes to point in the right direction, I'm all ears (eyes, I guess, since I'm reading this).

ergophobe’s picture

Okay, I did it basically as I said but changed it to use
_preprocess_page() as nevets suggested. I was thinking it was a better way, but really just wanted to get it done before dinner ;-).

As it turns out, hook_preprocess_page() simplifies things and I would have been done even earlier if I had started that way. It's a learning process! The big advantages are
- no need for two page templates, which obviously simplifies sitewide changes in your theme
- no need to actually manually change the $content variable. You simple choose your view and then set your displayl. I've chosen to set it as page, but actually at least for my views, it works fine as default or block too.

A couple of comments
- you will end up with multiple Page Views with the same path (taxonomy/term/%tid). This doesn't seem to be a problem, but for a variety of reasons, trying to do it with blocks has some disadvantages

- there are undoubtedly better ways to do this that are more the "drupal way" but this works. A bit of a hack though.

- I have only included two Views here. In my case a view for a listing of instructors and a default view for other taxonomy terms. Again, you create both of these Views via the Views UI and one of them is pretty much the default taxomony_term view with a path and a couple of filters added. You could have swithc statement and run through any number of views pretty easily.

- this has not been heavily tested. There are only about a dozen nodes of dummy content in the site currently, so there may be issues that arise, but so far it works.

- I hope all the comments in the code don't make it too hard to read - got a bit carried away.


function ultraskier_preprocess_page(&$vars, $hook) {

     // were wanting to theme taxonomy pages with Views so arg(0) has to be 'taxonomy'
     // we make sure that arg(1) is 'term' and arg(2) is a number.
     // This works even for aliased paths. If there's
     // a problem getting the right params, you can look up the system path easily enough
     // just by calling  $sys_path = drupal_get_normal_path($_GET['q']);  
  if (arg(0) == 'taxonomy' && arg(1) == 'term' && is_numeric(arg(2)))
  {
         // - we already know arg(2) is numeric, but we cast it as int as well.
         // - arg(2) returns tid even if /taxonomy/term/% is aliased and there is
         //       no third term in the actual URL (i.e http://site.com/myalias).
         // - according to the arg() documentation in /includes/path.inc, I should probably
         // be using a menu callback here, but this is quick and dirty and works.
    $tid = (int) arg(2);

          // Get the term object b/c we need the vid so we can feed views by vocab.
          // Returns false if not found
    $term = taxonomy_get_term($tid);
         // this will be the argument the view needs
         // Taxonomy ID set as arg in the view. If arg(2) not valid tid, set to empty
    $preview_args = ($term) ? array($term->tid) : array();

        // first test for the vocab ID that goes with your view
    if ($term && $term->vid == 4)
    {
       $view = views_get_view('instructors_list');
    }
       // now go to the default view assuming we have a valid taxonomy term
    elseif ($term->vid)
    {
       $view = views_get_view('taxonomy_term');
    }
    else
    {
       $view = '';
    }

    if (!empty($view))
    {
      $view->set_display('page');
    }
  }
}

916Designs’s picture

This is awesome... I've been researching this problem for about a week... I find no traces of a "drupal way solution" other than taxonomy redirect... can't figure out how to create links that get hooked by it (and not using any sorts of taxonomy menus).

I've also been mucking about with dynamically changing views parameters in the argument handling code for taxonomy/term/% but there wasn't much documentation or sucess.

It seems like this method would let me leave my breadcrumbs alone too! yes! Will try this and report findings.

Using teasers, or the same types of fields for every taxonomy term's view just seems like a huge limitation. Don't know why this isn't solved and well documented yet.

916Designs’s picture

I'm successfully getting another view when I want it... but Drupal is still using the default view taxonomy/term/%

How is creating a $view variable changing anything? From what I can tell, at this point in page load, $variables from template_preprocess_page and get_defined_vars() both do not have $view.

So I'm setting $view but its not changing anything.

Help! Thanks

<?php
function indianchief_preprocess_page(&$variables) {

  if (arg(0) == 'taxonomy' && arg(1) == 'term' && is_numeric(arg(2)))
  {
    $tid = (int) arg(2);
    $term = taxonomy_get_term($tid);

    $preview_args = ($term) ? array($term->tid) : array();

    if ($term && $term->vid == 1 && $term->tid == 1117  )
    {
       $view = views_get_view('dining_list');
       //drupal_set_message('view gotten:<pre>'.print_r($view,TRUE).'</pre>','status',TRUE);
       //drupal_set_message('variables:<pre>'.print_r($variables,TRUE).'</pre>','status',TRUE);
       //drupal_set_message('vars2:<pre>'.print_r(get_defined_vars(),TRUE).'</pre>','status',TRUE);
    }
    elseif ($term->vid)
    {
       $view = views_get_view('taxonomy_term');
    }
    else
    {
       $view = '';
    }

    
    if (!empty($view))
    {
    //drupal_set_message('setting display...','status',TRUE);
      $view->set_display('default');
    }
  }
}

?>

I guess I could always throw away the content of the page and embed my view from here :)

Another option? The hook_views_default_views() in a module. This will have my views sitting in code (harder to update), but if Im in a module I can check the current url for a given tid or vid. See http://views-help.doc.logrus.com/help/views/api-default-views from the man himself.

ergophobe’s picture

In my original version, I simply overwrote the $content variable in the preprocess function. That works too of course, but it's really the brute force way.

So in that case you set $variables['content'] = whatever view output you need. Look at the views_embed_view() function in views.module for some ideas.

That might at least get you up and running until you have time to find a better way.

916Designs’s picture

I got tired of trying to get taxonomy redirect to hook my links, and tired of undocumented views variables you can supposedly mess with in the argument handling.

So I went for the old 301 redirect... If a given term is called 'Hotels' and at a certain depth, I redirect, passing the same tid argument in the url to a secondary view. Note that this could redirect based on vocabulary, term depth, or anything. This second view has a page display with a path called hotels/%, taking a tid argument just like the original taxonomy view.

taxonomy/term/% argument validation:

<?php
$parents = taxonomy_get_parents_all($argument);
$p_count = count($parents);

//other stuff...

else if ( $p_count == 4 ) {
  
  $term = taxonomy_get_term($argument);
  //drupal_set_message('term<pre>'.print_r($term,TRUE).'</pre>','status',FALSE);
  drupal_set_message('got term id ' . $term->tid . ' named ' . $term->title,'status',FALSE);
  
  if ( $term->name == 'Hotels' )
  {
    drupal_goto('hotels/'.$term->tid, NULL, NULL, 301); 
  }

  //redirect to other views here for certain tid's, or don't to let the taxonomy view provide the output
  

  
} /*end if parents = 4 */
return TRUE;

?>

As a side effect of having this view displayed at a different path, the links produced in taxonomy breadcrumb assume they are linking to the same view, which isn't the case. All taxonomy links come out as 'hotels/123' So we need to redirect back to the standard view, unless someone is clicking on a Hotels link again.

Second view's taxonomy tid validation code, to redirect back to the standard taxonomy/term/% :

<?php
  $term = taxonomy_get_term($argument);
  
  if ( $term->name != 'Hotels' )
  {
    drupal_goto('taxonomy/term/'.$term->tid, NULL, NULL, 301); 
  }

return TRUE;

?>


I'm still not sure how all of this will play out with pathauto, but it seems to be working ok. I'm also not sure about SEO... with all of the redirecting. I may choose to leave taxonomy terms out of the sitemap. However I am more confident with this solutions than any others I've found so far,

ergophobe’s picture

Hey Deric,

I don't think taxonomy redirect is a good solution. I did *not* try to integrate that in my solution.

I wouldn't do a 301. That sends the wrong message to users and search engines. In specific, it tells them to delete the "old" URL from the index. I would use either a 302 (better) or simply a rewrite so that it's all transparent, but then you don't have the right arguments.

Anyway, I'm not sure why you need to rewrite. You should just need to get the argument number correct. So if your views path is /hotels/% rather than taxonomy/term/% your argument would be arg(1) rather than arg(2).

So in your preprocess, you can check

if arg(0) == 'hotels' && is_numeric(arg(1)) {do your hotels view}
else {do your default view}

Something like that?

Anonymous’s picture

For some reason I cannot get my views to use more than one view when setting the path to "taxonomy/term/%".

Did you simply enable and close the default views taxonomy? What arguments do you have in your view? Also, this code is going in the template.php file, correct? No matter what I do, I can only get one view to display. Any assistance you can provide me with would be greatly appreciated.

Thank you.

ergophobe’s picture

1. yes, the code is in the template.php file plus in the template itself to actually force output of the view.

2. I didn't understand this question:
>>enable and close the default views taxonomy?

Do you mean clone? No, it's quite different, but that would depend on what you want the view to do. The key thing is that in both cases, the path is declared for the page view as /taxonomy/term/%

dogboy72’s picture

I ran into this problem last year, and decided to use custom content types rather than taxonomies and a cck text field rather than terms. Very flexible to theme. I still use taxonomies, but only for stuff that can use a more generic view.

I'm somewhat of a novice here, but it seems to me that cck has made a lot of taxonomy uses obsolete.

ergophobe’s picture

The view in question does a lot of filtering/selection based on CCK fields, but the vocabulary is a pretty essential part of the view and the site - it's a hierarchical taxonomy by country -> state/province -> city

You just can't build those sorts of relationships with CCK fields.

summit’s picture

Subscribing, interested in taxonomy-views also, greetings, Martijn

Monochrome’s picture

In Drupal 7 we made two views for taxonomy term pages. One of them is at 'taxonomy/term/%' the other is at 'store/term/%'. Used hook_menu_get_item_alter to redirect incoming 'taxonomy/term/%' pages with terms in the 'shopping' vocab to the view that handles 'store/term/%'.

function mymodule_menu_get_item_alter(&$router_item, $path, $original_map) {
  if ($router_item['path'] === 'taxonomy/term/%') {
    $term = taxonomy_term_load($original_map[2]);
    if ($term->vocabulary_machine_name === 'shopping') {
      $store_item = menu_get_item('store/term/'.$original_map[2]);
      $router_item['page_arguments'] = serialize($store_item['page_arguments']);
    }    
  }
}

Alternatively, could have done it using hook_init() that set the router items for the current page to a different one using something like the following

function mymodule_init() {
  $args = arg(NULL, NULL);
  if (count($args) == 3 && $args[0] == 'taxonomy' && $args[1] == 'term') {
    $term = taxonomy_term_load($args[2]);
    if ($term->vocabulary_machine_name === 'shopping') {
      menu_set_item($_GET['q'], menu_get_item('store/term/'.$args[2]));
    }
  }
}

In Drupal 6 the following should work

function mymodule_init() {
  $args = arg(NULL, NULL);
  if (count($args) == 3 && $args[0] == 'taxonomy' && $args[1] == 'term') {
    $term = taxonomy_get_term($args[2]);
    if ($term->vid == SOME_VID_CONSTANT) {
      menu_set_item($_GET['q'], menu_get_item('alt/term/'.$args[2]));
    }
  }
}
nevets’s picture

One can do the same thing with the panels module and no code. Enable the page with the path taxonomy/term/%tid and add a variant for each vocabulary as needed. Views is handy for producing content based on the term.