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']); ?>