User login

Theme (lots of) views the Agaric way: Background

Go straight to the useful, how-to information: http://agaricdesign.com/note/how-theme-lots-views-agaric-way

Condensing the wisdom of the wizard

Looking at the code produced by the Views Theme Wizard you can see (especially after being told this at a DrupalCamp in NYC a year or so ago) that the code is essentially the same. Clearly, it could be consolidated and the right template still get called for each view you want to theme.

Before doing this Agaric wanted to confirm how exactly views new to use a template at all for a given view. In order for phptemplate_views_view_list_make_me_pretty to be called and give you a chance to customize the presentation of view 'make_me_pretty', somewhere there had to be a theme_views_view... function of some sort for it to override.

After tracing theme_views_view_list() backwards through some internal functions hidden in views_cache.inc (view_list led me eventually to _views_get_query, which led back to views.module and searched back for theme('views_view ...

Finally, to theme_views_view($view, $type, $nodes, ...) but that still doesn't seem to have a place for the callback with 'make_me_pretty' to have its say. Oh, there it is.

Here's the section of views.module's theme_views_view that matters to phptemplate override. Above this the header is added (modifiable with hook_views_pre_view(), by the way and any exposed filters, via the overridable theme_views_display_filters.)

<?php
  $plugins = _views_get_style_plugins();
  $view_type = ($type == 'block') ? $view->block_type : $view->page_type;
  if ($num_nodes || $plugins[$view_type]['even_empty']) {
    if ($level !== NULL) {
      $output .= "<div class='view-summary ". views_css_safe('view-summary-'. $view->name) ."'>". views_theme($plugins[$view_type]['summary_theme'], $view, $type, $level, $nodes, $args) . '</div>';
    }
    else {
      $output .= "<div class='view-content ". views_css_safe('view-content-'. $view->name) ."'>". views_theme($plugins[$view_type]['theme'], $view, $nodes, $type) . '</div>';
    }
    $output .= views_get_textarea($view, $type, 'footer');

    if ($type == 'block' && $view->block_more && $num_nodes >= $view->nodes_per_block) {
      $output .= theme('views_more', $view->real_url);
    }
  }
  else {
    $output .= views_get_textarea($view, $type, 'empty');
  }
?>

The magic happens in the lines that call views_theme, such as the more commonly called views_theme($plugins[$view_type]['theme'], $view, $nodes, $type). Here it is, in all its elegant glory:

<?php
/**
 * Easily theme any item to a view.
 * @param $function
 *   The name of the function to call.
 * @param $view
 *   The view being themed.
 */
function views_theme() {
  $args = func_get_args();
  $function = array_shift($args);
  $view = $args[0];

  if (!($func = theme_get_function($function . "_" . $view->name))) {
    $func = theme_get_function($function);
  }

  if ($func) {
    return call_user_func_array($func, $args);
  }
}
?>

You can see what's being handed in by $plugins[$view_type]['theme'] by checking out the ones views provides itself:

<?php
/**
 * Default Views style plugins. Implementation of hook_views_style_plugins()
 */
function views_views_style_plugins() {
  return array(
    'list' => array(
      'name' => t('List View'),
      'theme' => 'views_view_list',
      'validate' => 'views_ui_plugin_validate_list',
      'needs_fields' => true,
      'weight' => -10,
    ),
...
}
?>

So, breaking this down, what is handed to views_theme (by the curious route of theme('views_view' ...)<code> calling <code>theme_views_view – that's the Drupal way – which then calls views_theme for the key part – that's the views way) is the following:

views_theme($plugins[$view_type]['theme'], $view, $nodes, $type)

$plugins[$view_type]['theme'] equals

views_view_list<code> for a list view.
The $view object we know and love.

$nodes, as <code>views_theme('views_view', $view, $type, $items, $info['level'], $args);

is called in views_build_view and the $items argument is renamed $nodes ... uh... in theme_views. But we're at views_theme here already!

The above is a bit confused. Now we've got it though.

So:

  1. views_build_view calls theme_views with the arguments 'views_view', the $view object, the $type ('page', 'block', etc.), the $items (result of fetch_object interation over the result of the query), the level, and an argument for arguments.
  2. $function = array_shift($args), which means it is set to 'views_view' and 'views_view' is removed from the arguments (note that theme_views takes no named arguments, but rather sets $args equal to all the arguments passed to it).
  3. The code, modeled directly on Drupal core's theme(), is a little strange with the ! (not) test first, but in effect theme_views will first try 'views_view' plus the view name. This means we could override EVERYTHING about how a view 'make_me_pretty' is displayed (ignoring completely if it is a list, table, teasers, grid, calc, whatever) by creating a phptemplate_make_me_pretty or mythemename_make_me_pretty function in our theme's template.php. Note that this means you would have to handle everything about presenting a view. This works through theme_get_function (which apparently is renamed in Drupal 6).*
  4. Only mad dogs and Englishmen would take over theming a view at that point. So in the typical case, there's no function by that name and the call to views_theme<code> becomes a call to <code>theme_views_view. Now we're back on the trail we were on, and after doing some work on the views surroundings (header etc.) theme_views_view will itself call views_theme, this time with the parameters (ok, arguments) 'views_view_list' (for the typical list view you're most likely to theme), the view object, the items (nodes), and the type, which will all be passed along by...
  5. views_theme this time first tries phptemplate_views_view_list_make_me_pretty, and if that's not there it falls back on 'theme_views_view_list' (technically its trying mytheme_ prefix first, and going to views_view_list after trying theme_views_view_list_make_me_pretty which we know isn't there!**).

There's no doubt we're going to create a common, refactored function for theming list views by calling a view template 'views-list-make_me_pretty.tpl.php', rather than having virtually identical output provided by Views Theme Wizard in our template.php a half-dozen or more times.

Now the question is the relative speeds of (from probably fastest to slowest) defining functions to call the common, refactored function:

  • in plain code each phptemplate_views_view_list_...
  • dynamic functions generated with an array of views we're providing templates for, manually provided
  • dynamic functions generated with an array of views, read from the files in the themes directory directly to see which templates have been made
  • taking over theme_views_view_list to check for an existing template (....tpl.php) every time

CORRECTION: There's no such thing as dynamic functions of the kind proposed. There are variable functions, but this means functions can be called with a variable-- not defined with one.

Going to have to do benchmarks, searches aren't bringing anything up (dynamic functions slow, php check if file exists speed, php variable functions slow, php variable functions speed). The easiest option may not be that slow after all though: According to file_exists page on php.net, "Note: The results of this function are cached."

* Please note that I would give a kidney to have theme_get_function additionally look, last, in a global module namespace of some kind. Then I could create a view in my module and theme it exactly as I want without creating a whole new view type. Maybe the new hook_theme in Drupal 6 will allow this.

** Actually, because of views' special system, it is possible to declare a function called theme_views_view_list_my_module_custom_view and it will work. Cool.

Resolution

Searched words: 
theme view with template condense views theming wizard output

Comments

Post new comment

The content of this field is kept private and will not be shown publicly.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • You can use Markdown syntax to format and style the text. Also see Markdown Extra for tables, footnotes, and more.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img> <blockquote> <small> <h2> <h3> <h4> <h5> <h6> <sub> <sup> <p> <br> <strike> <table> <tr> <td> <thead> <th> <tbody> <tt> <output>
  • Lines and paragraphs break automatically.

More information about formatting options

By submitting this form, you accept the Mollom privacy policy.