Preprocessors for each view

greg.harvey - September 3, 2008 - 17:15
Project:Views
Version:6.x-2.0-rc1
Component:Miscellaneous
Category:support request
Priority:normal
Assigned:Unassigned
Status:postponed (maintainer needs more info)
Description

Another question, sorry.

Since there are templates for each view, does that mean there are dynamically generated preprocessors somewhere? I think not, since I can't find any code which looks like it might be doing that. To illustrate my example:

* view named my_first_view
* template is views-view--my-first-view.tpl.php

Following that logic through, one would expect to find (dynamically generated, of course):

<?php
function template_preprocessor_views_view__my_first_view() {
  ...
}
?>

Which, following on again, would imply I can extend that with this in my own module:

<?php
function my_module_preprocessor_views_view__my_first_view(&$variables) {
 
$variables['new_var'] = 'foo';
}
?>

#1

merlinofchaos - September 3, 2008 - 17:21
Status:active» fixed

There aren't dynamically generated preprocess functions; instead, all of the theme functions use the base preprocessor automatically. So views-view--my-first-view.tpl.php still uses template_preprocess_views_view(&$vars) as its preprocessor function.

You can add preprocess functions for specific views with the exact same naming conventions. Note that that it's named *_preprocess_ not preprocessor_.

Note that currently Drupal's design requires that you have the .tpl.php file in order to have the preprocess function (though you could drop in a generic preprocess function and do switches based upon information contained within the $vars['view'] variable. There's an issue for this which is fairly easily found by going to the Drupal queue and searching in the 'theme system' queue.

#2

greg.harvey - September 3, 2008 - 17:53

You can add preprocess functions for specific views with the exact same naming conventions. Note that that it's named *_preprocess_ not preprocessor_.

Sorry to be slow, but I don't want to spend hours scratching my head because I misunderstood you... So you mean I can do this in my module?

<?php
function template_preprocess_views_view__my_view(&$variables) {
 
$variables['my_var'] = 'foo';
}
?>

There's an issue for this which is fairly easily found by going to the Drupal queue and searching in the 'theme system' queue.

I've just been staring at the issue queue for half an hour and I don't see it. =(
It's late, I need my dinner, I'm going home. To be continued tomorrow.

Thanks again for your time!

#3

merlinofchaos - September 3, 2008 - 17:56

You would use your theme name in place of template and as long as you have views-view--my-view.tpl.php yes.

Note that if using linux, another trick you can use is symbolic links so that you can have the same template without copying it, if you only want to change stuff in the preprocess function. =)

#4

greg.harvey - September 3, 2008 - 17:59

Note that if using linux, another trick you can use is symbolic links so that you can have the same template without copying it, if you only want to change stuff in the preprocess function. =)

Ooooo, nice idea! =)

Or junction in Windows. But I've had horrible issues with that in the past. Anyway, the dev and stage servers are Fedora and production server is Redhat, so we're good there.

Thanks *again*!

#5

WorldFallz - September 3, 2008 - 23:27

interesting issue-- just as an fyi the issue merlin is referring is i believe: Themes cannot have a preprocess function without a corresponding .tpl.php file

#6

greg.harvey - September 3, 2008 - 23:34

Thanks WorldFallz. =)

Merlin, FYI, I've blogged a summary of my day researching here:
http://www.drupaler.co.uk/blog/theming-views-drupal-6x/67

If you find five minutes to read it and I've got anything wildly wrong, let me know.

I'm also wondering if this is possible on a module level. My experimentation indicates not, but I find this odd. If themename_preprocess_views_view__my_view works, I would expect modulename_preprocess_views_view__my_view to work too, but it seems this is not the case?

#7

merlinofchaos - September 3, 2008 - 23:42

It can be made to work but only if you use hook_theme() to register a .tpl.php file, which can then be overridden later in the the theme itself. The reason this is necessary is that discovery of the template happens during the theme section, which happens after modules; so the module's preprocess hook isn't looked for because that variant of the theme function does not (yet) exist.

#8

greg.harvey - September 3, 2008 - 23:53

Ahhh, gotcha. Thanks! =)

I'd prefer this in a module because then if I switch theme my custom vars will still be available.

#9

gollyg - September 3, 2008 - 23:48

subscribing

#10

merlinofchaos - September 4, 2008 - 00:00

Greg;

There is one quibble with your article I have:

The Views module is an excellent example of this. There is a preprocess function called template_preprocess_views_view() which is responsible to generating *all* view tpl files. Here are examples of valid view template file names:

Not all view tpl files; only variants of the views_view theme. For example, this won't handle views-view-list.tpl.php. But the basic point is correct: phptemplate_preprocess_views_view_list() will handle all *variants* of views-view-list.tpl.php such as views-view-list--myview--displayid.tpl.php

Also, one mention is that you can pretty safely override views-view-field.tpl.php (an individual field) not as a template. Earlier I mentioned that Views wants everything to be a template but this is one place that I actually fibbed slightly, due to misremembering.

Fields are actually handled by theme function for performance reasons:

<?php
/**
* Display a single views field.
*
* Interesting bits of info:
* $field->field_alias says what the raw value in $row will be. Reach it like
* this: @code { $row->{$field->field_alias} @endcode
*/
function theme_views_view_field($view, $field, $row) {
  return
$field->render($row);
}
?>

However, to make it work automatically when you implement a field template, I include a preprocess function that isn't used unless a template is discovered:

<?php
/**
* Process a single field within a view.
*
* This preprocess function isn't normally run, as a function is used by
* default, for performance. However, by creating a template, this
* preprocess should get picked up.
*/
function template_preprocess_views_view_field(&$vars) {
 
$vars['output'] = $vars['field']->render($vars['row']);
}
?>

For performance reasons, not using a template for individual fields is actually a good idea. Sorry about the misinformation, and thank you for writing that so I realized my error.

(both functions, above, are in theme/theme.inc)

#11

greg.harvey - September 4, 2008 - 00:08

Thanks for reading and I will update it now. =)

I won't bother documenting the additional point about theming fields, as I link to this issue anyway from that article.

#12

greg.harvey - September 4, 2008 - 00:18

Changed to read:

The Views module is an excellent example of this. There are several preprocess functions within the Views module which are generic but have specific purposes. One is called template_preprocess_views_view() and it is responsible for generating *all* view tpl files with filename in the form views-view--my-view-name.tpl.php. Another, template_preprocess_views_view_list(), acts upon all variations of views-view-list--my-view-name.tpl.php, etc. You get the idea.

All possible variations of valid view template file names are demonstrated here, each set of variations having its own default preprocess function provided by the Views module:
http://views.doc.logrus.com/group__views__templates.html

#13

tayzlor - September 4, 2008 - 17:06

Hi Merlin;
following on from your suggestion:

It can be made to work but only if you use hook_theme() to register a .tpl.php file, which can then be overridden later in the the theme itself. The reason this is necessary is that discovery of the template happens during the theme section, which happens after modules; so the module's preprocess hook isn't looked for because that variant of the theme function does not (yet) exist.

i tried setting up the following in my module:

preprocessor function :

<?php

function modulename_preprocess_views_view_fields__viewname(&$variables) {

}
?>

theme function:

