ソースを参照

Recursive Insert/Update of fields is working for Entities

Stephen Ficklin 9 年 前
コミット
f2145dcc08
1 ファイル変更156 行追加86 行削除
  1. 156 86
      tripal_entities/includes/tripal_entities.field_storage.inc

+ 156 - 86
tripal_entities/includes/tripal_entities.field_storage.inc

@@ -16,122 +16,195 @@ function tripal_entities_field_storage_info() {
  * Implements hook_field_storage_write().
  */
 function tripal_entities_field_storage_write($entity_type, $entity, $op, $fields) {
- 
-  // Use the cvterm_id to look up tables where this term is used
-  $cvterm_id = $entity->cvterm_id;
-  $sel_values = array(
-    'term_id' => array(
-        'cvterm_id' => $cvterm_id
-    ),
-  );
-  $term_usage = chado_generate_var('tripal_term_usage', $sel_values, array('return_array' => 1));
-  
-  // For each table that uses this term, insert the field recursively
-  foreach ($term_usage as $usage) {
-    $data_table = $usage->data_table;
-    //$type_table = $usage->type_table;
-    $type_field = $usage->field;
-    switch ($op) {
-      case FIELD_STORAGE_INSERT :
-        tripal_entities_field_storage_write_recursive($entity_type, $entity, $op, $fields, $data_table, $type_field);
-        break;
-      
-      case FIELD_STORAGE_UPDATE :
-        
-        break;
-    }
+
+  switch ($op) {
+    case FIELD_STORAGE_INSERT:
+      // Use the cvterm_id to look up tables where this term is used
+      $sel_values = array(
+        'term_id' => array(
+          'cvterm_id' => $entity->cvterm_id,
+        ),
+      );
+      $term_usage = chado_generate_var('tripal_term_usage', $sel_values, array('return_array' => 1));
+
+      // For each table that uses this term, insert the field recursively
+      foreach ($term_usage as $usage) {
+        $data_table = $usage->data_table;
+        //$type_table = $usage->type_table;
+        $type_field = $usage->field;
+        tripal_entities_field_storage_write_recursive($entity_type, $entity,
+          $op, $fields, $data_table, $type_field);
+      }
+      break;
+
+    case FIELD_STORAGE_UPDATE :
+
+      // Get the base table and record id for the fields of this entity.
+      $details = db_select('chado_entity', 'ce')
+        ->fields('ce')
+        ->condition('entity_id', $entity->id)
+        ->execute()
+        ->fetchObject();
+      $tablename = $details->data_table;
+      $type_field = $details->field;
+      $record_id = $details->record_id;
+
+      tripal_entities_field_storage_write_recursive($entity_type, $entity,
+        $op, $fields, $tablename, $type_field, $record_id);
+      if (!$details) {
+        // TODO: what to do if record is missing!
+      }
+      break;
   }
+}
+
+/**
+ * Implements hook_field_storage_write_recursive().
+ */
+function tripal_entities_field_storage_write_recursive($entity_type, $entity,
+    $op, $fields, $tablename, $type_field = NULL, $record_id = NULL, $depth = 0) {
 
-  // Get the IDs for this entity.
-/*   list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+  // Intialize the values array and $record_id;
+  $values = array();
 
-  // Find out which table should receive the insert.
-  $tablename = 'feature';
-  $type_field = 'type_id';
+  // Get the schema for this table so that we can identify the primary key
+  // and foreign keys.
   $schema = chado_get_schema($tablename);
   $pkey_field = $schema['primary key'][0];
+  $fkey_fields = $schema['foreign keys'];
+  $fkey_fields_list = array();
 
-  // Construct the values array that will be used to insert into the table.
-  $values = array();
+  // STEP 1: Recurse on the FK fields.
+  // Loop through the foreign keys os that we can recurse on those first.
+  foreach ($fkey_fields as $fk_table => $details) {
+    foreach ($details['columns'] as $local_id => $remote_id) {
+      // If this is the base table then do not recurse on the type_id.  The
+      // type_id is set in the entity.  We are at the base table when
+      // the depth is 0.
+      if ($depth == 0 && $local_id == $type_field) {
+        $values[$local_id] = $entity->cvterm_id;
+        continue;
+      }
+      // Keep track of the FK fields so that in STEP 2 we don't have to
+      // loop through the $fk_fields again.
+      $fkey_fields_list[] = $local_id;
+
+      // Recurse differently if this is an insert or an update.
+      $fk_val = NULL;
+      switch ($op) {
+        case FIELD_STORAGE_INSERT:
+          // On an insert we do not pass in a $record_id because
+          // we don't have any.
+          $fk_val = tripal_entities_field_storage_write_recursive($entity_type,
+            $entity, $op, $fields, $fk_table, NULL, NULL, $depth + 1);
+          break;
+        case FIELD_STORAGE_UPDATE:
+          // On an update we must get the value of the FK field. This should
+          // be present as a field, so get it's value and pass it in as the
+          // $record_id field.
+          $fk_val = tripal_entities_get_field_value($tablename . '__' . $local_id, $entity, $entity_type);
+          $fk_val = tripal_entities_field_storage_write_recursive($entity_type,
+            $entity, $op, $fields, $fk_table, NULL, $fk_val, $depth + 1);
+          break;
+      }
+      if ($fk_val) {
+        $values[$local_id] = $fk_val;
+      }
+    }
+  }
+
+  // STEP 2: Loop through the incoming fields.
+  // Loop through the fields passed to the function and find any that
+  // are for this table.  Then add their values to the $values array.
   foreach ($fields as $field_id) {
     $field = field_info_field_by_id($field_id);
     $field_name = $field['field_name'];
 
-    $matches = array();
     if (preg_match('/^' . $tablename . '__(.*)/', $field_name, $matches)) {
       $chado_field = $matches[1];
-      // Currently, we only support one language, but for for the sake of
-      // thoroughness we'll iterate through all possible languages.
-      $all_languages = field_available_languages($entity_type, $field);
-      $field_languages = array_intersect($all_languages, array_keys((array) $entity->$field_name));
-      foreach ($field_languages as $langcode) {
-        $items = (array) $entity->{$field_name}[$langcode];
-        // The number of items is related to the cardinatily of the field.
-        foreach ($items as $delta => $item) {
-          $values[$chado_field] = $item['value'];
-        }
+
+      // Skip FK fields as those should already have been dealt with the
+      // recursive code above.
+      if (in_array($field_id, $fkey_fields_list)) {
+        continue;
       }
+
+      // Add the value of the field to the $values arr for later insert/update.
+      $values[$chado_field] = tripal_entities_get_field_value($tablename . '__' . $chado_field, $entity, $entity_type);
     }
   }
-  // Add in the type_id field.
-  $values[$type_field] = $entity->cvterm_id;
 
+  // STEP 3: Insert/Update the record.
+  // If there are no values then return.
   $entity->storage = array();
   switch ($op) {
     case FIELD_STORAGE_INSERT:
+      if (count($values) == 0) {
+        return NULL;
+      }
+      // Inser the values array as a new record in the table.
       $record = chado_insert_record($tablename, $values);
       if ($record === FALSE) {
         drupal_set_message('Could not insert Chado record.', 'error');
       }
-      $entity->storage['chado']['record_id'] = $record[$pkey_field];
-      $entity->storage['chado']['data_table'] = $tablename;
-      $entity->storage['chado']['type_table'] = $tablename;
-      $entity->storage['chado']['field'] = $type_field;
+      $record_id = $record[$pkey_field];
 
       // Add a record to the chado_entity table so that the data for the
-      // fields can be pulled from Chado when loaded the next time.
-      $record = array(
-        'entity_id' => $entity->id,
-        'record_id' => $entity->storage['chado']['record_id'],
-        'data_table' => $entity->storage['chado']['data_table'],
-        'type_table' => $entity->storage['chado']['type_table'],
-        'field' => $entity->storage['chado']['field'],
-      );
-      $success = drupal_write_record('chado_entity', $record);
-      if (!$success) {
-        drupal_set_message('Unable to insert new data.', 'error');
+      // fields can be pulled from Chado when loaded the next time. Only do
+      // this for the base table record.
+      if ($depth == 0) {
+        $record = array(
+          'entity_id' => $entity->id,
+          'record_id' => $record_id,
+          'data_table' => $tablename,
+          'type_table' => $tablename, // TODO: this must be fixed.
+          'field' => $type_field,
+        );
+        $success = drupal_write_record('chado_entity', $record);
+        if (!$success) {
+          drupal_set_message('Unable to insert new data.', 'error');
+        }
       }
       break;
     case FIELD_STORAGE_UPDATE:
-      $match[$pkey_field] = $entity->storage['chado']['record_id'];
+      // If we don't have any values we are not going to update the
+      // record because there is nothing to udpate, do just return the
+      // record_id.
+      if (count($values) == 0) {
+        return $record_id;
+      }
+      $match[$pkey_field] = $record_id;
+
       chado_update_record($tablename, $match, $values);
       break;
-  } */
+  }
+  return $record_id;
 }
 
 /**
- * Implements hook_field_storage_write_recursive().
+ *
  */
-function tripal_entities_field_storage_write_recursive($entity_type, $entity, $op, $fields, $tablename, $type_field = NULL, $record_id = NULL) {
-  $values = array ();
-  $schema = chado_get_schema($tablename);
-  $pkey_field = $schema['primary key'][0];
-  $fkey_fields = $schema['foreign keys'];
+function tripal_entities_get_field_value($field_name, $entity, $entity_type) {
+  $value = NULL;
+  $field = field_info_field($field_name);
+  if (!$field) {
+    return $value;
+  }
 
-  //Loop through the foreign keys
-  foreach ($fkey_fields AS $fkey) {
-    $foreign_table = $fkey['table'];
-    $foreign_column = key($fkey['columns']);
-    // Do not recurse on the $type_field
-    if ($foreign_column == $type_field) {
-      continue;
-    }
-    $record_field = $tablename . "__" . $foreign_column;
-    $record_id = $entity->{$record_field}['und'][0]['value'];
-    if($record_id) {
-      tripal_entities_field_storage_write_recursive($entity_type, $entity, $op, $fields, $foreign_table, NULL, $record_id);
+  // Currently, we only support one language, but for for the sake of
+  // thoroughness we'll iterate through all possible languages.
+  $all_languages = field_available_languages($entity_type, $field);
+  $field_languages = array_intersect($all_languages, array_keys((array) $entity->$field_name));
+  foreach ($field_languages as $langcode) {
+    $items = (array) $entity->{$field_name}[$langcode];
+    // The number of items is related to the cardinatily of the field.
+    // We should always only have one item because this is a field in a
+    // table. But we loop anyway.
+    foreach ($items as $delta => $item) {
+      $value = $item['value'];
     }
   }
+  return $value;
 }
 
 /**
@@ -146,6 +219,7 @@ function tripal_entities_field_storage_load($entity_type, $entities, $age, $fiel
   $langcode = $language->language;
 
   foreach ($entities as $id => $entity) {
+
    // Get the base table and record id for the fields of this entity.
    $details = db_select('chado_entity', 'ce')
       ->fields('ce')
@@ -155,17 +229,13 @@ function tripal_entities_field_storage_load($entity_type, $entities, $age, $fiel
     if (!$details) {
       // TODO: what to do if record is missing!
     }
-    $entity->storage['chado']['record_id'] = $details->record_id;
-    $entity->storage['chado']['data_table'] = $details->data_table;
-    $entity->storage['chado']['type_table'] = $details->type_table;
-    $entity->storage['chado']['field'] = $details->field;
 
     // Find out which table should receive the insert.
-    $tablename = $entity->storage['chado']['data_table'];
-    $type_field = $entity->storage['chado']['field'];
+    $tablename = $details->data_table;
+    $type_field = $details->field;
     $schema = chado_get_schema($tablename);
     $pkey_field = $schema['primary key'][0];
-    $record_id = $entity->storage['chado']['record_id'];
+    $record_id = $details->record_id;
 
     // Iterate through the field names to get the list of tables and fields
     // that should be queried.