Wednesday 4 July 2012

Changing view_mode mid-stream

In my current contract I have to display a hierarchy of complex taxonomy terms - each one as a page with relevant data hanging off it. There are three levels to the taxonomy: Level 1 displays one set of data and links, Level 2 is never displayed on its own (which is something I'll have to take care of) and Level 3 has a different page structure again.

Different page layouts means Display Suite (http://drupal.org/project/ds) and that's great. It works fine when you want to configure a different layout for a node - as long as it's the same for every node type. Or entity bundle.

DS works fine for taxonomy terms in just the same way as for nodes. It is one of my favourite modules.

But I needed to be able to change the view_mode on the fly: to check what level the taxonomy term is and change the view_mode based on that. Which is when I ran into trouble - and it's not DS's fault. Though I never had to do it apparently this was not a problem in D6 but in D7 there is a bug which makes it tricky to change view_mode. You can read all about it here: http://drupal.org/node/1154382

This issue gives some hints as to the solution (hook_entity_prepare_view() is not it), but there is a link to this blog here. The solution described  is for changing build mode for nodes based on the current theme (so you can change things if you're using a mobile theme).

My solution is the same except slightly more generic, instead of intercepting hook_node_...() hooks, I intercept hook_entity_...() hooks.

I'm just going to throw my code at you because I know you can work out what to do in your own situation.


/**
 * Implements hook_entity_prepare_view().
 *
 * We have to play silly games to change the view_mode, first we intercept
 * hook_entity_prepare_view() and establish what view_mode we want, and
 * save it. But there's a bug which means this has no effect on the output
 * so...
 */
function page_g2g_entity_prepare_view($entities, $entity_type, $langcode) {
  if ($entity_type!='taxonomy_term') {
    return;
  }


  foreach ($entities as $id => $entity) {
    if ($entity->vocabulary_machine_name!='gtg_tags' || $entity->view_mode!='full') {
      // wrong vocabulary or not a "full page"
      continue;
    }


    // Change the display dependent on the number of parents
    switch (count(taxonomy_get_parents_all($entity->tid))) {

      case 1:

        $entity->view_mode = 'g2g_level_1';
        break;

      case 2:

        // Hm. Need to do something else here, maybe
        // do a redirect to the parent term.
        break;

      case 3:

        $entity->view_mode = 'g2g_level_3';
        break;
    }
  }
}


/**
 * Implements hook_entity_view_alter().
 *
 * ...we intercept before the full build is enacted. You can test and see that
 * even though we changed the view_mode in the term itself, it hasn't transferred
 * to the build theme. So having verified we want to do it with this entity
 * we transfer it. And now it gets changed.
 */
function page_g2g_entity_view_alter(&$build) {
  if (isset($build['#entity_type']) && $build['#entity_type']=='taxonomy_term') {
    $build['#view_mode'] = $build['#term']->view_mode;
  }
}


Arguably you don't need the first hook, you could do it all in the second call. But it's a matter of elegance and splitting actions into their appropriate locations.

No comments: