25

I have a couple of content types which I need to preprocess in different ways. So template.php in my theme foo currently looks like:

function foo_preprocess_node(&$variables) {
    if ('news' ==$variables['type']) _preprocess_news($variables);
    if ('event'==$variables['type']) _preprocess_event($variables);
    if ('alert'==$variables['type']) _preprocess_alert($variables);
    ...
}

function _preprocess_news(&$variables) {
    ...
}

function _preprocess_event(&$variables) {
    ...
}

function _preprocess_alert(&$variables) {
    ...
}

I'd like to be able to specify a Drupal specific preprocess function which would hook on the content type's machine name. I tried using foo_preprocess_news but it is never called.

Is there a better way?

5 Answers 5

11

The name of the preprocess function is based on the theme name, so for theme_table() your preprocess function is MYTHEME_preprocess_table().

Since there is no theme_node_node_type function a preprocess hook such as foo_preprocess_news or foo_preprocess_node_news won't work out of the box.

You could go about overriding the theme registry so that it behaves differently for nodes, but I really wouldn't recommend it; it could get very messy.

I'm a huge fan of refactoring code but in your case I don't think it's necessary; if you have complex logic that needs to run in your preprocess hook dependent on node type then farming it out to different functions in the way you currently doing seems like good practice to me.

The other method, of course, would be to implement a custom module for each of the different content types and implement hook_preprocess_node() in each. That way, each module's preprocess function can be responsible for a different content type.

However this may be overkill for your situation; if you don't have extra logic (i.e. non theme preprocess logic) to perform on each content type, then this method probably doesn't add any extra value.

5
  • 1
    OK. I could also "automate" the foo_preprocess_node by implementing it as call_user_func('_preprocess_' . $vars['type'], $vars); in order to avoid repeating the ifs but it's probably best to stay simple.
    – cherouvim
    Apr 10, 2012 at 11:05
  • I implemented hook_preprocess_node() on a custom module, and was hoping that would restrict when the hook gets called, but it doesn't. Any way to restrict when the hook gets called by content type?
    – neuquen
    Jun 26, 2015 at 15:52
  • @Keven You can't stop it being invoked if the function exists, but if ($vars['node']->type == 'foo') { ... will achieve the effect you're looking for
    – Clive
    Jun 26, 2015 at 15:53
  • I'm really just looking for slight optimizations, similar to what you can do with hook_block_view_MODULE_DELTA_alter(). I currently do what you say, but wish there was a way to restrict when the hook gets hit.
    – neuquen
    Jun 26, 2015 at 15:55
  • It wouldn't be an optimisation, though @Keven - you'd just be shifting the decision-making process to another part of code. In fact if Drupal offered such an override, it would need to be generic, and would almost certainly take more processor time to accomplish. You're already winning by only making the decision at the last moment
    – Clive
    Jun 26, 2015 at 15:59
32

The zen subtheme accomplishes this by adding this to its theme_preprocess_node function:

function foo_preprocess_node(&$variables, $hook) {
  ...
    // Optionally, run node-type-specific preprocess functions, like
  // foo_preprocess_node_page() or foo_preprocess_node_story().
  $function = __FUNCTION__ . '_' . $variables['node']->type;
  if (function_exists($function)) {
    $function($variables, $hook);
  } 
  ...
}

If you have a content type called 'news', then you would be able to create a function called foo_preprocess_node_news in your template.php file.

1
  • We also use this on ZURB foundation with our own impelementation, very useful code snippet.
    – Blissful
    Jun 29, 2016 at 10:27
3

I only just had a similar issue which is why google brought me to this page: my node preprocess function was growing so enormously big, that i would rather split the function into multiple files.

I've already done a similar approach in my template.php file which contains all the alter functions, and since the same method works perfectly well here, I thought I'd share my approach:

file setup inside the folder MYTHEME/preprocess:

- node.preprocess.inc
- node--blog-post.preprocess.inc
- node--device-variation.preprocess.inc
- (...)

you should already have node.preprocess.inc, the others you can create yourself. how you call them really is rather arbitrary, but better give them names that nicely identify them and fit the whole drupal naming system.
onwards to the content of these files!

node.preprocess.inc, here I'm doing doing something like this:

<?php

function MYTHEME_preprocess_node(&$variables) {

    switch($variables['type']) {

      case 'blog_post':
        // if the type of the node is a Blog Post, include this:
        include 'node--blog-post.preprocess.inc';
        break;

      case 'device_variation':
        // if Device Variation, include this:
        include 'node--device-variation.preprocess.inc';
        break;

      case 'foo':
        // ...
        break;
    }

    // additional stuff for all nodes

}

we basically switch through the type of the current node. what you switch through is up to you; #id, #view_mode, all depending on your exact needs.
once there is a match, it will load the specified file and act upon its content just as if it was written right inside this function.

the content of these included files looks exactly like you'd put it in the node.preprocess.inc file, except we don't call the preprocess function again:

node--device-variation.preprocess.inc

<?php

    // Device Name
    $device = drupal_clean_css_identifier(strtolower($variables['title']));

    // Determine whether only Device Version is of type 'N/A' and set ppvHasVariations accordingly
    $deviceHasVariations = true;
    if( $variables['content']['product:field_model_variation'][0]['#options']['entity']->weight == 0 ) {
        $deviceHasVariations = false;
    }
    //...

you can basically do this with as many files as you want and even cascade multiple switches, for example further splitting up specific node preprocess files depending on the #view_mode, having one file for the full view mode and another for the teaser

hope this helps, should someone ever stumble upon this question again (:

1

call_user_func() doesn't pass parameters by reference. So, in case of $variables your preprocess_foo() functions will work on copies of the original array only; changes on non-objects will not apply during the remaining render process.

1

In your main hook_preprocess_node Implement the following code at the end

$preprocess_function = 'themename_node__' . $node->type . '__preprocess';
if (function_exists($preprocess_function)) {
 $preprocess_function($variables);
}

So you would have now preprocess by node type

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.