in Drupal

Theming Custom Entities in Drupal 7

One of the most useful features of Drupal is the ability to define one’s own content types and field types. Prior to Drupal 7 one could create custom content types (limited to Title and Body fields) without using any third-party modules, or add custom fields to existing content types using the Content Construction Kit (CCK) module. In Drupal 7 most of the features of the CCK were moved to the Drupal Core so it is even easier to create custom content types and add an arbitrary number and arrangement of predefined as well as custom fields to an entity.

Drupal 7 also saw the introduction of a new concept in custom content types: entities. Through this new API, Drupal 7 allows you to define a completely custom type of data object or entity which can have its own menu paths, callback handlers, and access, display and storage rules.

Entities are a powerful addition to Drupal’s repretoire of content creation and management tools. However, because the concept is so new, it can be a little tricky to find documentation about the nitty gritty details of Entities. One such problem I encountered recently was figuring out how to theme custom entities.

I expected to be able to simply create a template file named {entity_type}.tpl.php following the same format as node.tpl.php or page.tpl.php but that did not work. I was able to create an alternate page template specific to my entity type in the format page–{entity_type).tpl.php. The problem with this approach, however, is that it requires reconstructing the entire page just to theme the entity output. This isn’t exactly what I wanted. What I want to do is create a custom theme for the entity output only.

I found the solution by digging through the Drupal source code. I finally came upon the ctools_node_content_render_node function in Ctools node_content.inc.

Custom entities are defined via a Drupal module. Since this article is specifically focused on theming the output of custom entities and not how to create entities, I will not go into details. You can find a detailed explanation and step-by-step tutorial on creating entities in “Drupal 7 Module Development” by Pakt Publishing.

There are three pieces, or more specifically functions, that are required to theme the output of a custom entity.

MODULE_menu() hook

In order to access our entities, Menu paths must be created for tasks such as adding, editing, viewing and deleting entities. Adding, Editing and Deleting entities are related to Drupal’s Forms API and beyond the scope of this article. While these other menu definitions are required, we will focus solely on the menu definition for the ‘view’ task.

The menu hook allows us to define the path necessary to view a particular entity, the callback function that handles the request, the page title and the access requirements (among others).

In the code below, pay particular attention to the page callback key/value pair. This entry in the array tells Drupal that requests for http://mysite.com/my_entity/$id should be handled by the function entity_page_view. Drupal will simply hand the request off to this function.

/**
 * Defines the menu callback for viewing our entity.
 * Note that "page callback" will point
 * to a function name in our module. When a user requests
 * the "entity/$id" path (e.g., http://mysite.com/entity/1),
 * this callback will be executed.
 */
function entity_menu() {
  $items['entity/%entity'] = array(
    'title callback'   => 'entity_page_title',
    'title arguments'  => array(1),
    'page callback'    => 'entity_page_view',
    'page arguments'   => array(1),
    'access arguments' => array('view entitys'),
    'type'             => MENU_CALLBACK,
  );
  return $items;
}

Page Callback

In the entity_menu() hook above, we indicated that requests to view an individual entity will be handled by a callback named entity_page_view. Now we need to define that callback. The code below is pretty simple and most of what you see is comments that explain what each section of the code does.

The code takes the entity being viewed as the first argument and the view mode (full or teaser) as the second. Drupal will pass the view mode arguement and you can use its value to make decisions about what to display and how to display it but for now we will ignore it for the sake of simplicity. What we really want to understand is how to tell Drupal to use a custom template file to theme the output of the entity.

The first section of the code sets the $entity->content property to an empty array to clear out any previously rendered content. This property is where the what gets rendered in the template will be stored. In other words, this is the Render Array that Drupal will use to build the output of the entity.

In the middle section of the code, we use Drupal’s hooks to add any custom fields to the entity. When you add fields to an entity through the Admin GUI, this is the code that retrieves the field definitions and their values and attaches them to the entity for display. During this step, the output for the fields is rendered and stored in the #markup property of the field. It is possible to create custom templates for the output of each field but an explanation of how to do so is beyond the scope of this article.

And finally, in the last section of the code, we tell Drupal which themplate file to use to render the output of the entity. By default, Drupal will use the BLOCK template to theme the output. We need to over-ride the default and point drupal to the name of our custom template. It is not necessary to indicate the full path or full name of the file. Drupal can figure that out on its own. We simply point to the theme in the Theme Registry we wish to use.

It is important to note that the order in which these tasks are performed appears to be important. The fields must be rendered and attached to the entity before the theme is specified.

/**
 * This is the callback we defined to be executed when a user
 * requests http://mysite.com/entity/1 (1 is just an example ID,
 * it could be anything). This function will set up the data and
 * prepare the render array(s). You will specify the template to
 * use in this callback. The critical thing to note below is the
 * order in which field_attach_prepare_view, entity_prepare_view
 * and field_attach_view are called. These functions must be
 * called in this order and they must be called before
 * you specify which theme to use.
 */
