Describe field properties to make custom fields work with Entity Metadata Wrappers

Versions used: 

I've created a custom field using the Field API and it works great. However, I want to use the Entity module and its Entity Metadata Wrapper to quickly access Entity data, including data from the new field I've created. If you try to access data from the new field you will see an error message similar to the below.

EntityMetadataWrapperException: Unknown data property field_example_rgb. in EntityStructureWrapper->getPropertyInfo() (line 339 of mysite\sites\all\modules\entity\includes\entity.wrapper.inc).

The problem is that all modules providing a new field type have to provide the appropriate mapping to property info. So the onus is on us, as the creator of the new field, to provide metadata about the available properties of our new field. This can be done using the 'property_type' key in hook_field_info(). If we use the field created in the Field Example module as an example, we simply need to add one line in its implementation of hook_field_info() to make it work with Entity Meta Wrappers:

<?php
/**
* Implements hook_field_info().
*
* Provides the description of the field.
*/
function field_example_field_info() {
  return array(
 
// We name our field as the associative name of the array.
   
'field_example_rgb' => array(
     
'label' => t('Example Color RGB'),
     
'description' => t('Demonstrates a field composed of an RGB color.'),
     
'default_widget' => 'field_example_3text',
     
'default_formatter' => 'field_example_simple_text',
     
'property_type' => 'text', // we add the propert type here
   
),
  );
}
?>

With this added line, the Entity API now knows how to handle that field. For a more complex field, such as fields with more than one database column, it is not enough to simply decalare the property type. We need to specify further callbacks that may alter the generated property info. To do so we need to use the key 'property_callbacks' to add a custom callback function that will provide the extra metadata required. For example, and hypothetically assuming the example field has more than one column for multiple values:

<?php
/**
* Implements hook_field_info().
*
* Provides the description of the field.
*/
function field_example_field_info() {
  return array(
 
// We name our field as the associative name of the array.
   
'field_example_rgb' => array(
     
'label' => t('Example Color RGB'),
     
'description' => t('Demonstrates a field composed of an RGB color.'),
     
'default_widget' => 'field_example_3text',
     
'default_formatter' => 'field_example_simple_text',
     
'property_type' => 'field_example_rgb', // we add the propert type here
     
'property_callbacks' => array('field_example_rgb_property_info_callback'), // we add the callback
   
),
  );
}
?>

The callback we have added allows us define metadata about entity properties, similar to how the text (field) module is added via entity_metadata_field_text_property_callback. The callback function would look something like:

<?php
function field_example_rgb_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) {
 
$property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']];

 
$property['getter callback'] = 'entity_metadata_field_verbatim_get';
 
$property['setter callback'] = 'entity_metadata_field_verbatim_set';
  unset(
$property['query callback']);
 
 
$property['property info']['rgb'] = array(
   
'type' => 'text',
   
'label' => t('RGB'),
   
'setter callback' => 'entity_property_verbatim_set',
  );
 
$property['property info']['someothervalue'] = array(
   
'type' => 'text',
   
'label' => t('Some other value'),
   
'setter callback' => 'entity_property_verbatim_set',
  );
}
?>

This callback function allows us to alter the properties for this entity (the node). The first line allows us to change properties for the field specific to the entity bundle - our custom field belonging to the node type such as 'page'. Using 'property info' we can tell the Entity module that our field uses two values. The setter callback ensures that the data is stored in the correct format.