Index: relation.field.inc
===================================================================
RCS file: /cvs/drupal/contributions/modules/relation/relation.field.inc,v
retrieving revision 1.1
diff -u -p -r1.1 relation.field.inc
--- relation.field.inc	24 Nov 2010 20:57:49 -0000	1.1
+++ relation.field.inc	4 Dec 2010 04:44:57 -0000
@@ -27,34 +27,61 @@ function relation_field_info() {
 /**
  * Implements hook_field_is_empty().
  */
-function relation_field_is_empty() {
-  return FALSE;
+function relation_field_is_empty($item, $field) {
+  $empty = (empty($item['entity_id']) || empty($item['relation_id']));
+  return $empty;
 }
 
 /**
- * Implements hook_field_insert().
- */
-function relation_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
-  list($entity_id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-  $insert1 = db_insert('relation')->fields(array('predicate'));
-  $insert2 = db_insert('relation_data')->fields(array('relation_id', 'entity_type', 'entity_id'));
-  foreach ($items as $item) {
-    if (!empty($item['entity_id'])) {
-      $relation_id = $insert1->values(array($field['field_name']))->execute();
-      $insert2->values(array($relation_id, $item['entity_type'], $item['entity_id']));
-      $insert2->values(array($relation_id, $entity_type, $entity_id));
+ * Implements hook_field_presave().
+ */
+function relation_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
+  $field_name = $field['field_name'];
+  list($eid) = entity_extract_ids($entity_type, $entity);
+dpm($entity,'presaving ' . $entity_type);
+dpm(debug_backtrace());
+  if (!isset($entity->recursion)) {
+    foreach ($items as $item) {
+      //get target entities individually, because they may be of different types.
+      $target_type = $item['entity_type'];
+      $target_id = $item['entity_id'];
+      $target = entity_load($target_type, array($target_id));
+      $target = $target[$target_id];
+      if (is_object($target)) { //in case $target is not an object..
+        list($tid) = entity_extract_ids($target_type, $target);
+        $exists = FALSE;
+        $next = '0';
+        unset($index);
+        // Check to see if this relationship already exists, and which field value contains it.
+        if (!empty($target->{$field_name})) {
+          $next = (string) count($target->{$field_name}[$langcode]); //avoid notices
+          foreach ($target->{$field_name}[$langcode] as $key => $value) {
+            if ($value['entity_id'] == $eid) {
+              $exists = TRUE;
+              $index = $key;
+            }
+          }
+        }
+        $item['entity_type'] = $entity_type;
+        $item['entity_id'] = $eid;
+        //need to prevent an infinite loop here.. <entity>_save() calls hook_field_presave()
+        $target->recursion = TRUE;
+        if ($exists == TRUE) {
+          // Update field value.
+          $target->{$field_name}[$langcode][$index] = $item;
+          entity_save($target_type, $target);
+        }
+        else {
+          $target->{$field_name} = array($langcode => array());
+          $target->{$field_name}[$langcode][$next] = $item;
+          entity_save($target_type, $target);
+        }
+      }
     }
   }
-  $insert2->execute();
-  $items = array();
-}
-
-/**
- * Implements hook_field_update().
- */
-function relation_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
-  relation_field_delete($entity_type, $entity, $field, $instance, $langcode, $items);
-  relation_field_insert($entity_type, $entity, $field, $instance, $langcode, $items);
+  else {
+    unset($entity->recursion);
+  }
 }
 
 /**
@@ -73,28 +100,6 @@ function relation_field_delete($entity_t
 }
 
 /**
- * Implements hook_field_load().
- */
-function relation_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) {
-  foreach ($entities as $entity) {
-    list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-    $entity_ids[] = $id;
-  }
-  $query = db_select('relation', 'base')
-    ->condition('l.entity_type', $entity_type)
-    ->condition('l.entity_id', $entity_ids);
-  _relation_entity_query_helper($query);
-  foreach ($query->execute() as $item) {
-    $items[$item->left_entity_id][] = array(
-      'relation_id' => $item->relation_id,
-      'predicate' => $item->predicate,
-      'entity_id' => $item->right_entity_id,
-      'entity_type' => $item->right_entity_type,
-    );
-  }
-}
-
-/**
  * Implements hook_field_widget_info().
  */
 function relation_field_widget_info() {
@@ -114,7 +119,7 @@ function relation_field_widget_form(&$fo
   $element['entity_type'] = array(
     '#type' => 'select',
     '#title' => t('Entity type'),
-    '#options' => drupal_map_assoc(array_keys(entity_get_info())),
+    '#options' => drupal_map_assoc(relation_get_possible_targets($field['field_name'])),
     '#default_value' => isset($items[$delta]) ? $items[$delta]['entity_type'] : '',
   );
   $element['entity_id'] = array(
@@ -122,6 +127,10 @@ function relation_field_widget_form(&$fo
     '#type' => 'textfield',
     '#default_value' => isset($items[$delta]) ? $items[$delta]['entity_id'] : '',
   );
+  $element['relation_id'] = array(
+    '#type' => 'hidden',
+    '#value' => !empty($items[$delta]['relation_id']) ? $items[$delta]['relation_id'] : db_next_id(),
+  );
   return $element;
 }
 
@@ -141,15 +150,20 @@ function relation_field_formatter_info()
  * Implements hook_field_formatter_view().
  */
 function relation_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
-  $list = array();
+  $links = array();
   foreach ($items as $item) {
+    $class = $item['entity_type'] . '-' . $item['entity_id'];
     $uri = entity_uri($item['entity_type'], $item['entity']);
-    $list[] = l($item['entity_type'], $uri['path'], $uri['options']);
-  }
-  return array(
-    '#theme' => 'item_list',
-    '#items' => $list,
+    $links[$class] = array(
+      'title' => entity_label($item['entity_type'], $item['entity']),
+      'href' => $uri['path'],
+    ) + $uri;
+  }
+  $element[0] = array(
+    '#theme' => 'links',
+    '#links' => $links,
   );
+  return $element;
 }
 
 /**
@@ -172,3 +186,15 @@ function relation_field_formatter_prepar
     }
   }
 }
+
+/**
+ * Helper function to get all entity types with given field attached.
+ */
+function relation_get_possible_targets($field_name) {
+  $instances = field_read_instances(array('field_name' => $field_name));
+  $entity_types = array();
+  foreach ($instances as $instance) {
+    $entity_types[] = $instance['entity_type'];
+  };
+  return $entity_types;
+}
Index: relation.info
===================================================================
RCS file: /cvs/drupal/contributions/modules/relation/relation.info,v
retrieving revision 1.3
diff -u -p -r1.3 relation.info
--- relation.info	24 Nov 2010 21:12:52 -0000	1.3
+++ relation.info	4 Dec 2010 04:44:57 -0000
@@ -1,6 +1,7 @@
 ; $Id: relation.info,v 1.3 2010/11/24 21:12:52 sun Exp $
 name = Relation
 description = Describes relationships between entities.
+dependencies[] = entity
 core = 7.x
 files[] = relation.module
 files[] = relation.field.inc
Index: relation.install
===================================================================
RCS file: /cvs/drupal/contributions/modules/relation/relation.install,v
retrieving revision 1.2
diff -u -p -r1.2 relation.install
--- relation.install	24 Nov 2010 20:57:49 -0000	1.2
+++ relation.install	4 Dec 2010 04:44:57 -0000
@@ -7,6 +7,36 @@
  */
 
 /**
+ * Implements hook_field_schema().
+ */
+function relation_field_schema() {
+  $schema = array(
+    'columns' => array(
+      'relation_id' => array(
+        'type' => 'int',
+        'unsigned' => FALSE,
+        'not null' => TRUE,
+      ),
+      'entity_type' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'entity_id' => array(
+        'type' => 'int',
+        'unsigned' => FALSE,
+        'not null' => TRUE,
+      ),
+    ),
+    'indexes' => array(
+      'relation_id' => array('relation_id'),
+    ),
+  );
+  return $schema;
+}
+
+/**
  * Implements hook_schema().
  */
 function relation_schema() {
Index: relation.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/relation/relation.module,v
retrieving revision 1.2
diff -u -p -r1.2 relation.module
--- relation.module	24 Nov 2010 20:57:49 -0000	1.2
+++ relation.module	4 Dec 2010 04:44:57 -0000
@@ -46,6 +46,8 @@ function relation_entity_info_alter(&$en
     // it will do things like list 'user' entities related to one or more nodes
     'handler' => 'RelationEntityRelation',
   );
+  $entity_info['node']['static cache'] = FALSE;
+  $entity_info['node']['field cache'] = FALSE;
 }
 
 /**
@@ -72,15 +74,12 @@ class RelationEntityController extends D
   }
 }
 
-function _relation_entity_query_helper($query) {
-  // Add the machine name field from the {taxonomy_vocabulary} table.
-  $query->innerJoin('relation_data', 'l', 'base.relation_id = l.relation_id');
-  $query->innerJoin('relation_data', 'r', 'base.relation_id = r.relation_id AND NOT (l.entity_type = r.entity_type AND l.entity_id = r.entity_id)');
-  $query->addField('base', 'relation_id');
-  $query->addField('base', 'predicate');
-  $query->addField('l', 'entity_type', 'left_entity_type');
+function _relation_entity_query_helper($query, $table, $field_name) {
+  $query->innerJoin($table, 'r', "l.{$field_name}_relation_id = r.{$field_name}_relation_id AND NOT (l.etid = r.etid AND l.entity_id = r.entity_id)");
+  $query->addField('l', "{$field_name}_relation_id", 'relation_id');
+  $query->addField('l', 'etid', 'left_etid');
   $query->addField('l', 'entity_id', 'left_entity_id');
-  $query->addField('r', 'entity_type', 'right_entity_type');
+  $query->addField('r', 'etid', 'right_etid');
   $query->addField('r', 'entity_id', 'right_entity_id');
 }
 