function modulename_theme() {
  return array(
    'views_view_fields__VIEWNAME' => array(
    'arguments' => array('view' => NULL, 'fields' => NULL, 'row' => NULL),
    'template' => 'views-view-fields--VIEWNAME',
    ),
}

I also have a tpl file in my theme directory - views-view-fields-VIEWNAME.tpl.php

I tried passing up additional variables from the preprocessor to the template file but it doesnt seem to invoke the preprocessor function at all :(

#14

Anonymous (not verified) - September 18, 2008 - 17:12
Status:fixed» closed

Automatically closed -- issue fixed for two weeks with no activity.

#15

greg.harvey - September 18, 2008 - 19:52
Status:closed» postponed (maintainer needs more info)

Hi,

Setting this active again because of #13. I tried it too and couldn't get it to work. And I can't find a comprehensive guide to this. I would've expected the code in #13 to work, and I did create a hook_theme() too, but nothing. Any pointers? =(

#16

greg.harvey - September 20, 2008 - 09:56

To follow on, printing out the $info array in the theme() function confirms the module preprocess is failing to register:

Array
(
    [template] => views-view-table--insights-authors
    [path] => sites/defaqto.com/themes/defaqto/templates/views
    [arguments] => Array
        (
            [view] =>
            [options] =>
            [rows] =>
            [title] =>
        )

    [original hook] => views_view_table
    [type] => theme_engine
    [theme path] => sites/defaqto.com/themes/defaqto
    [theme paths] => Array
        (
            [0] => sites/defaqto.com/themes/defaqto/templates/views
        )

    [preprocess functions] => Array
        (
            [0] => template_preprocess
            [1] => template_preprocess_views_view_table
            [2] => mytheme_preprocess_views_view_table__insights_authors
        )

)

For this to work I think preprocess functions needs to look like this:

    [preprocess functions] => Array
        (
            [0] => template_preprocess
            [1] => template_preprocess_views_view_table
            [2] => template_preprocess_views_view_table__insights_authors
            [3] => mytheme_preprocess_views_view_table__insights_authors
        )

This is the code, which should work according to other module examples and the docs, but doesn't:

<?php
/**
* Implementation of hook_theme().
*/
function mymodule_theme() {
  return array(
   
'views_view_table__insights_authors' => array (
     
'arguments' => array(),
     
'template' => 'views-view-table--insights-authors',
    ),
  );
}

function
template_preprocess_views_view_table__insights_authors(&$variables) {
 
$variables['foo'] = bar;
}
?>

Even tried this, to try and force our preprocess to stick, and it didn't do anything either:

<?php
/**
* Implementation of hook_theme().
*/
function mymodule_theme() {
  return array(
   
'views_view_table__insights_authors' => array (
     
'arguments' => array(),
     
'template' => 'views-view-table--insights-authors',
     
'preprocess functions' => 'template_preprocess_views_view_table__insights_authors',
     
'override preprocess functions' => true,
    ),
  );
}
?>

#17

merlinofchaos - September 19, 2008 - 17:01
Status:postponed (maintainer needs more info)» active

What if you add an 'original hook' directive in there? I'm not sure that actually works though.

#18

greg.harvey - September 20, 2008 - 09:34

Will try on Monday and report back. =)

#19

greg.harvey - September 22, 2008 - 17:19

Mixed results on this. With the following code:

<?php
/**
* Implementation of hook_theme().
*/
function mymodule_theme() {
  return array(
   
'views_view_table__insights_authors' => array (
     
'arguments' => array(),
     
'template' => 'views-view-table--insights-authors',
     
'original hook' => 'views-view-table--insights-authors',
    ),
  );
}
?>

I get the following $info output from theme():

    [preprocess functions] => Array
        (
            [0] => template_preprocess
            [1] => mymodule_preprocess_views_view_table__insights_authors
        )

As you can see, the Views module's default table preprocess gets dropped. So this seems to work, if I do this in my preprocess:

<?php
function mymodule_preprocess_views_view_table__insights_authors(&$vars) {
 
// invoke the missing Views preprocess before we mess with the vars
 
template_preprocess_views_view_table($vars);
 
$vars['foo'] = 'bar';
?>

Interestingly, if I do this:

<?php
/**
* Implementation of hook_theme().
*/
function mymodule_theme() {
  return array(
   
'views_view_table__insights_authors' => array (
     
'arguments' => array(),
     
'template' => 'views-view-table--insights-authors',
     
'original hook' => 'views-view-table--insights-authors',
     
'preprocess functions' => 'template_preprocess_views_view_table',
    ),
  );
}
?>

Then $info in theme() looks like this:

    [preprocess functions] => Array
        (
            [0] => template_preprocess
            [1] => template_preprocess_views_view_table
            [2] => mymodule_preprocess_views_view_table__insights_authors
        )

Which is almost correct, except the structure of the rows output changes (it's an object instead of an array and the data is raw instead passed through the correct handlers) so I guess this approach misses something somewhere.

Finally, I tried this, since I now know what the structure should be - I hard-wired it in my module:

<?php
/**
* Implementation of hook_theme().
*/
function mymodule_theme() {
  return array(
   
'views_view_table__insights_authors' => array (
     
'arguments' => array(),
     
'template' => 'views-view-table--insights-authors',
     
'original hook' => 'views-view-table--insights-authors',
     
'preprocess functions' => array(
       
0 => 'template_preprocess',
       
1 => 'template_preprocess_views_view_table',
       
2 => 'mymodule_preprocess_views_view_table__insights_authors',
      ),
    ),
  );
}
?>

This approach works fine. I'm not sure why this third approach would result in properly themed fields and the second one would not, but the first and third approaches both seem to work, while the second one works to an extent but for some reason the view output is not "finished".

#20

merlinofchaos - September 23, 2008 - 00:41

Ok, I understand the problem and what's wrong.

This code works:

<?php
function ptest_theme($existing) {
  return array(
   
'views_view__ptest_tracker' => array (
     
'arguments' => array('view' => NULL),
     
'template' => 'views-view--ptest-tracker',
     
'original hook' => 'views_view',
    ),
  );
}
?>

HOWEVER, there is a caveat. It ONLY works if the module weight > views; because it needs the pre-existing views theme function to exist for the 'original hook' directive to work.

In place of it, you can add the preprocess function manually, like Greg did above.

#21

moshe weitzman - September 23, 2008 - 02:58

Theme developer module helps a ton for understanding what .tpl.php file names you can use to customize your output. It also helps you know the variables that get passed into those templates. But knowing the available preprocess functions could be useful. I made a patch for theme developer at #312061: Show which preprocess functions were called on a theme call. Feedback welcome.

#22

greg.harvey - September 23, 2008 - 11:51

Fiddled around with this some more this morning. I find the easiest way to make this work is to have NO 'original hook' at all (the red herring in this was I accidentally used hyphens instead of underscores in my 'original hook', so it wasn't doing anything anyway). This definitively works for me:

hook_theme()

<?php
   
'views_view_table__insights_authors' => array (
     
'arguments' => array(),
     
'template' => 'views-view-table--insights-authors',
    ),
?>

preprocess function

<?php
function mymodule_preprocess_views_view_table__insights_authors(&$vars) {
 
// invoke the missing Views preprocess before we mess with the vars
 
template_preprocess_views_view_table($vars);
 
$vars['foo'] = 'bar';
?>

Whereas trying to use the 'original hook' takes me back to the problem I had with the second approach in #19, the data is unfinished and unthemed.

Also, we tried to mirror this approach with mymodule_preprocess_views_view_fields__view_name() and could not get it to work at all, in any combination. Weird! =(

#23

merlinofchaos - September 23, 2008 - 16:24

Greg: Read #20. I promise you, it works 100% *if* you set the module weight > views.

#24

greg.harvey - September 23, 2008 - 16:30

Ok, will try this properly. I confess I didn't try your module weight tip, because I probably misunderstood this and tried to do the same again:

In place of it, you can add the preprocess function manually, like Greg did above.

It seems this does not work in all cases.

#25

merlinofchaos - September 23, 2008 - 16:35

BTW, I plan to take what this issue is generating and write up a documentation page about it (and putting Views in a module in general) either today or tomorrow. I probably was being too lazy when I said 'above' since you tried a lot of things. The one I was pointing at that seems like it should work reliably if you are unable/unwilling to change the module's weight:

<?php
/**
* Implementation of hook_theme().
*/
function mymodule_theme() {
  return array(
   
'views_view_table__insights_authors' => array (
     
'arguments' => array(),
     
'template' => 'views-view-table--insights-authors',
     
'preprocess functions' => array(
       
'template_preprocess',
       
'template_preprocess_views_view_table',
       
'mymodule_preprocess_views_view_table__insights_authors',
      ),
    ),
  );
}
?>

Note that I removed the erroneous 'original hook' designation. Also I should add that 'arguments' is wrong there too, and that could have unintended consequences (like, theme() shouldn't be able to figure out how to translate the arguments to variables without that).

#26

greg.harvey - September 23, 2008 - 16:52

Sounds great - look forward to reading the page! =)

#27

merlinofchaos - September 29, 2008 - 21:49
Status:active» fixed

This is now documented in the advanced help.

--project followup subject--

Anonymous (not verified) - October 13, 2008 - 21:53

Automatically closed -- issue fixed for two weeks with no activity.

#28

Anonymous (not verified) - October 13, 2008 - 22:02
Status:fixed» closed

Automatically closed -- issue fixed for two weeks with no activity.

#29

moshe weitzman - January 27, 2009 - 22:52

Might be me, but I do not see this in the Advanced help for Views. Looking in HEAD.

#31

greg.harvey - January 30, 2009 - 13:42
Status:closed» postponed (maintainer needs more info)

I'm still having some problems with this - figured I'd post here so the thread continues, rather than starting a new one, but let me know if that's a bad idea...!

Following the docs, my module's weight > than Views' weight, check!

hook_theme() looks like this:

<?php
/**
* Implementation of hook_theme().
*/
function cck_gallery_theme() {
  return array(
   
'views_view_unformatted__gallery_contents' => array (
     
'arguments' => array('view' => NULL, 'options' => NULL, 'rows' => NULL, 'title' => NULL),
     
'template' => 'views-view-unformatted--gallery-contents',
     
'original hook' => 'views_view',
    ),
  );
}
?>

Template is in the root of the module (which seems to be where it's wanted - Drupal complains if it isn't there) and is named 'views-view-unformatted--gallery-contents.tpl.php'

Preprocess simply looks like this:

<?php
function cck_gallery_preprocess_views_view_unformatted__gallery_contents(&$vars) {
 
print_r($vars);
}
?>

Nada! Firefox says "No document" or "No data" or somesuch.

But if I ditch the 'original hook' part everything is fine! WTF?! =(

I mean, it's cool because it works, but clearly I would rather understand why 'original hook' in hook_theme() seems to cause Drupal 6.9 to crash...

#32

momo18 - February 23, 2009 - 18:13

I'm using Organic Groups and I want to customize the default og_ghp_ron view that comes with it. By default the view uses the "unformatted" view style. I changed the style to a table style and all the data I need is in there correctly.

However, essentially what I want to do is pluck out the fields and arrange them however I want in a table. In order to do that properly, I thought I should use a preprocessing function to redefine the field names, so that I could just drop them into a template wherever I want. Some of the fields are CCK fields.

I created a module and defined the preprocessing function there but when I did that the table view got messed up and for example, the dates lost their format and were not expressed the way they should.

I'm not a PHP whiz, so I'm finding it hard to find some concrete examples on what the correct development path to correctly achieve this basic task is.

Should I be creating a preprocess function per view (preferred), or just change the default views preprocess functions (field, unformatted, table, grid, etc.)? If I do, then how do I correctly redefine variable names and format the date (which is getting messed up) through the preprocessing function? How about changing the date format then through a .tpl.php file?

Which style should I be using (table, list, unformatted, grid) to extract the fields and why use one style and not the other?

What's the difference between the two row styles (fields, nodes)?

Some of the template files for example use code in them (such as content-field.tpl.php)? Should they be moved to the preprocess function instead to separate presentation from logic?

Are there any complete examples of calling embedded views from within a .tp.php template file, and how do you call one view from another view? Wouldn't it be easier to write a view like this and customize it in code rather than through an interface?

Unfortunately there aren't any beginning to end examples or best practices to perform common views tasks such as this out there, because I'm sure a lot of people have run into it this issue.

Thanks for any help,
Mo
www.itwrite.com | Having fun with Drupal

 
 

Drupal is a registered trademark of Dries Buytaert.