Nesting Views like a Mecha-Eagle

As you have probably figured out, I love views. I love teasing out little features that are completely buried and obscure. Nesting Views is one of my favorite new travesties; not sure if it’s ready for Drupal for Evil, but it’s certainly not kosher.

If birds can nest, why can’t I?

extreme nesting
image by Benjamin Verdonck, reported by apartmenttherapy.com

Ah nesting season, time to buckle down, collect bits of straw, and raise little Views babies that will eventually grow up and save the world. An anecdote from several weeks ago: I was creating a fairly epic view, if I do say so myself, and it just wasn’t panning out. Somewhere between the 4 relationships and the three sorts my taxonomies were getting mismatched and my images were coming back in the wrong order. Now the taxonomies were a pretty easy fix which I’ll talk about in a later snippet post, but the images were killing me.

The situation was thus: I needed to find the most recent image uploaded for a node, but the images were stored in a related child node rather than in a cck field or something. So I had this awkward situation where I’m sorting on the child node by date, but I want the child node’s image sorted by date the other direction. This just doesn’t work in views, no amount of hook_views_query_alter or sort ordering was working out for me.

But I had a similar view in another area, actually on the child node’s page I was displaying those images in the correct order, was it possible that I could use the child nid and nest a view inside my view? Is this anathema? Surely it must be.

Turns out, this is completely painless and extremely performant! How is this possible Chuck? You’re issuing an extra Views display for every single row in a View! Ah, yes, this is true, but Views and MySQL have excellent caching for simple things and complicated Views that have 4 relationships end up not really being cacheable at all. So comparatively, a nested view is much, much faster.

This is in my views-view-field.tpl.php in my themes folder. Make sure when you add it the first time to rescan your views template files. There’s a button in Views under Theme → Information. After that if you don’t have caching on you shouldn’t need to rescan unless you add/delete more template files.

<?php
$views = array('homepage_recently_updated',
  'node_index',
  'node_index_list');
$vname = $view->name;
if (array_search($view->name, $views) !== FALSE) {
  // Print out the correct image for a node.
  // Finding the field_alias for the correct row is just homework, try running a
  // dpm on $field->field_alias when you save the view and you'll see a list of
  // possibilities. Mine was absurdly named because of the many relationships,
  // I'm sure that yours will be much nicer because you're smarter than I am.
  if ($field->field_alias == "node_node_data_field_journal_node_data_field_scan_field_scan_fid") {
    // My view relies on the nid of the parent node, but this is your standard
    // list of args that you would expect to have in the query string.
    $view_args = array($row->nid);

    // If you know which display_id you're going to be using drop it here and
    // skip the switch later on.
    $display_id = '';

    // Pull down the inner view for display later
    $view_inner = views_get_view('journal_node_primary_image');
    if (!empty($view)) {
      // Select the correct display based on which view called us
      switch ($vname) {
        case 'node_index':
          $display_id = 'block_2';
          break;
        case 'node_index_list':
        case 'homepage_recently_updated':
          $display_id = 'block_4';
          break;
      }

      // Don't use display or execute if you're just wanting to output the View.
      // Those both have extra stuff strapped onto them that may cause problems
      // later on in the Views lifecycle according to the comments in the code.
      print $view_inner->preview($display_id, $view_args);
      return;
    }
  }

So really, all it comes down to is making sure that you’re in the right field_alias, pulling down the view with views_get_view(), figuring out your display id and any arguments, then doing a $view→preview($display_id, $view_args). Not too shabby!