I'll start this off with a quick caveat that this is something really simple; that if you're accustomed to Drupal then you'll think, "of course, you don't need to blog this".  However being fairly new to working with Drupal, this caught me out for too long, so if this helps just one other person I'll be happy!

The scenario was a fairly simple one, I have a page that isn't powered directly by any data in the system but a MENU_CALLBACK function that needs to output a custom HTML template to integrate a popup with no header, footer and specifically no Drupal javascript.  I wanted to use templates and not just spit it out in code for a more integrated solution.

So I had the module menu:

function products_menu() {
    $items = array();

    $items['products/%/%/360'] = array(
        'page callback' => 'products_360_view',
        'page arguments' => array(1, 2),
        'type' => MENU_CALLBACK

    return $items;

And the appropriate callback:

function products_360_view($c, $p) {
    // Stuff happens in here
    // Variables have been changed to protect the innocent

So all was good, I just needed to get the right templates being used, as these had no internal nodes to hook into and the URL is dynamic this makes using the normal theme_hook_suggestions invalid, as they will generate template names based on the URL.  theme_hook_suggestions are Drupal's method for resolving the current thing being rendered to a template file, the Drupal site talks about it here: https://www.drupal.org/node/1089656.

We know that the HOOK_menu allows us to define a URL alias to function map and that that function will, by default, go through the usual Drupal rendering system.  This system will for each component being rendered use a theme_hook_suggestion map that defines which template files to use to render that component.  We just need to get our new ones into the theme_hook_suggestions and this is done in the THEME_preprocess_HOOK functions, so I created the following template files:

  • html--products--products-360.tpl.php
  • page--products--products-360.tpl.php

Followed by the preprocess page hook:

function products_preprocess_page(&$variables, $hook) {
    if (/*I'm on the 360 product page*/) {
        $variables['theme_hook_suggestions'][] = 'page__products__products_360';

What we're saying here is that, when rendering page and the URL is going to be handled by my MENU_CALLBACK then add my template to the list.  Note that the underscores are changed to hyphens before looking for template files.  Now, if we reference this page https://www.drupal.org/node/171194 it shows how the template files are used for the different layers of the template system.  Notice how page is a template layer, it's the relationship between the hooks and the template layers that I missed!  THEME_preprocess_page() is called before page is rendered, likewise THEME_preprocess_region() will exist to be called before a region is rendered.  So to set a theme_hook_suggestions for the HTML, you guessed it THEME_preprocess_html().

function products_preprocess_html(&$variables, $hook) {
    if (/*I'm on the 360 product page*/) {
        $variables['theme_hook_suggestions'][] = 'html__products__products_360';

And now we have a custom html layout for this one page!  It all makes sense now I've done it of course and if this saved you time, I'm glad!


No comments yet, be the first to comment.