Coding Drupal Entities - part 4, metadata

Defining the metadata of your entities properly is a very powerful way of making Drupal understand what information you are storing in the properties of your entities. With a few lines of code you can do a lot to make your entities work together with Drupal core and many popular contrib modules.

What is entity metadata good for?

Besides being able to easily get/set your data, you can tell Drupal a lot about the properties of your entity:

  • If you have a timestamp property (e.g. creation date of your entity), then it is stored as a simple number in the database. You can let Drupal know that this is actually a date so that if you e.g. want to use this property in a view, you'll be able to display it as a date.
  • If you have a property that references another entity (e.g. user id), you can tell Drupal to associate the referenced entity with this property, so you'll be able to easily get/set the properties of the referenced entity,  add a relationship to it in a view.

How is the metadata defined?

There are two way to define your metadata:

  • Implement hook_entity_property_info().
  • Define a 'metadata controller class' in your hook_entity_info implementation. The default value is EntityDefaultMetadataController, chances are you'll want to extend this class. The metadata info will be provided in a method called entityPropertyInfo(). This method will be invoked from entity_entity_property_info(), which is called from entity_get_property_info().

I prefer the second method as I find it easier to reuse my code if I use a class in a separate file instead of putting a hook implementation into my .module file.

You can override metadata of any entity by implementing hook_entity_property_info_alter().

The entityPropertyInfo() method

This method defines property info in an associative array with the following structure:

array('entity_machine_name' => array(
  'properties' => array(
    'property_1' => array(...),
    'property_2' => array(...),
  ),
)

The default EntityDefaultMetadataController::entityPropertyInfo() method does the following:

  • uses the schema definition from your .install file to set the type of your properties, e.g. if you have a 'created' timestamp, it will be set to an integer as it is stored in the database as an integer
  • adds a default description of the property
  • if you've defined ['entity keys']['name'] in your hook_entity_info(), the appropriate property will be set as 'token' type
  • if you haven't implemented a 'url' property in your hook_entity_info() but implemented the 'uri callback' key (chances are this is the case), a 'url' property will be created for you and it'll be exposed to Views

Define your own property info

You can define your custom properties by overrideing EntityDefaultMetadataController::entityPropertyInfo(). Check hook_entity_property_info() in entity.api.php to see what possible keys you can use when defining the associative array for your properties. We'll take a closer look at some these below.

Getters and setters

The 'getter callback' key will be used to fetch the value of the property. The default value is entity_property_verbatim_get(), which basically returns the raw data. A few examples when you might want to set your own getter callback:

If the property is referencing another entity (e.g. the user id is a number, but it refers to a user account) you can use entity_property_getter_method() and define a method for you entity that returns the required object. The method must have the same name as the property. E.g. this is how the profile2 module uses this:

    // ...
    unset($properties['uid']);
    $properties['user'] = array(
      'label' => t("User"),
      'type' => 'user',
      'description' => t("The owner of the profile."),
      'getter callback' => 'entity_property_getter_method',
      'setter callback' => 'entity_property_setter_method',
      'setter permission' => 'administer profiles',
      'required' => TRUE,
      'schema field' => 'uid',
    );
    //...

First the 'uid' property is unset and a new property with the more convenient 'user' name is defined. It's type is set to user, so Entity API will expect a user object from the getter and setter callbacks. In order to make the getter and setter callbacks work, the appropriate methods must be added to the Profile class:

class Profile extends Entity {
  //...
  // This is called by the getter callback.
  public function user() {
    return user_load($this->uid);
  }
  // This is called by the setter callback.
  public function setUser($account) {
    $this->uid = is_object($account) ? $account->uid : $account;
  }
  // ...
}

As you can see, Profile::user() has the same name as the appropriate key of the $properties array and the 'set' is prepended to the name of the setter method. (If you have dashes in the keys of the $properties array, those should be removed from the function names.)

Schema field

If the value of the property is stored in a database field (in hook_schema), you can define the field name here. This is set in entity_metadata_convert_schema for all schema fields. Getters and setters can use this, however if the field name and property name is the same (i.e. the "created" property is stored in the "created" database field) you might omit this.

If you need Views integration you need to define this key, otherwise you'll get a "Missing handler: [entity_name] [property_name] field" message.

Entity views field

Settings this to TRUE will expose your property to Views. If a property has a schema field Views will know about it without setting thiskey.

For custom properties you'll need to set this to TRUE for easy views integration. Make sure you define the correct 'type' for the property in your entityPropertyInfo() implementation so that the field will use the correct handler. (see entity_views_get_field_handlers)

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