Unsure how to define an entity type using Entity API I'm here because of this:    


  EntityMalformedException: Missing bundle property on entity of type contacts. in entity_extract_ids()


I'm trying to create my own entity. Can you identify what is incorrect or missing about my entity configuration?

I am using the Creating your own entities with Entity API guide for setting up a new module using entities.

For my needs, I only need one entity type. The example creates two entity types, one called "task", and another called "task_type".  So I did my best to eliminate the "task_type" sections, but I'm getting the above error and am not understanding what to do about the bundle type.

This is my hook_entity_info().

/**
 * Implements hook_entity_info().
 */
function contacts_entity_info() {
  $return = array(
    'contacts' =&gt; array(
      'label' =&gt; t('Contact'),
      'entity class' =&gt; 'Contacts',
      'controller class' =&gt; 'ContactsController',
      'base table' =&gt; 'contacts',
      'fieldable' =&gt; TRUE,
      'entity keys' =&gt; array(
        'id' =&gt; 'ckid',
      ),
      'bundle keys' =&gt; array(),
      'bundles' =&gt; array(),
      'load hook' =&gt; 'contacts_load',
      'view modes' =&gt; array(
        'full' =&gt; array(
          'label' =&gt; t('Default'),
          'custom settings' =&gt; FALSE,
        ),
      ),
      'label callback' =&gt; 'entity_class_label',
      'uri callback' =&gt; 'entity_class_uri',
      'module' =&gt; 'contacts',
      'access callback' =&gt; 'contacts_access',
    ),
  );

  return $return;
}


This is my custom controller. (Do I even need this?)

class ContactsController extends EntityAPIController {
  public function create(array $values = array()) {
    global $user;
    $values += array(
      'mail' =&gt; '',
      'notes' =&gt; '',
      'created' =&gt; REQUEST_TIME,
      'changed' =&gt; REQUEST_TIME,
      'uid' =&gt; $user-&gt;uid,
    );
    return parent::create($values);
  }

  public function buildContent($entity, $view_mode = 'full', $langcode = NULL, $content = array()) {
    $wrapper = entity_metadata_wrapper('contacts', $entity);
    $content['author'] = array('#markup' =&gt; t('Created by: !author', array('!author' =&gt; $wrapper-&gt;uid-&gt;name-&gt;value(array('sanitize' =&gt; TRUE)))));

    // Make Description and Status themed like default fields.
    $content['notes'] = array(
      '#theme' =&gt; 'field',
      '#weight' =&gt; 0,
      '#title' =&gt;t('Notes'),
      '#access' =&gt; TRUE,
      '#label_display' =&gt; 'above',
      '#view_mode' =&gt; 'full',
      '#language' =&gt; LANGUAGE_NONE,
      '#field_name' =&gt; 'field_contact_notes',
      '#field_type' =&gt; 'text',
      '#entity_type' =&gt; 'contacts',
      '#items' =&gt; array(array('value' =&gt; $entity-&gt;notes)),
      '#formatter' =&gt; 'text_default',
      0 =&gt; array('#markup' =&gt; check_plain($entity-&gt;notes))
    );

    return parent::buildContent($entity, $view_mode, $langcode, $content);
  }
}
entity_extract_ids() raises an exception when $info['entity keys']['bundle'] is not defined, or is an empty string. If you don't want to explicitly set the bundle property, you need to set it to  value that is considered empty from PHP, but it is not an empty string, such as an empty array, or FALSE. In such cases, the bundle property is set to the entity type name.

The following is the code contained in entity_extract_ids(), and which raises the exception you are seeing.

if (!empty($info['entity keys']['bundle'])) {
  // Explicitly fail for malformed entities missing the bundle property.
  if (!isset($entity-&gt;{$info['entity keys']['bundle']}) || $entity-&gt;{$info['entity keys'] ['bundle']} === '') {
    throw new EntityMalformedException(t('Missing bundle property on entity of type @entity_type.', array('@entity_type' =&gt; $entity_type)));
  }
  $bundle = $entity-&gt;{$info['entity keys']['bundle']};
}
else {
  // The entity type provides no bundle key: assume  single bundle, named
  // after the entity type.
  $bundle = $entity_type;
}


Keep in mind that entity_get_info() caches the entity information it gets. If you edit an enabled module, you need to disable it, and re-enable it. This forces Drupal to clean the cache it uses, and call the implementations of hook_entity_info() to get a fresh definition of the entity.

  // Use the advanced drupal_static() pattern, since this is called very often.
  static $drupal_static_fast;
  if (!isset($drupal_static_fast)) {
    $drupal_static_fast['entity_info'] = &amp;drupal_static(__FUNCTION__);
  }
  $entity_info = &amp;$drupal_static_fast['entity_info'];

  // hook_entity_info() includes translated strings, so each language is cached
  // separately.
  $langcode = $language-&gt;language;

  if (empty($entity_info)) {
    if ($cache = cache_get("entity_info:$langcode")) {
      $entity_info = $cache-&gt;data;
    }
    else {
      $entity_info = module_invoke_all('entity_info');
      // Merge in default values.
      foreach ($entity_info as $name =&gt; $data) {
      // …
    }
    // …
  }


Installing a new module has the same effect, as Drupal should clear its cache in that case too. If it doesn't happen, you can always force Drupal to clear its cache with cache_clear_all('entity_info', 'cache', TRUE).