function entity_page_view($entity, $view_mode='full') {

  /*
   * Remove previously built content, if exists
   */
  $entity->content = array();

  $title = filter_xss($entity->title);

  /*
   * Build the fields content
   */

  field_attach_prepare_view(
    'entity',
    array($entity->aid => $entity),
    $view_mode
  );

  entity_prepare_view('entity', array($entity->aid => $entity));

  $entity->content += field_attach_view(
      'entity',
      $entity, $view_mode
  );

  /*
   * Specify the theme to use and set the #element. Note that the
   * key you use to pass the entity object must match the key you
   * set in in the variables in entity_theme(). So in the case below,
   * we use the key named #element because in entity_theme() we set
   * the following code:
   *
   * array(
   *   'entity' => array(
   *     'variables' => array('element' => null),
   *     'template' => 'entity'
   *   ),
   * );
   */
  $entity->content += array(
    '#theme'     => 'entity',
    '#element'   => $entity,
    '#view_mode' => 'full',
    '#language'  => NULL,
  );

  return $entity->content;
}

MODULE_theme() Hook

So far we have defined the menu item path and callback for our entity. The only two pieces remaining are to create an entry in the Theme Registry which points to our template, then to create the template file.

The function below implements the MODULE_theme() hook to create this module’s Theme Registry entries. The array that is returned has as its keys, the name of the Theme Registry entry, which must match the value specified in:

$entity->content += array(
  '#theme' => 'entity'
);
/**
 * Adds our theme specificiations to the Theme Registry.
 */
function entity_theme($existing, $type, $theme, $path) {
  return array(
    'entity' => array(
      'variables' => array('element' => null),
      'template' => 'entity'
    ),
  );
}

So the #theme key in $entity->content points to the Theme Registry entry. The template key in the Theme Registry entry points to the name of the actual template file. The value of this key should be set to the {name}.tpl.php portion of the template file. It is simply the name of the file minus the .tpl.php extension.

After adding the code above, be sure to empty all caches and rebuild the Theme Registry. I use Devel to do this very easily from a sidebar link.

The Template File

Now all that remains is creating the template file. Just create a new file in /sites/all/themes/your_theme/{entity_type}.tpl.php. When someone requests http://yoursite.com/entity/$id, Drupal will use your template to render the output. The code below is some sample code from the entity theme but it can be whatever you want it to be.

<?php
    $content = $element->content;
    $aid = isset($element->aid) ? $aid = $element->aid : "" ;
?>
<?php if (user_is_logged_in()) : ?>
    <p style="float: right;">
      <a href="?q={entity_name}/<?php echo $aid; ?>/edit">Edit</a>
    </p>
<?php endif; ?>
<?php echo render($content['title']); ?>
<p class="meta">
    <?php echo render($content['field_date']); ?><br />
    <?php echo render($content['field_author']); ?>
</p>
<?php echo render($content['field_image']); ?>
<?php echo render($content['field_description']); ?>

Leave a Reply

  1. What will be your opinion about using a module like Display Suite http://drupal.org/project/ds  to achieve this from the web interface, I mean thinking about: -The less experienced users.-Maintain theme compatibility when new version published.Best Regards,

    • Hey Jesus. I apologize for the really slow response. I stay ridiculously busy (by my own choice) so it takes me a while sometimes. Display Suite looks really interesting. I especially like the idea of a drag-and-drop interface. I have been dreaming of such a thing for a long time, not just for Drupal, but for content management in general. Even though I’m glad to be able to make a living coding, there really is no reason for us to keep reinventing the same wheels. My only concern is the inevitable code bloat that comes with something like this. What I mean is that there is always a tradeoff and the more simple the interface becomes, that tradeoff is usually in code bloat or complexity. Drupal itself is a great example of this. The ability to re-skin Drupal in unlimited fashion is that the HTML it outputs is really bloated. Look at the main navigation to see what I mean. There are about 8-10 CSS classes applied to every HTML element. I’m not criticizing the developers. They have no choice. I have built about a dozen versions of my own CMS so I am intimately familiar with the problems that must be solved. The only way to make the code really flexible is to make it more complex.

  2. Normally, There is a Node–[content type].tpl.php file that will just render the actual node and not a complete page. Is there something similar so that I can focus on just theming the actual entity and not the entire page?

    • At the time I wrote this post, there was no way to theme entities with an entity-specific template file. This may have changed now but I am not certain. That would definitely be the preferable approach and I hope that if the Drupal team has not added this ability, that they will at some point in the near future.

      I am not a Drupal expert by any means. I was working on a Drupal project at work and needed to theme an entity in D7 but couldn’t find an easy way to do it or any documentation so I thought I’d share what I learned.

      If there is a better way, I’d love to learn about it and I’ll be happy to share it as an update or follow-up to this post.

  3. If you are using the entity module, as you should, it offers an entity.tpl.php template. You can override that template, but it will affect all of your custom entities. So the entity module decided to use the “theme hook suggestions” system to allow more granular theming. So what can be done is, define a new theme function or template with the mymodule_theme function using the correct format (ej. myentitytype__mybundle__viewmode). To see all of the available theme_hook_suggestions, check the template_preprocess_entity function in the entity module.

  4. //entity_page_view($entity, $view_mode=’full’)

    It says that this function receives the entity object but looks like this actually receives only the entity id. – ie $entity will contain the entity id (number) only and not the whole entity object.