Coding Drupal Entities - part 1, Overview

This series of posts is not intended to be a step-by-step introduction to creating custom Drupal entites from code. If you are after that, there are some great resources to get you started:

Overview

The things I'm going to write about here is more of a reference for myself to remember how things are working (and why). If you have never created a custom entity before, you should probably start with one of the links above.

In my modules I prefer to define two entity types:

  • configuration entity, e.g. node type or profile type
  • content entity, this can have multiple bundles, like page and article for nodes

hook_entity_info

This is the hook to implement in order to let Drupal know about your entities. It returns (as usual) a giant associative array. More about this hook here:

Some of the keys that seem to confuse me a bit:

'load hook'

This is not going to load your entity. Basically you are defining a hook here that will be called when your entity is loaded. So if you set this as:

'load hook' => 'whatever',

then you have defined a hook called hook_whatever and it'll be called each time your entity is loaded. However as you can see it in entity_get_info:

foreach ($entity_info as $name => $data) {
        $entity_info[$name] += array(
          'fieldable' => FALSE,
          'controller class' => 'DrupalDefaultEntityController',
          'static cache' => TRUE,
          'field cache' => TRUE,
          'load hook' => $name . '_load',
          'bundles' => array(),
          'view modes' => array(),
          'entity keys' => array(),
          'translation' => array(),
        );

'load hook' already has a default value: the machine name of your entity and '_load' attached to it. Just as you would expect. So you can safely omit 'load hook' from your hook_entity_info implementation.

Then how is my entity loaded?

It depends. :) It still makes sense to write a load function for your entity, just in case. However this is only used if you (or somebody working with your entity) manually call it from code.

If you are viewing your content entity on its own page e.g. myentity/[myentity_id] (like node/[nid]), this is what happens, assuming you are using the Entity API module (hint: you should):

1. EntityContentUIController implements a hook_menu method, this is the relevant part:

  public function hook_menu() {
		// ...
    $wildcard = isset($this->entityInfo['admin ui']['menu wildcard']) ? $this->entityInfo['admin ui']['menu wildcard'] : '%entity_object';
		// ...
    // Add view, edit and delete menu items for content entities.
    $items[$this->path . '/' . $wildcard] = array(
      'title callback' => 'entity_ui_get_page_title',
      'title arguments' => array('view', $this->entityType, $this->id_count),
      'page callback' => 'entity_ui_entity_page_view',
      'page arguments' => array($this->id_count),
      'load arguments' => array($this->entityType),
      'access callback' => 'entity_access',
      'access arguments' => array('view', $this->entityType, $this->id_count),
    ) + $defaults;
    // ...

As you can see from the $wildcard value, unless you define your own menu loader function in your hook_entity_info implementation, using the ['admin ui']['menu wildcard'] key, the entity_object_load function will be called, the entity id (from the path) and the machine name of the entity type (from 'load arguments') will be passed to it.

2. The hook_menu implementation in entity.module will call the hook_menu method of the controller:

function entity_menu() {
  $items = array();
  foreach (entity_ui_controller() as $controller) {
    $items += $controller->hook_menu();
  }
  return $items;
}

I really like this trick, you can implement all kinds of hooks using this method and define the actual hook implementations in your controllers.

'uri callback'

It seems to be the best practice to simply do this:

'uri callback' => 'entity_class_uri',

What happens here is that if somebody wants to find out the URI of your entity, entity_uri is called. which will look for this key and call the function you name here. entity_class_uri seems to be widely used, which is just going to call the uri() method of your custom entity. This is how the Entity class implements this method:

public function uri() {
  if (isset($this->entityInfo['uri callback']) && $this->entityInfo['uri callback'] == 'entity_class_uri') {
    return $this->defaultUri();
  }
  return entity_uri($this->entityType, $this);
}

Basically setting 'uri callback' to entity_class_uri allows you to define your own uri callback, named defaultUri, in your custom entity class, overriding this default implementation in the Entity class:

protected function defaultUri() {
  return array('path' => 'default/' . $this->identifier());
}

I usually replace 'default' with the machine name of my entity and that's it. For custom path patterns I recommend using the Pathauto Entity module.

'label callback'

Entity label is like node title or comment subject. If your entity has a property that defines a label, you can omit the 'label callback' key and put the name of this property to ['entity keys']['label'] instead. However you might need custom logic to create the entity label, in this case this seems to be the best practice:

'label callback' => 'entity_class_label',

What happens is that entity_label will call your custom label callback function, entity_class_label in this case. entity_class_label will call Entity::label() which calls the defaultLabel method of your custom entity.

If you are using a label callback, make sure that your labels are unique, otherwise you'll be having difficulties on the UI:

Make entity labels uniqueWhich one do you want to edit?

'metadata controller class'

I like to use the class defined here to provide info about the properties of my custom entites (e.g. the uid property is not a simple integer but a user id, the created property is not only an integer but a date). You can also use hook_entity_property_info for this, but I prefer to go with classes.

Services

Drupal theming and sitebuild

  • PSD to Drupal theme
  • Installing and configuring modules (Views, CCK, Rules, etc.)
  • Theming modules' output

Drupal module development

  • Almost every site needs smaller modules to modify HTML output or override certain default functionalities
  • Developing custom modules

From the blog