tripal_entities.field_storage.inc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. <?php
  2. /**
  3. * Implements hook_field_storage_info().
  4. */
  5. function tripal_entities_field_storage_info() {
  6. return array(
  7. 'field_chado_storage' => array(
  8. 'label' => t('Chado storage'),
  9. 'description' => t('Stores fields in the local Chado database.'),
  10. 'settings' => array(),
  11. ),
  12. );
  13. }
  14. /**
  15. * Implements hook_field_storage_query().
  16. *
  17. * @param $query
  18. */
  19. function tripal_entities_field_storage_query($query) {
  20. // TODO: figure out what this function does.
  21. }
  22. /**
  23. * Implements hook_field_storage_write().
  24. */
  25. function tripal_entities_field_storage_write($entity_type, $entity, $op, $fields) {
  26. // Conver the fields into a key/value list of fields and their values.
  27. $field_vals = tripal_entities_field_storage_unnest_fields($fields, $entity_type, $entity);
  28. // Calculate the md5 checksum for the sequence per user's request
  29. if (key_exists('feature__md5checksum', $field_vals) && key_exists('feature__residues', $field_vals)) {
  30. $residues = $field_vals['feature__residues'];
  31. $field_vals['feature__md5checksum'] = $field_vals['feature__md5checksum'] ? md5($residues) : NULL;
  32. }
  33. $transaction = db_transaction();
  34. try {
  35. switch ($op) {
  36. case FIELD_STORAGE_INSERT:
  37. // Use the cvterm_id to look up tables where this term is used
  38. $sel_values = array(
  39. 'term_id' => array(
  40. 'cvterm_id' => $entity->cvterm_id,
  41. ),
  42. );
  43. $term_usage = chado_generate_var('tripal_term_usage', $sel_values, array('return_array' => 1));
  44. // For each table that uses this term, insert the field recursively
  45. foreach ($term_usage as $usage) {
  46. $data_table = $usage->data_table;
  47. //$type_table = $usage->type_table;
  48. $type_field = $usage->field;
  49. tripal_entities_field_storage_write_recursive($entity_type, $entity,
  50. $op, $field_vals, $data_table, $type_field);
  51. }
  52. break;
  53. case FIELD_STORAGE_UPDATE :
  54. // Get the base table and record id for the fields of this entity.
  55. $details = db_select('chado_entity', 'ce')
  56. ->fields('ce')
  57. ->condition('entity_id', $entity->id)
  58. ->execute()
  59. ->fetchObject();
  60. $tablename = $details->data_table;
  61. $type_field = $details->field;
  62. $record_id = $details->record_id;
  63. tripal_entities_field_storage_write_recursive($entity_type, $entity,
  64. $op, $field_vals, $tablename, $type_field, $record_id);
  65. if (!$details) {
  66. // TODO: what to do if record is missing!
  67. }
  68. break;
  69. }
  70. }
  71. catch (Exception $e) {
  72. $transaction->rollback();
  73. watchdog_exception('tripal_feature', $e);
  74. drupal_set_message('Could not write record to Chado. See recent logs', 'error');
  75. }
  76. }
  77. /**
  78. * Implements hook_field_storage_write_recursive().
  79. */
  80. function tripal_entities_field_storage_write_recursive($entity_type, $entity,
  81. $op, $field_vals, $tablename, $type_field = NULL, $record_id = NULL, $depth = 0) {
  82. // Intialize the values array and $record_id;
  83. $values = array();
  84. // Get the schema for this table so that we can identify the primary key
  85. // and foreign keys.
  86. $schema = chado_get_schema($tablename);
  87. $pkey_field = $schema['primary key'][0];
  88. $fkey_fields = $schema['foreign keys'];
  89. $fkey_fields_list = array();
  90. // STEP 1: Recurse on the FK fields.
  91. // Loop through the foreign keys os that we can recurse on those first.
  92. foreach ($fkey_fields as $fk_table => $details) {
  93. foreach ($details['columns'] as $local_id => $remote_id) {
  94. // If this is the base table then do not recurse on the type_id. The
  95. // type_id is set in the entity. We are at the base table when
  96. // the depth is 0.
  97. if ($depth == 0 && $local_id == $type_field) {
  98. $values[$local_id] = $entity->cvterm_id;
  99. continue;
  100. }
  101. // Keep track of the FK fields so that in STEP 2 we don't have to
  102. // loop through the $fk_fields again.
  103. $fkey_fields_list[] = $local_id;
  104. // Recurse on the FK field. Pass in the ID for the FK field if one
  105. // exists in the $field_vals;
  106. $fk_val = NULL;
  107. $fk_field_name = $tablename . '__' . $local_id;
  108. $fk_val = array_key_exists($fk_field_name, $field_vals) ? $field_vals[$fk_field_name] : NULL;
  109. $fk_val = tripal_entities_field_storage_write_recursive($entity_type,
  110. $entity, $op, $field_vals, $fk_table, NULL, $fk_val, $depth + 1);
  111. if ($fk_val) {
  112. $values[$local_id] = $fk_val;
  113. }
  114. }
  115. }
  116. // STEP 2: Loop through the incoming fields.
  117. // Loop through the fields passed to the function and find any that
  118. // are for this table. Then add their values to the $values array.
  119. foreach ($field_vals as $field_name => $field_val) {
  120. // If the field value is empty then continue.
  121. if (!isset($field_val) or $field_val === '' or $field_val === NULL) {
  122. continue;
  123. }
  124. if (preg_match('/^' . $tablename . '__(.*)/', $field_name, $matches)) {
  125. $chado_field = $matches[1];
  126. // Skip the Pkey field. We won't ever insert a primary key and if
  127. // one is provided in the fields then we use it for matching on an
  128. // update. We don't add it to the $values array in either case.
  129. if ($chado_field == $pkey_field) {
  130. continue;
  131. }
  132. // Skip FK fields as those should already have been dealt with the
  133. // recursive code above.
  134. if (in_array($chado_field, $fkey_fields_list)) {
  135. continue;
  136. }
  137. // Add the value of the field to the $values arr for later insert/update.
  138. $values[$chado_field] = $field_vals[$field_name];
  139. }
  140. }
  141. // STEP 3: Insert/Update the record.
  142. // If there are no values then return.
  143. if (count($values) == 0) {
  144. return $record_id;
  145. }
  146. // If we don't have an incoming record ID then this is an insert.
  147. if (!$record_id) {
  148. // Insert the values array as a new record in the table.
  149. $record = chado_insert_record($tablename, $values);
  150. if ($record === FALSE) {
  151. drupal_set_message('Could not insert Chado record into table, "$table".', 'error');
  152. }
  153. $record_id = $record[$pkey_field];
  154. // Add a record to the chado_entity table so that the data for the
  155. // fields can be pulled from Chado when loaded the next time. Only do
  156. // this for the base table record.
  157. if ($depth == 0) {
  158. $record = array(
  159. 'entity_id' => $entity->id,
  160. 'record_id' => $record_id,
  161. 'data_table' => $tablename,
  162. 'type_table' => $tablename, // TODO: this must be fixed.
  163. 'field' => $type_field,
  164. );
  165. $success = drupal_write_record('chado_entity', $record);
  166. if (!$success) {
  167. drupal_set_message('Unable to insert new Chado entity.', 'error');
  168. }
  169. }
  170. }
  171. // We have an incoming record_id so this is an update.
  172. else {
  173. $match[$pkey_field] = $record_id;
  174. if (!chado_update_record($tablename, $match, $values)) {
  175. drupal_set_message('Could not update Chado record for in table, "$table".', 'error');
  176. }
  177. }
  178. return $record_id;
  179. }
  180. /**
  181. * Implements hook_field_storage_load().
  182. *
  183. * Responsible for loading the fields from the Chado database and adding
  184. * their values to the entity.
  185. */
  186. function tripal_entities_field_storage_load($entity_type, $entities, $age, $fields, $options) {
  187. $load_current = $age == FIELD_LOAD_CURRENT;
  188. global $language;
  189. $langcode = $language->language;
  190. foreach ($entities as $id => $entity) {
  191. // Get the base table and record id for the fields of this entity.
  192. $details = db_select('chado_entity', 'ce')
  193. ->fields('ce')
  194. ->condition('entity_id', $entity->id)
  195. ->execute()
  196. ->fetchObject();
  197. if (!$details) {
  198. // TODO: what to do if record is missing!
  199. }
  200. // Find out which table should receive the insert.
  201. $tablename = $details->data_table;
  202. $type_field = $details->field;
  203. $schema = chado_get_schema($tablename);
  204. $pkey_field = $schema['primary key'][0];
  205. $record_id = $details->record_id;
  206. // Iterate through the field names to get the list of tables and fields
  207. // that should be queried.
  208. $columns = array();
  209. foreach ($fields as $field_id => $ids) {
  210. // By the time this hook runs, the relevant field definitions have been
  211. // populated and cached in FieldInfo, so calling field_info_field_by_id()
  212. // on each field individually is more efficient than loading all fields in
  213. // memory upfront with field_info_field_by_ids().
  214. $field = field_info_field_by_id($field_id);
  215. $field_name = $field['field_name'];
  216. $matches = array();
  217. if (preg_match('/^(.*?)__(.*?)$/', $field_name, $matches)) {
  218. $table = $matches[1];
  219. $field = $matches[2];
  220. $columns[$table][] = $field;
  221. }
  222. }
  223. // Get the record
  224. $values = array($pkey_field => $record_id);
  225. $record = chado_select_record($tablename, $columns[$tablename], $values);
  226. // Now set the field values
  227. foreach ($fields as $field_id => $ids) {
  228. $field = field_info_field_by_id($field_id);
  229. $field_name = $field['field_name'];
  230. $matches = array();
  231. if (preg_match('/^(.*?)__(.*?)$/', $field_name, $matches)) {
  232. $table = $matches[1];
  233. $field = $matches[2];
  234. $entity->{$field_name}['und'][] = array('value' => $record[0]->$field);
  235. }
  236. }
  237. }
  238. }
  239. /**
  240. * Iterates through all of the fields reformats to a key/value array.
  241. *
  242. * @param $fields
  243. */
  244. function tripal_entities_field_storage_unnest_fields($fields, $entity_type, $entity) {
  245. $new_fields = array();
  246. foreach ($fields as $field_id => $ids) {
  247. $field = field_info_field_by_id($field_id);
  248. $field_name = $field['field_name'];
  249. // Currently, we only support one language, but for for the sake of
  250. // thoroughness we'll iterate through all possible languages.
  251. $all_languages = field_available_languages($entity_type, $field);
  252. $field_languages = array_intersect($all_languages, array_keys((array) $entity->$field_name));
  253. foreach ($field_languages as $langcode) {
  254. $items = (array) $entity->{$field_name}[$langcode];
  255. // The number of items is related to the cardinatily of the field.
  256. // We should always only have one item because this is a field in a
  257. // table. But we loop anyway.
  258. foreach ($items as $delta => $item) {
  259. // If the $value is an array then this field has nested fields. We
  260. // want to add those to our $new_fields list.
  261. if (is_array($item['value'])) {
  262. foreach ($item['value'] as $children) {
  263. foreach ($children as $child_field_name => $child_value) {
  264. if (preg_match('/^.*?__.*?$/', $child_field_name)) {
  265. $new_fields[$child_field_name] = $child_value;
  266. }
  267. }
  268. }
  269. }
  270. else {
  271. $new_fields[$field_name] = $item['value'];
  272. }
  273. }
  274. }
  275. }
  276. return $new_fields;
  277. }