Embedding a View within a node can be done in several ways (e.g., see ViewField module for a very flexible CCK approach, or Insert View module for a tag-based approach). But what I wanted was to embed the same view for every node of a specific type, where the view's filter is context dependent on the node being displayed - e.g., a context sensitive - embedded view - per node-type

Overview: basic concept

Nodes on your site have relationships you'd like to exploit to create sophisticated node displays. For example, say you have "secondary" nodes (say of type 'artwork') that have a reference (e.g., say a nodereference) to a "primary" node (say of type 'artist'). You'd like to display some field(s) from all of the former as a list, table, or grid at the bottom of the later (e.g. display a mini-gallery of artwork images associated with each artist).

To accomplish this, we will create a custom View that selects only secondary-type nodes with a reference to the primary node being displayed. Then we will do some custom theming to display this view at the bottom of every primary node. In this way, the primary node knows nothing about the secondary nodes - it simply displays a custom view that will automatically pull in and theme all of the related secondary nodes. That's pretty cool!

In this article, I'll provide a recipe for this specific example, but I think this basic approach can be adapted to a wide variety of situations.

Requirements:

  • You are using a template theme, and are not averse to a little custom theming.
  • You are using Views and CCK.
  • For my example, you need ImageField and NodeReference CCK types.

For some background info. see:

Step 1: Create your content types

In this example, will use two content types, named "artist" and "artwork". The "artist" node-type can just be a basic page-type node. The "artwork" node-type requires two CCK fields: an ImageField, to hold an image of the artwork, and a NodeReferece field, to provide a reference back to the artist.

There is lots of documentation on creating CCK fields. But, be sure that in the NodeReference Data Settings, you restrict "Content types that can be referenced" to "artist". Another really handy trick, is to further restrict which nodes can be referenced to only those owned by the current user (so users can only reference their own nodes). To do this, create a simple View, named "users-artists", that Filters Node:Type is "artist" and Node:Author is the "currently logged-in user". Now use your new "users-artists" view to set the "Nodes that can be referenced" in the NodeReference field. (I just love the way CCK and Views work together!)

Step 2: Create a View to "pull-in" secondary nodes

  1. Create a new view, called "mini-gallery".
  2. Layout a block view for the "artwork" nodes. I used a "Bonus:Grid" view-type, with 10 images max. to create a 2-row gallery.
  3. Add any static header or footer info. you want displayed with the mini-gallery.
  4. Select the fields to display in the "artwork" mini-gallery. I display the Imagefield Thumbnail and the Title as link (so the user can link back to the artwork node from the mini-gallery).
    Tip: I am using Lightbox2 and ImageCache to create a very nice way to browse through the images directly within the mini-gallery view. So I use "Lightbox2: Thumbnail->original" option for the Imagefield - nice!
  5. Add an Argument Type : Node Reference as "Use Empty Text"
  6. Add "$args[0] = arg(1);" to the Argument Handling code (leave out the quotes).
    That's the magic bit that will make the whole thing "context sensitive".
  7. Filter Published Nodes of Node Type : artwork.
  8. Sort by Last Modified Date so newer images show up first, if you like.

Step 3: Custom theme artist nodes to display the mini-gallery view

  1. Create the file node-artist.tpl.php file in you theme directory by copying node.tpl.php
  2. After the $content div, add the following:
      <div class="mini-gallery">  <?php
    print theme('view', 'mini_gallery', 10, false, 'block', NULL); ?>
      </div>
      

    See How To: Embed a View along with background links above.

And that's it.

Because the view is displayed "embedded" within a node, arg(1) contains the node being displayed. The Argument Handling code saves this into $args[0]. This allows the NodeReference Argument to select only nodes with a reference to the one currently being displayed.

To be quite honest, I'm not 100% sure how this actually works - if someone with the Drupal / PHP knowledge could explain the mechanism at work here, it'd be greatly appreciated, as this appears to be a very powerful and widely applicable technique.

Good luck!

Comments

jfall’s picture

I can add a few extra tid-bits here:

  • I finally understand the Views argument handling code magic: $args[0] = arg(1); The arg() function gets items off the query string (path) from the URL, so when the query is ?q=node/1234 arg(1) returns the node id (1234). $args is the View argument array, so we're setting the first argument value to be the node ID for the "page" being viewed. Voila - the view can now use that node ID as a filter to create the context sensitive magic!
  • I split the theming up now. I put some custom code into template.php:

    function _phptemplate_variables($hook, $vars = array()) {
      switch ($hook) {
      // ...
          case 'node':
          $node = $vars['node'];
    
          $mini_gallery_types = array('page');  // add more node types here
          if (!$vars['teaser'] && in_array($node->type, $mini_gallery_types)) {
            $vars['mini_gallery'] = theme('view', 'mini_gallery', 8, false, 'block', NULL);
            $vars['mini_gallery_title'] = $vars['text_title'] . " Gallery";
            $vars['mini_gallery_description'] = $vars['field_mini_gallery_description'][0]['value'];
          }
      // ...
     }
    }
    

    This allows me to set several variables, including a description which you can see is pulled from a CCK field in the node itself. And this removes the "logic" code from my node.tpl.php (this is the "right way" to do theming):

      <div class="content"><?php print $content?></div>
    
      <?php if ($mini_gallery): ?>
        <a name="gallery"></a>
        <fieldset class="collapsible expanded">
          <legend><b><?php print $mini_gallery_title ?></b></legend>
          <div class="mini-gallery">  
            <?php if ($mini_gallery_description): ?>
              <div class="description"> 
                <?php print $mini_gallery_description ?> 
              </div>
            <?php endif ?> 
            <?php print $mini_gallery ?>
          </div>
        </fieldset>
      <?php endif ?>
    

    Notice that I moved this to node.tpl.php rather than page.tpl.php - a better place I think - and I decided to put the embedded view into a collapsible fieldset, which works nicely.

I should say one of the big advantages I've found using this technique is that my users find it very convenient to update their gallery simply be posting a new photo (or product) - they don't need to actually edit their page, and the View takes care of always showing their latest postings. My users seem to really like the way this works - happy users, happy me ;-)

So glad this helped at least one person - hope others find the updates useful too.

mariagwyn’s picture

I am using Views 2, and I can't find the argument for these steps:
# Add an Argument Type : Node Reference as "Use Empty Text"
# Add "$args[0] = arg(1);" to the Argument Handling code (leave out the quotes).

What I want to do is embed a list of reference nodes using a view "biography" which lists nodes accorded to a CCK Author Profile field. The view works. What I want to do is embed the view on the user profile page, listing their 'authored' nodes. I need it to pull from either the profile url, which reads like this: "site.com/friend/username" or be able to reference the actual page's username somehow.

I have actually been most successful with this code:

  <?php
   print views_embed_view("biography", "page_1", all );
?>

Except: that the argument, "all" is what I would like to pull from the username of the contextual node.

BTW, I love your site, I refer to it frequently and always wonder how you did it. Usually, I can't figure it out!

physicsdude’s picture

Thank you!
I was able to use this method to create a block view of links - each one of which is only shown on a given page (but there are multiple links per page) - without having to put any code into the page tempaltes :)