tripal_chado.field_storage.inc 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998
  1. <?php
  2. /**
  3. * Implements hook_field_storage_info().
  4. */
  5. function tripal_chado_field_storage_info() {
  6. return [
  7. 'field_chado_storage' => [
  8. 'label' => t('Chado'),
  9. 'description' => t('Stores fields in the local Chado database.'),
  10. 'settings' => [
  11. 'tripal_storage_api' => TRUE,
  12. ],
  13. // The logo_url key is supported by Tripal. It's not a Drupal key. It's
  14. // used for adding a logo or picture for the data store to help make it
  15. // more easily recognized on the field_ui_field_overview_form. Ideally
  16. // the URL should point to a relative path on the local Drupal site.
  17. 'logo_url' => url(drupal_get_path('module', 'tripal') . '/theme/images/250px-ChadoLogo.png'),
  18. ],
  19. ];
  20. }
  21. /**
  22. * Implements hook_field_storage_write().
  23. */
  24. function tripal_chado_field_storage_write($entity_type, $entity, $op, $fields) {
  25. // Get the bundle and the term for this entity.
  26. $bundle = tripal_load_bundle_entity(['name' => $entity->bundle]);
  27. $term = entity_load('TripalTerm', ['id' => $entity->term_id]);
  28. $term = reset($term);
  29. // Convert the Tripal term entity into the appropriate record in Chado.
  30. $dbxref = chado_get_dbxref([
  31. 'accession' => $term->accession,
  32. 'db_id' => ['name' => $term->vocab->vocabulary],
  33. ]);
  34. $cvterm = chado_get_cvterm(['dbxref_id' => $dbxref->dbxref_id]);
  35. // Get the base table, type field and record_id from the entity.
  36. $base_table = $entity->chado_table;
  37. $type_field = $entity->chado_column;
  38. $record = $entity->chado_record;
  39. $record_id = $entity->chado_record_id;
  40. $linker = property_exists($entity, 'chado_linker') ? $entity->chado_linker : '';
  41. $type_id = property_exists($entity, 'chado_type_id') ? $entity->chado_type_id : '';
  42. $type_value = property_exists($entity, 'chado_type_value') ? $entity->chado_type_value : '';
  43. $base_type_id = property_exists($entity, 'chado_base_type_id') ? $entity->chado_base_type_id : '';
  44. $base_schema = chado_get_schema($base_table);
  45. $base_pkey = $base_schema['primary key'][0];
  46. // Convert the fields into a key/value list of fields and their values.
  47. list($field_vals, $field_items) = tripal_chado_field_storage_write_merge_fields($fields, $entity_type, $entity);
  48. // Start out with the base table fields.
  49. $values = $field_vals[$base_table];
  50. // First, set the pkey for the record if this is an update.
  51. if ($record_id) {
  52. $values[$base_pkey] = $record_id;
  53. }
  54. // For content types that use a type_id in the base table then we need
  55. // to add in the type_id value.
  56. elseif ($type_field and !$linker) {
  57. $values[$type_field] = $cvterm->cvterm_id;
  58. }
  59. // If we have a linker table and there is a base_type_id then the base
  60. // table requires a type_id and we need to add it in.
  61. elseif ($linker and $base_type_id) {
  62. // If a base table has a 'type_id' field then we must have the user
  63. // specify the base table type as well.
  64. $base_type_column = FALSE;
  65. $schema = chado_get_schema($base_table);
  66. foreach ($schema['foreign keys'] as $fk_id => $details) {
  67. if ($details['table'] == 'cvterm') {
  68. foreach ($details['columns'] as $fk_left => $fk_right) {
  69. if ($fk_left == 'type_id') {
  70. $base_type_column = 'type_id';
  71. }
  72. }
  73. }
  74. }
  75. if ($base_type_column) {
  76. $values[$base_type_column] = $base_type_id;
  77. }
  78. }
  79. // Insert the record into the base table and get the record ID.
  80. $base_record_id = tripal_chado_field_storage_write_table($base_table, $values, $base_table);
  81. if (!$base_record_id) {
  82. throw new Exception('Unable to write fields to Chado: ' . print_r($field_items, TRUE));
  83. }
  84. // For content types that use a linker table we need to add a record into
  85. // the linker table.
  86. if ($type_field and $linker) {
  87. // If this is for a property table then there will be a value.
  88. if ($type_value) {
  89. $field_vals[$linker][] = [
  90. $base_pkey => $base_record_id,
  91. 'type_id' => $type_id,
  92. 'value' => $type_value,
  93. ];
  94. }
  95. // If there is no value then this is a cvterm table
  96. else {
  97. $args = [':uniquename' => 'null'];
  98. $sql = "
  99. SELECT *
  100. FROM {pub}
  101. WHERE uniquename = :uniquename
  102. ";
  103. $pub = chado_query($sql, $args)->fetchObject();
  104. if (!$pub) {
  105. throw new Exception('Unable to save record because the NULL publication is missing from the pub table. Please add a null record where the uniquename value is "null".');
  106. }
  107. $field_vals[$linker][] = [
  108. $base_pkey => $base_record_id,
  109. 'cvterm_id' => $cvterm->cvterm_id,
  110. 'pub_id' => $pub->pub_id,
  111. ];
  112. }
  113. }
  114. // If this is an insert then add the chado_entity record.
  115. if ($op == FIELD_STORAGE_INSERT) {
  116. // Add the record to the proper chado entity table
  117. $chado_entity_table = chado_get_bundle_entity_table($bundle);
  118. $record_id = db_insert($chado_entity_table)
  119. ->fields([
  120. 'entity_id' => $entity->id,
  121. 'record_id' => $base_record_id,
  122. ])
  123. ->execute();
  124. if (!$record_id) {
  125. throw new Exception('Unable to insert new Chado entity.');
  126. }
  127. }
  128. // Now that we have handled the base table, we need to handle linking tables.
  129. foreach ($field_vals as $table_name => $details) {
  130. // Skip the base table as we've already dealt with it.
  131. if ($table_name == $base_table) {
  132. continue;
  133. }
  134. foreach ($details as $delta => $values) {
  135. $record_id = tripal_chado_field_storage_write_table($table_name, $values, $base_table, $base_pkey, $base_record_id);
  136. }
  137. }
  138. }
  139. /**
  140. * Write (inserts/updates/deletes) values for a Chado table.
  141. *
  142. * The $values array is of the same format used by chado_insert_record() and
  143. * chado_update_record(). However, both of those methods will use any nested
  144. * arrays (i.e. representing foreign keys) to select an appropriate record ID
  145. * that can be substituted as the value. Here, the nested arrays are
  146. * either inserted or updated as well, but the choice is determined if the
  147. * primary key value is present. If present an update occurs, if not present
  148. * then an insert occurs.
  149. *
  150. * This function is recursive and nested arrays from the lowest point of the
  151. * "tree" are dealt with first.
  152. *
  153. * @param $table_name
  154. * The name of the table on which the insertion/update is performed.
  155. * @param $values
  156. * The values array for the insertion.
  157. *
  158. * @throws Exception
  159. *
  160. * @return
  161. * The unique record ID.
  162. */
  163. function tripal_chado_field_storage_write_table($table_name, $values, $base_table, $base_pkey = NULL, $base_record_id = NULL) {
  164. $schema = chado_get_schema($table_name);
  165. $fkeys = $schema['foreign keys'];
  166. $pkey = $schema['primary key'][0];
  167. // Fields with a cardinality greater than 1 will often submit an
  168. // empty form. We want to remove these empty submissions. We can detect
  169. // them if all of the fields are empty.
  170. $num_empty = 0;
  171. foreach ($values as $column => $value) {
  172. if (!$value) {
  173. $num_empty++;
  174. }
  175. }
  176. if ($num_empty == count(array_keys($values))) {
  177. return '';
  178. }
  179. // If the primary key column has a value but all other values are empty then
  180. // this is a delete.
  181. if (array_key_exists($pkey, $values) and $values[$pkey]) {
  182. $num_vals = 0;
  183. foreach ($values as $value) {
  184. if ($value) {
  185. $num_vals++;
  186. }
  187. }
  188. if ($num_vals == 1) {
  189. $new_vals[$pkey] = $values[$pkey];
  190. if (!chado_delete_record($table_name, $new_vals)) {
  191. throw new Exception('Could not delete record from table: "' . $table_name . '".');
  192. }
  193. return '';
  194. }
  195. }
  196. // If the primary key column does not have a value then this is an insert.
  197. if (!array_key_exists($pkey, $values) or !$values[$pkey] or !isset($values[$pkey])) {
  198. // Remove the pkey field if it is present (it is empty).
  199. if (array_key_exists($pkey, $values)) {
  200. unset($values[$pkey]);
  201. }
  202. // Before inserting, we want to make sure the record does not
  203. // already exist. Using the unique constraint check for a matching record.
  204. $options = ['is_duplicate' => TRUE];
  205. $is_duplicate = chado_select_record($table_name, ['*'], $values, $options);
  206. if ($is_duplicate) {
  207. $record = chado_select_record($table_name, ['*'], $values);
  208. return $record[0]->$pkey;
  209. }
  210. // If this table_name is a linker table then we want to be sure to add in
  211. // the value for the $base_record_id it it isn't set. This would only
  212. // occur on an insert.
  213. if (array_key_exists($base_table, $fkeys) and $base_table != $table_name) {
  214. foreach ($fkeys[$base_table]['columns'] as $lkey => $rkey) {
  215. if ($rkey == $base_pkey and !array_key_exists($lkey, $values) or empty($values[$lkey])) {
  216. $values[$lkey] = $base_record_id;
  217. }
  218. }
  219. }
  220. // Insert the values array as a new record in the table but remove the
  221. // pkey as it should be set.
  222. $new_vals = $values;
  223. unset($new_vals[$pkey]);
  224. $record = chado_insert_record($table_name, $new_vals);
  225. if ($record === FALSE) {
  226. throw new Exception('Could not insert Chado record into table: "' . $table_name . '": ' . print_r($new_vals, TRUE));
  227. }
  228. return $record[$pkey];
  229. }
  230. // If we've made it to this point then this is an update.
  231. // TODO: what if the unique constraint matches another record? That is
  232. // not being tested for here.
  233. $match[$pkey] = $values[$pkey];
  234. if (!chado_update_record($table_name, $match, $values)) {
  235. drupal_set_message("Could not update Chado record in table: $table_name.", 'error');
  236. }
  237. return $values[$pkey];
  238. }
  239. /**
  240. * Implements hook_field_storage_pre_load().
  241. *
  242. * Adds a 'chado_record' object containing the base record for the
  243. * entity.
  244. */
  245. function tripal_chado_field_storage_pre_load($entity_type, $entities, $age,
  246. $fields, $options) {
  247. // Itereate through the entities and add in the Chado record.
  248. foreach ($entities as $id => $entity) {
  249. // Only deal with Tripal Entities.
  250. if ($entity_type != 'TripalEntity') {
  251. continue;
  252. }
  253. $record_id = NULL;
  254. if (property_exists($entity, 'chado_table')) {
  255. // Get the base table and record id for the fields of this entity.
  256. $base_table = $entity->chado_table;
  257. $type_field = $entity->chado_column;
  258. $record_id = $entity->chado_record_id;
  259. }
  260. else {
  261. $bundle = tripal_load_bundle_entity(['name' => $entity->bundle]);
  262. $base_table = $bundle->data_table;
  263. $type_field = $bundle->type_column;
  264. // Get the record id for the fields of this entity.
  265. $chado_entity_table = chado_get_bundle_entity_table($bundle);
  266. $details = db_select($chado_entity_table, 'ce')
  267. ->fields('ce')
  268. ->condition('entity_id', $entity->id)
  269. ->execute()
  270. ->fetchObject();
  271. if ($details) {
  272. $record_id = isset($details->record_id) ? $details->record_id : '';
  273. }
  274. $entity->chado_table = $base_table;
  275. $entity->chado_record_id = $record_id;
  276. $entity->chado_column = $type_field;
  277. }
  278. if ($record_id) {
  279. // Get this table's schema.
  280. $schema = chado_get_schema($base_table);
  281. $pkey_field = $schema['primary key'][0];
  282. // Get the base record if one exists
  283. $columns = ['*'];
  284. $match = [$pkey_field => $record_id];
  285. $record = chado_generate_var($base_table, $match);
  286. $entity->chado_record = $record;
  287. }
  288. }
  289. }
  290. /**
  291. * Implements hook_field_storage_load().
  292. *
  293. * Responsible for loading the fields from the Chado database and adding
  294. * their values to the entity.
  295. */
  296. function tripal_chado_field_storage_load($entity_type, $entities, $age,
  297. $fields, $options) {
  298. $load_current = $age == FIELD_LOAD_CURRENT;
  299. global $language;
  300. $langcode = $language->language;
  301. foreach ($entities as $id => $entity) {
  302. $base_table = $entity->chado_table;
  303. $type_field = $entity->chado_column;
  304. $record_id = $entity->chado_record_id;
  305. $record = $entity->chado_record;
  306. $schema = chado_get_schema($base_table);
  307. // Iterate through the entity's fields so we can get the column names
  308. // that need to be selected from each of the tables represented.
  309. $tables = [];
  310. foreach ($fields as $field_id => $ids) {
  311. // By the time this hook runs, the relevant field definitions have been
  312. // populated and cached in FieldInfo, so calling field_info_field_by_id()
  313. // on each field individually is more efficient than loading all fields in
  314. // memory upfront with field_info_field_by_ids().
  315. $field = field_info_field_by_id($field_id);
  316. $field_name = $field['field_name'];
  317. $field_type = $field['type'];
  318. $field_module = $field['module'];
  319. // Get the instance for this field. If no instance exists then skip
  320. // loading of this field. This can happen when a field is deleted from
  321. // a bundle using the user UI form.
  322. // TODO: how to deal with deleted fields?
  323. $instance = field_info_instance($entity_type, $field_name, $entity->bundle);
  324. if (!$instance) {
  325. continue;
  326. }
  327. // Skip fields that don't map to a Chado table.
  328. if (!array_key_exists('settings', $instance) or
  329. !array_key_exists('chado_table', $instance['settings'])) {
  330. continue;
  331. }
  332. // Get the Chado table and column for this field.
  333. $field_table = $instance['settings']['chado_table'];
  334. $field_column = $instance['settings']['chado_column'];
  335. // There are only two types of fields: 1) fields that represent a single
  336. // column of the base table, or 2) fields that represent a linked record
  337. // in a many-to-one relationship with the base table.
  338. // Type 1: fields from base tables.
  339. if ($field_table == $base_table) {
  340. // Set an empty value by default, and if there is a record, then update.
  341. $entity->{$field_name}['und'][0]['value'] = '';
  342. if ($record and property_exists($record, $field_column)) {
  343. // If the field column is an object then it's a FK to another table.
  344. // and because $record object is created by the chado_generate_var()
  345. // function we must go one more level deeper to get the value
  346. if (is_object($record->$field_column)) {
  347. $fkey_column = $field_column;
  348. foreach ($schema['foreign keys'] as $table => $fk_details) {
  349. foreach ($fk_details['columns'] as $lfkey => $rfkey) {
  350. if ($lfkey == $field_column) {
  351. $fkey_column = $rfkey;
  352. }
  353. }
  354. }
  355. $entity->{$field_name}['und'][0]['chado-' . $field_table . '__' . $field_column] = $record->$field_column->$fkey_column;
  356. }
  357. else {
  358. // For non FK fields we'll make the field value be the same
  359. // as the column value.
  360. $entity->{$field_name}['und'][0]['value'] = $record->$field_column;
  361. $entity->{$field_name}['und'][0]['chado-' . $field_table . '__' . $field_column] = $record->$field_column;
  362. // For datetime columns we need to strip out the milliseconds if
  363. // they are there because the Drupal date field can't deal with
  364. // miliseconds and causes the displayed time to rever to the
  365. // current time.
  366. if ($field_type == 'datetime') {
  367. $datevalue = $entity->{$field_name}['und'][0]['value'];
  368. $datevalue = preg_replace('/^(\d+-\d+-\d+ \d+:\d+:\d+)\.\d+$/', '\1', $datevalue);
  369. $entity->{$field_name}['und'][0]['value'] = $datevalue;
  370. }
  371. }
  372. }
  373. // Allow the creating module to alter the value if desired. The
  374. // module should do this if the field has any other form elements
  375. // that need populationg besides the value which was set above.
  376. tripal_load_include_field_class($field_type);
  377. if (class_exists($field_type) and is_subclass_of($field_type, 'TripalField')) {
  378. $tfield = new $field_type($field, $instance);
  379. $tfield->load($entity, ['record' => $record]);
  380. }
  381. // For text fields that were not handled by a TripalField class we
  382. // want to automatically expand those fields.
  383. else {
  384. if ($schema['fields'][$field_column]['type'] == 'text') {
  385. $record = chado_expand_var($record, 'field', "$field_table.$field_column");
  386. $entity->{$field_name}['und'][0]['value'] = $record->$field_column;
  387. // Text fields that have a text_processing == 1 setting need a
  388. // special 'format' element too:
  389. if ([
  390. key_exists('text_processing', $instance['settings']) and
  391. $instance['settings']['text_processing'] == 1,
  392. ]) {
  393. // TODO: we need a way to write the format back to the
  394. // instance settings if the user changes it when using the form.
  395. $entity->{$field_name}['und'][0]['format'] = array_key_exists('format', $instance['settings']) ? $instance['settings']['format'] : 'full_html';
  396. }
  397. }
  398. }
  399. }
  400. // Type 2: fields for linked records. These fields will have any number
  401. // of form elements that might need populating so we'll offload the
  402. // loading of these fields to the field itself.
  403. if ($field_table != $base_table) {
  404. // Set an empty value by default, and let the hook function update it.
  405. $entity->{$field_name}['und'][0]['value'] = '';
  406. tripal_load_include_field_class($field_type);
  407. if (class_exists($field_type) && method_exists($field_type, 'load')) {
  408. $tfield = new $field_type($field, $instance);
  409. $tfield->load($entity, ['record' => $record]);
  410. }
  411. }
  412. } // end: foreach ($fields as $field_id => $ids) {
  413. } // end: foreach ($entities as $id => $entity) {
  414. }
  415. /**
  416. * Merges the values of all fields into a single array keyed by table name.
  417. */
  418. function tripal_chado_field_storage_write_merge_fields($fields, $entity_type, $entity) {
  419. $all_fields = [];
  420. $base_fields = [];
  421. $field_items = [];
  422. // Iterate through all of the fields and organize them into a
  423. // new fields array keyed by the table name
  424. foreach ($fields as $field_id => $ids) {
  425. // Get the field name and information about it.
  426. $field = field_info_field_by_id($field_id);
  427. $field_name = $field['field_name'];
  428. $instance = field_info_instance('TripalEntity', $field['field_name'], $entity->bundle);
  429. // Some fields don't add data to
  430. // Chado so they don't have a table, but they are still attached to the
  431. // entity. Just skip these.
  432. if (!array_key_exists('chado_table', $instance['settings'])) {
  433. continue;
  434. }
  435. $chado_table = $instance['settings']['chado_table'];
  436. $chado_column = $instance['settings']['chado_column'];
  437. $base_table = $instance['settings']['base_table'];
  438. // We want to iterate through the field's items. We will also store the
  439. // field values in the $field_items array to be returned by this function.
  440. $items = field_get_items($entity_type, $entity, $field_name);
  441. $temp = [];
  442. $field_items[$field_name] = $items;
  443. // If there are no items for the field and it's a base table field then
  444. // we need to set it to NULL so that the value can be removed.
  445. if (empty($items)) {
  446. if (empty($items) and ($chado_table == $base_table)) {
  447. $base_fields[$chado_table][$chado_column] = '__NULL__';
  448. }
  449. }
  450. else {
  451. if (is_array($items)) {
  452. // Note: fields with cardinality ($delta) > 1 are multi-valued.
  453. foreach ($items as $delta => $item) {
  454. // A field may have multiple items. The field can use items
  455. // indexed with "chado-" to represent values that should map directly
  456. // to chado tables and fields.
  457. $value_set = FALSE;
  458. foreach ($item as $item_name => $value) {
  459. $matches = [];
  460. if (preg_match('/^chado-(.*?)__(.*?)$/', $item_name, $matches)) {
  461. $table_name = $matches[1];
  462. $column_name = $matches[2];
  463. // If this field belongs to the base table then we just add
  464. // those values in... there's no delta.
  465. if ($table_name == $base_table) {
  466. $base_fields[$table_name][$column_name] = $value;
  467. }
  468. else {
  469. $temp[$table_name][$delta][$column_name] = $value;
  470. }
  471. $value_set = TRUE;
  472. }
  473. }
  474. // If there is no value set for the field using the
  475. // chado-[table_name]__[field name] naming schema then check if a 'value'
  476. // item is present and if so use that for the table column value.
  477. if (!$value_set and array_key_exists('value', $items[$delta]) and
  478. !is_array($items[$delta]['value'])) {
  479. // If this field belongs to the base table then we just add
  480. // those values in... there's no delta.
  481. if ($base_table == $chado_table) {
  482. $base_fields[$chado_table][$chado_column] = $item['value'];
  483. }
  484. else {
  485. $temp[$chado_table][$delta][$chado_column] = $item['value'];
  486. }
  487. }
  488. }
  489. // Now merge the records for this field with the $new_fields array
  490. foreach ($temp as $table_name => $details) {
  491. foreach ($details as $delta => $list) {
  492. $all_fields[$table_name][] = $list;
  493. }
  494. }
  495. }
  496. }
  497. }
  498. $all_fields = array_merge($base_fields, $all_fields);
  499. return [$all_fields, $field_items];
  500. }
  501. /**
  502. * Implements hook_field_storage_query().
  503. */
  504. function tripal_chado_field_storage_query($query) {
  505. // Initialize the result array.
  506. $result = [
  507. 'TripalEntity' => [],
  508. ];
  509. // There must always be an entity filter that includes the bundle. Otherwise
  510. // it would be too overwhelming to search every table of every content type
  511. // for a matching field.
  512. if (!array_key_exists('bundle', $query->entityConditions)) {
  513. return $result;
  514. }
  515. $bundle = tripal_load_bundle_entity(['name' => $query->entityConditions['bundle']]);
  516. if (!$bundle) {
  517. return;
  518. }
  519. $data_table = $bundle->data_table;
  520. $type_column = $bundle->type_column;
  521. $type_id = $bundle->type_id;
  522. $schema = chado_get_schema($data_table);
  523. $pkey = $schema['primary key'][0];
  524. // Initialize the Query object.
  525. $cquery = chado_db_select($data_table, 'base');
  526. $cquery->fields('base', [$pkey]);
  527. // Iterate through all the conditions and add to the filters array
  528. // a chado_select_record compatible set of filters.
  529. foreach ($query->fieldConditions as $index => $condition) {
  530. $field = $condition['field'];
  531. $field_name = $field['field_name'];
  532. $field_type = $field['type'];
  533. // Skip conditions that don't belong to this storage type.
  534. if ($field['storage']['type'] != 'field_chado_storage') {
  535. continue;
  536. }
  537. // The Chado settings for a field are part of the instance and each bundle
  538. // can have the same field but with different Chado mappings. Therefore,
  539. // we need to iterate through the bundles to get the field instances.
  540. foreach ($field['bundles']['TripalEntity'] as $bundle_name) {
  541. // If there is a bundle filter for the entity and if the field is not
  542. // associated with the bundle then skip it.
  543. if (array_key_exists('bundle', $query->entityConditions)) {
  544. if (strtolower($query->entityConditions['bundle']['operator']) == 'in' and
  545. !array_key_exists($bundle_name, $query->entityConditions['bundle']['value'])) {
  546. continue;
  547. }
  548. else {
  549. if ($query->entityConditions['bundle']['value'] != $bundle_name) {
  550. continue;
  551. }
  552. }
  553. }
  554. // Allow the field to update the query object.
  555. $instance = field_info_instance('TripalEntity', $field['field_name'], $bundle_name);
  556. if (tripal_load_include_field_class($field_type)) {
  557. $field_obj = new $field_type($field, $instance);
  558. $field_obj->query($cquery, $condition);
  559. }
  560. // If there is not a ChadoField class for this field then add the
  561. // condition as is.
  562. else {
  563. $alias = $field['field_name'];
  564. $chado_table = $instance['settings']['chado_table'];
  565. $base_table = $instance['settings']['base_table'];
  566. $bschema = chado_get_schema($base_table);
  567. $bpkey = $bschema['primary key'][0];
  568. if ($chado_table == $base_table) {
  569. // Get the base table column that is associated with the term
  570. // passed as $condition['column'].
  571. $base_field = chado_get_semweb_column($chado_table, $condition['column']);
  572. $matches = [];
  573. $key = $condition['value'];
  574. $op = array_key_exists('operator', $condition) ? $condition['operator'] : '=';
  575. if (preg_match('/^(.+);(.+)$/', $key, $matches)) {
  576. $key = $matches[1];
  577. $op = $matches[2];
  578. }
  579. switch ($op) {
  580. case 'eq':
  581. $op = '=';
  582. break;
  583. case 'gt':
  584. $op = '>';
  585. break;
  586. case 'gte':
  587. $op = '>=';
  588. break;
  589. case 'lt':
  590. $op = '<';
  591. break;
  592. case 'lte':
  593. $op = '<=';
  594. break;
  595. case '<>':
  596. case 'ne':
  597. $op = '!=';
  598. break;
  599. case 'LIKE':
  600. case 'contains':
  601. $op = 'LIKE';
  602. if (!preg_match('/\%/', $key)) {
  603. $key = '%' . $key . '%';
  604. }
  605. break;
  606. case 'NOT LIKE':
  607. $op = 'NOT LIKE';
  608. if (!preg_match('/\%/', $key)) {
  609. $key = '%' . $key . '%';
  610. }
  611. break;
  612. case 'starts':
  613. $op = 'LIKE';
  614. $key = $key . '%';
  615. break;
  616. default:
  617. $op = '=';
  618. }
  619. $cquery->condition('base.' . $base_field, $key, $op);
  620. }
  621. if ($chado_table != $base_table) {
  622. // TODO: I don't think we'll get here because linker fields will
  623. // always have a custom field that should implement a query()
  624. // function. But just in case here's a note to handle it.
  625. }
  626. }
  627. }
  628. } // end foreach ($query->fieldConditions as $index => $condition) {
  629. // Now join with the chado entity table to get published records only.
  630. $chado_entity_table = chado_get_bundle_entity_table($bundle);
  631. $cquery->join($chado_entity_table, 'CE', "CE.record_id = base.$pkey");
  632. $cquery->join('tripal_entity', 'TE', "CE.entity_id = TE.id");
  633. $cquery->fields('CE', ['entity_id']);
  634. $cquery->fields('TE', ['bundle']);
  635. if (array_key_exists('start', $query->range)) {
  636. $cquery->range($query->range['start'], $query->range['length']);
  637. }
  638. // Make sure we only get records of the correct entity type
  639. $cquery->condition('TE.bundle', $query->entityConditions['bundle']['value']);
  640. // Iterate through all the property conditions. These will be filters
  641. // on the tripal_entity table.
  642. foreach ($query->propertyConditions as $index => $condition) {
  643. $cquery->condition('TE.' . $condition['column'], $condition['value']);
  644. }
  645. // If there is an entity_id filter then apply that.
  646. if (array_key_exists('entity_id', $query->entityConditions)) {
  647. $value = $query->entityConditions['entity_id']['value'];
  648. $op = '';
  649. if (array_key_exists('op', $query->entityConditions['entity_id'])) {
  650. $op = $query->entityConditions['entity_id']['op'];
  651. }
  652. // Handle a single entity_id
  653. if (!is_array($value)) {
  654. switch ($op) {
  655. case 'CONTAINS':
  656. $op = 'LIKE';
  657. $value = '%' . $value . '%';
  658. break;
  659. case 'STARTS_WITH':
  660. $op = 'LIKE';
  661. $value = $value . '%';
  662. break;
  663. case 'BETWEEN':
  664. // This case doesn't make sense for a single value. So, just set it
  665. // equal to the end.
  666. $op = '=';
  667. default:
  668. $op = $op ? $op : '=';
  669. }
  670. }
  671. // Handle multiple entitie IDs.
  672. else {
  673. switch ($op) {
  674. case 'CONTAINS':
  675. $op = 'IN';
  676. break;
  677. case 'STARTS_WITH':
  678. $op = 'IN';
  679. break;
  680. case 'BETWEEN':
  681. $op = 'BETWEEN';
  682. default:
  683. $op = 'IN';
  684. }
  685. }
  686. if ($op == 'BETWEEN') {
  687. $cquery->condition('TE.id', $value[0], '>');
  688. $cquery->condition('TE.id', $value[1], '<');
  689. }
  690. else {
  691. $cquery->condition('TE.id', $value, $op);
  692. }
  693. }
  694. // Now set any ordering.
  695. foreach ($query->order as $index => $sort) {
  696. // Add in property ordering.
  697. if ($sort['type'] == 'property') {
  698. // TODO: support ordering by bundle properties.
  699. }
  700. // Add in filter ordering
  701. if ($sort['type'] == 'field') {
  702. $field = $sort['specifier']['field'];
  703. $field_type = $field['type'];
  704. $field_name = $field['field_name'];
  705. // Skip sorts that don't belong to this storage type.
  706. if ($field['storage']['type'] != 'field_chado_storage') {
  707. continue;
  708. }
  709. $column = $sort['specifier']['column'];
  710. $direction = $sort['direction'];
  711. // The Chado settings for a field are part of the instance and each bundle
  712. // can have the same field but with different Chado mappings. Therefore,
  713. // we need to iterate through the bundles to get the field instances.
  714. foreach ($field['bundles']['TripalEntity'] as $bundle_name) {
  715. // If there is a bundle filter for the entity and if the field is not
  716. // associated with the bundle then skip it.
  717. if (array_key_exists('bundle', $query->entityConditions)) {
  718. if (strtolower($query->entityConditions['bundle']['operator']) == 'in' and
  719. !array_key_exists($bundle_name, $query->entityConditions['bundle']['value'])) {
  720. continue;
  721. }
  722. else {
  723. if ($query->entityConditions['bundle']['value'] != $bundle_name) {
  724. continue;
  725. }
  726. }
  727. }
  728. // See if there is a ChadoField class for this instance. If not then do
  729. // our best to order the field.
  730. $instance = field_info_instance('TripalEntity', $field_name, $bundle_name);
  731. if (tripal_load_include_field_class($field_type)) {
  732. $field_obj = new $field_type($field, $instance);
  733. $field_obj->queryOrder($cquery, [
  734. 'column' => $column,
  735. 'direction' => $direction,
  736. ]);
  737. }
  738. // There is no class so do our best to order the data by this field
  739. else {
  740. $base_table = $instance['settings']['base_table'];
  741. $chado_table = $instance['settings']['chado_table'];
  742. $table_column = chado_get_semweb_column($chado_table, $column);
  743. if ($table_column) {
  744. if ($chado_table == $base_table) {
  745. $cquery->orderBy('base.' . $table_column, $direction);
  746. }
  747. else {
  748. // TODO: how do we handle a field that doesn't map to the base table.
  749. // We would expect that all of these would be custom field and
  750. // the ChadoField::queryOrder() function would be implemented.
  751. }
  752. }
  753. else {
  754. // TODO: handle when the name can't be matched to a table column.
  755. }
  756. }
  757. } // end foreach ($field['bundles']['TripalEntity'] as $bundle_name) {
  758. } // end if ($sort['type'] == 'field') {
  759. } // end foreach ($query->order as $index => $sort) {
  760. // Only include records that are deleted. Tripal doesn't keep track of
  761. // records that are deleted that need purging separately so we can do nothing
  762. // with this.
  763. if (property_exists($query, 'deleted') and $query->deleted) {
  764. // There won't ever be field data marked as deleted so just created a
  765. // condition that always evaluates to false.
  766. $cquery->where('1=0');
  767. }
  768. // Please keep these dpm's they help with debugging
  769. // dpm($cquery->__toString());
  770. // dpm($cquery->getArguments());
  771. $records = $cquery->execute();
  772. // If this is not a count query then return the results.
  773. $result = [];
  774. while ($record = $records->fetchObject()) {
  775. $ids = [$record->entity_id, 0, $record->bundle];
  776. $result['TripalEntity'][$record->entity_id] = entity_create_stub_entity('TripalEntity', $ids);
  777. }
  778. return $result;
  779. }
  780. function tripal_chado_field_storage_bundle_mapping_form_get_defaults($form, $form_state) {
  781. // The term for the content type should already be set in the form state.
  782. $term = $form_state['values']['term'];
  783. // Get the form default values.
  784. $default = [
  785. 'table' => '',
  786. 'has_all' => '',
  787. 'use_linker' => '',
  788. 'use_prop' => '',
  789. 'use_cvterm' => '',
  790. 'cv_id' => '',
  791. 'type_column' => '',
  792. 'base_selected_term' => '',
  793. 'prop_selected_term' => '',
  794. 'prop_term_value' => '',
  795. 'stage' => 'table-select',
  796. ];
  797. // Get the stage.
  798. if (array_key_exists('chado-stage', $form_state)) {
  799. $default['stage'] = $form_state['chado-stage'];
  800. }
  801. if (array_key_exists('base_chado_table', $form_state['values'])
  802. and $form_state['values']['base_chado_table']) {
  803. $default['table'] = $form_state['values']['base_chado_table'];
  804. }
  805. else {
  806. $mapped_table = chado_get_cvterm_mapping(['cvterm_id' => $term->cvterm_id]);
  807. if ($mapped_table) {
  808. $default['table'] = $mapped_table->chado_table;
  809. }
  810. }
  811. if (array_key_exists('chado_table_has_all', $form_state['values'])
  812. and $form_state['values']['chado_table_has_all']) {
  813. $default['has_all'] = $form_state['values']['chado_table_has_all'];
  814. }
  815. if (array_key_exists('chado_type_use_linker', $form_state['values'])
  816. and $form_state['values']['chado_type_use_linker']) {
  817. $default['use_linker'] = $form_state['values']['chado_type_use_linker'];
  818. }
  819. if (array_key_exists('chado_type_use_prop', $form_state['values'])
  820. and $form_state['values']['chado_type_use_prop']) {
  821. $default['use_prop'] = $form_state['values']['chado_type_use_prop'];
  822. }
  823. if (array_key_exists('type_column', $form_state['values'])
  824. and $form_state['values']['type_column']) {
  825. $default['type_column'] = $form_state['values']['type_column'];
  826. }
  827. if (array_key_exists('prop_term_value', $form_state['values'])
  828. and $form_state['values']['prop_term_value']) {
  829. $default['prop_term_value'] = $form_state['values']['prop_term_value'];
  830. }
  831. if (array_key_exists('term_name1', $form_state['values'])) {
  832. $default['base_selected_term'] = tripal_get_term_lookup_form_result($form, $form_state, '', 1);
  833. }
  834. if (array_key_exists('term_name2', $form_state['values'])) {
  835. $default['prop_selected_term'] = tripal_get_term_lookup_form_result($form, $form_state, '', 2);
  836. }
  837. if (array_key_exists('chado_type_use_cv', $form_state['values'])
  838. and $form_state['values']['chado_type_use_cv']) {
  839. $default['use_cvterm'] = $form_state['values']['chado_type_use_cv'];
  840. }
  841. if (array_key_exists('chado_type_cv_id', $form_state['values'])
  842. and $form_state['values']['chado_type_cv_id']) {
  843. $default['cv_id'] = $form_state['values']['chado_type_cv_id'];
  844. }
  845. return $default;
  846. }
  847. /**
  848. * Implements hook_field_storage_bundle_mapping_form().
  849. *
  850. * This is a Tripal added hook to the field storage API.
  851. */
  852. function tripal_chado_field_storage_bundle_mapping_form($form, &$form_state, $term, &$submit_disabled) {
  853. $selected_term_id = (is_object($term)) ? $term->cvterm_id : NULL;
  854. $submit_disabled = TRUE;
  855. // Get the form defaults values from the form state.
  856. $default = tripal_chado_field_storage_bundle_mapping_form_get_defaults($form, $form_state);
  857. // Initialize the form.
  858. $form = [];
  859. $form['selected_cvterm_id'] = [
  860. '#type' => 'value',
  861. '#value' => $selected_term_id,
  862. ];
  863. tripal_chado_field_storage_bundle_mapping_form_summary($form, $form_state, $default);
  864. if ($default['stage'] == 'table-select') {
  865. tripal_chado_field_storage_bundle_mapping_form_table_select($form, $form_state, $term, $default);
  866. }
  867. else {
  868. if ($default['stage'] == 'has-all-select') {
  869. tripal_chado_field_storage_bundle_mapping_form_has_all_select($form, $form_state, $term, $default);
  870. }
  871. else {
  872. if ($default['stage'] == 'type-select') {
  873. tripal_chado_field_storage_bundle_mapping_form_type_select($form, $form_state, $term, $default);
  874. }
  875. else {
  876. if ($default['stage'] == 'prop-select') {
  877. tripal_chado_field_storage_bundle_mapping_form_prop_select($form, $form_state, $term, $default);
  878. }
  879. else {
  880. if ($default['stage'] == 'prop-settings') {
  881. tripal_chado_field_storage_bundle_mapping_form_prop_settings($form, $form_state, $term, $default);
  882. }
  883. else {
  884. if ($default['stage'] == 'linker-select') {
  885. tripal_chado_field_storage_bundle_mapping_form_linker_select($form, $form_state, $term, $default);
  886. }
  887. else {
  888. if ($default['stage'] == 'linker-settings') {
  889. tripal_chado_field_storage_bundle_mapping_form_linker_settings($form, $form_state, $term, $default);
  890. }
  891. else {
  892. if ($default['stage'] == 'complete') {
  893. $submit_disabled = FALSE;
  894. }
  895. }
  896. }
  897. }
  898. }
  899. }
  900. }
  901. }
  902. $form['chado-settings-reset'] = [
  903. '#type' => 'submit',
  904. '#value' => t('Reset'),
  905. '#name' => 'chado-settings-reset',
  906. '#validate' => [
  907. 'tripal_chado_field_storage_bundle_mapping_form_validate',
  908. 'tripal_admin_add_type_form_validate',
  909. ],
  910. ];
  911. return $form;
  912. }
  913. /**
  914. * Adds form elements for the summary of the selection.
  915. */
  916. function tripal_chado_field_storage_bundle_mapping_form_summary(&$form, &$form_state, $default) {
  917. $rows = [];
  918. $headers = [];
  919. // If we're on the first step then just return, there is no summary.
  920. if ($default['stage'] == 'table-select') {
  921. return;
  922. }
  923. // Make sure the form values are set.
  924. $form['base_chado_table'] = [
  925. '#type' => 'value',
  926. '#value' => $default['table'],
  927. ];
  928. $form['chado_table_has_all'] = [
  929. '#type' => 'value',
  930. '#value' => $default['has_all'],
  931. ];
  932. $form['type_column'] = [
  933. '#type' => 'value',
  934. '#value' => $default['type_column'],
  935. ];
  936. $form['chado_type_use_prop'] = [
  937. '#type' => 'value',
  938. '#value' => $default['use_prop'],
  939. ];
  940. $form['prop_term'] = [
  941. '#type' => 'value',
  942. '#value' => $default['prop_selected_term'],
  943. ];
  944. $form['base_term'] = [
  945. '#type' => 'value',
  946. '#value' => $default['base_selected_term'],
  947. ];
  948. $form['prop_term_value'] = [
  949. '#type' => 'value',
  950. '#value' => $default['prop_term_value'],
  951. ];
  952. $form['chado_type_use_linker'] = [
  953. '#type' => 'value',
  954. '#value' => $default['use_linker'],
  955. ];
  956. $form['chado_type_use_cv'] = [
  957. '#type' => 'value',
  958. '#value' => $default['use_cvterm'],
  959. ];
  960. $form['chado_type_cv_id'] = [
  961. '#type' => 'value',
  962. '#value' => $default['cv_id'],
  963. ];
  964. //
  965. // Add the base table row.
  966. //
  967. $rows[] = [
  968. [
  969. 'data' => 'Chado Base Table',
  970. 'header' => TRUE,
  971. 'width' => '20%',
  972. ],
  973. $default['table'],
  974. ];
  975. //
  976. // Add the row indicating if all of the records are of the type.
  977. //
  978. if ($default['has_all'] == 'Yes' or $default['has_all'] == 'No') {
  979. $message = 'Yes. All records in the ' . $default['table'] . ' table are of this type.';
  980. if ($default['has_all'] == 'No') {
  981. $message = 'No. All records in the ' . $default['table'] . ' table are not of this type.';
  982. }
  983. $rows[] = [
  984. [
  985. 'data' => 'All are of this type?',
  986. 'header' => TRUE,
  987. 'width' => '20%',
  988. ],
  989. $message,
  990. ];
  991. }
  992. //
  993. // Add the row indicating if the type_column is used.
  994. //
  995. if ($default['type_column']) {
  996. $message = $default['type_column'];
  997. if ($default['type_column'] == 'none') {
  998. $message = 'No type column is needed.';
  999. }
  1000. $rows[] = [
  1001. [
  1002. 'data' => 'Type Column',
  1003. 'header' => TRUE,
  1004. 'width' => '20%',
  1005. ],
  1006. $message,
  1007. ];
  1008. }
  1009. //
  1010. // Add the row indicating that the record can be distinguished with a prop.
  1011. //
  1012. if ($default['use_prop']) {
  1013. $message = 'Yes. The field is distinguishable using a property record.';
  1014. if ($default['use_prop'] == 'No') {
  1015. $message = 'No. The field is not distinguishable using a property record.';
  1016. }
  1017. $rows[] = [
  1018. [
  1019. 'data' => 'Use a property?',
  1020. 'header' => TRUE,
  1021. 'width' => '20%',
  1022. ],
  1023. $message,
  1024. ];
  1025. }
  1026. //
  1027. // Add the row for the base type for a property mapping
  1028. //
  1029. if ($default['use_prop'] == 'Yes' and $default['base_selected_term']) {
  1030. $term = $default['base_selected_term'][0];
  1031. $rows[] = [
  1032. [
  1033. 'data' => 'Base Type',
  1034. 'header' => TRUE,
  1035. 'width' => '20%',
  1036. ],
  1037. $term->name . ' (' . $term->dbxref_id->db_id->name . ':' . $term->dbxref_id->accession . ')',
  1038. ];
  1039. }
  1040. //
  1041. // Add the row for the type for a property mapping
  1042. //
  1043. if ($default['prop_selected_term']) {
  1044. $term = $default['prop_selected_term'][0];
  1045. $rows[] = [
  1046. [
  1047. 'data' => 'Property Type',
  1048. 'header' => TRUE,
  1049. 'width' => '20%',
  1050. ],
  1051. $term->name . ' (' . $term->dbxref_id->db_id->name . ':' . $term->dbxref_id->accession . ')',
  1052. ];
  1053. }
  1054. //
  1055. // Add the row for the value for a property mapping
  1056. //
  1057. if ($default['prop_term_value']) {
  1058. $rows[] = [
  1059. [
  1060. 'data' => 'Property Value',
  1061. 'header' => TRUE,
  1062. 'width' => '20%',
  1063. ],
  1064. $default['prop_term_value'],
  1065. ];
  1066. }
  1067. //
  1068. // Add the row for the linker table selection
  1069. //
  1070. if ($default['use_linker']) {
  1071. $message = 'Yes. The field is distinguishable using a cvterm linker table. The Content Type will be used to find records in the linker table';
  1072. if ($default['use_linker'] == 'No') {
  1073. $message = 'No. The field is not distinguishable using a cvterm linker table.';
  1074. }
  1075. $rows[] = [
  1076. [
  1077. 'data' => 'Use a Chado cvterm linker?',
  1078. 'header' => TRUE,
  1079. 'width' => '20%',
  1080. ],
  1081. $message,
  1082. ];
  1083. }
  1084. //
  1085. // Add the row for the base type for a property mapping
  1086. //
  1087. if ($default['use_linker'] == 'Yes' and $default['base_selected_term']) {
  1088. $term = $default['base_selected_term'][0];
  1089. $rows[] = [
  1090. [
  1091. 'data' => 'Base Type',
  1092. 'header' => TRUE,
  1093. 'width' => '20%',
  1094. ],
  1095. $term->name . ' (' . $term->dbxref_id->db_id->name . ':' . $term->dbxref_id->accession . ')',
  1096. ];
  1097. }
  1098. //
  1099. // Add the row for cvterm options
  1100. //
  1101. if ($default['use_cvterm']) {
  1102. $message = 'No. All records belong to a single controlled vocabulary.';
  1103. if ($default['use_cvterm'] == 'parent') {
  1104. $message = 'Yes. Records should include all child records of the specified term.';
  1105. }
  1106. $rows[] = [
  1107. [
  1108. 'data' => 'Use a Parent Chado cvterm?',
  1109. 'header' => TRUE,
  1110. 'width' => '20%',
  1111. ],
  1112. $message,
  1113. ];
  1114. // Add the cv if needed
  1115. if ($default['cv_id'] > 0) {
  1116. $cv_name = chado_query('SELECT name FROM chado.cv WHERE cv_id=:id',
  1117. [':id' => $default['cv_id']])->fetchField();
  1118. $rows[] = [
  1119. [
  1120. 'data' => 'Restrict to Vocabulary',
  1121. 'header' => TRUE,
  1122. 'width' => '20%',
  1123. ],
  1124. $cv_name,
  1125. ];
  1126. }
  1127. }
  1128. $table = [
  1129. 'header' => $headers,
  1130. 'rows' => $rows,
  1131. 'attributes' => [
  1132. ],
  1133. 'sticky' => FALSE,
  1134. 'caption' => '',
  1135. 'colgroups' => [],
  1136. 'empty' => '',
  1137. ];
  1138. $form['chado_settings_summary'] = [
  1139. '#type' => 'markup',
  1140. '#markup' => theme_table($table),
  1141. '#weight' => -100,
  1142. ];
  1143. if ($default['stage'] == 'complete') {
  1144. $form['im_ready'] = [
  1145. '#type' => 'markup',
  1146. '#markup' => '<p>Your content type is ready for creation! Please click the "Create Content Type" button below.</p>',
  1147. ];
  1148. }
  1149. if ($default['stage'] == 'stop') {
  1150. $form['im_ready'] = [
  1151. '#type' => 'markup',
  1152. '#markup' => '<p>Unfortunately, it is not possible to create this conent type. Please check your selections above. If you need help creating a content type, you may reach out to the Tripal Community for assistance.</p>',
  1153. ];
  1154. }
  1155. }
  1156. /**
  1157. * Adds form elements for Chado table selection step
  1158. */
  1159. function tripal_chado_field_storage_bundle_mapping_form_table_select(&$form, &$form_state, $term, $default) {
  1160. $base_tables = chado_get_base_tables();
  1161. $options = [0 => '-- Select table --'];
  1162. foreach ($base_tables AS $tablename) {
  1163. $options[$tablename] = $tablename;
  1164. }
  1165. $form['base_chado_table'] = [
  1166. '#type' => 'select',
  1167. '#title' => 'Chado table',
  1168. '#options' => $options,
  1169. '#description' => 'Select the Chado table into which the primary records for this content type will be stored.',
  1170. '#default_value' => $default['table'],
  1171. '#ajax' => [
  1172. 'callback' => "tripal_admin_add_type_form_ajax_callback",
  1173. 'wrapper' => "tripal-add-type-form",
  1174. 'effect' => 'fade',
  1175. 'method' => 'replace',
  1176. ],
  1177. ];
  1178. $form['table-select-continue'] = [
  1179. '#type' => 'submit',
  1180. '#value' => t('Continue'),
  1181. '#name' => 'chado-table-select-continue',
  1182. '#validate' => [
  1183. 'tripal_chado_field_storage_bundle_mapping_form_validate',
  1184. 'tripal_admin_add_type_form_validate',
  1185. ],
  1186. ];
  1187. }
  1188. /**
  1189. * Adds form elements for step 1: Chado type field selection.
  1190. */
  1191. function tripal_chado_field_storage_bundle_mapping_form_has_all_select(&$form, &$form_state, $term, $default) {
  1192. // Get the schema for this table.
  1193. $schema = chado_get_schema($default['table']);
  1194. // If the selected base table is 'cvterm' then handle it specially.
  1195. if ($default['table'] == 'cvterm') {
  1196. tripal_chado_field_storage_bundle_mapping_form_add_cvterm($form,
  1197. $form_state, $term, $default);
  1198. if ($default['use_cvterm'] == 'cv' and $default['cv_id']) {
  1199. $submit_disabled = FALSE;
  1200. }
  1201. if ($default['use_cvterm'] == 'parent') {
  1202. $submit_disabled = FALSE;
  1203. }
  1204. return;
  1205. }
  1206. $term_name = $term->name;
  1207. // Form elements to determine if all records in base table are of this type.
  1208. $form['chado_table_has_all'] = [
  1209. '#type' => 'radios',
  1210. '#options' => [
  1211. 'Yes' => 'Yes',
  1212. 'No' => 'No',
  1213. ],
  1214. '#title' => 'Are all records in the "' . $default['table'] .
  1215. '" table of type "' . $term_name . '"?',
  1216. '#description' => 'Select "No" if the "' .
  1217. $default['table'] . '" table houses more than just data of type "' .
  1218. $term_name . '".',
  1219. '#default_value' => $default['has_all'],
  1220. ];
  1221. $form['chado-has-all-select-continue'] = [
  1222. '#type' => 'submit',
  1223. '#value' => t('Continue'),
  1224. '#name' => 'chado-has-all-select-continue',
  1225. '#validate' => [
  1226. 'tripal_chado_field_storage_bundle_mapping_form_validate',
  1227. 'tripal_admin_add_type_form_validate',
  1228. ],
  1229. ];
  1230. }
  1231. /*
  1232. // If all the records in the table are of this type then we're done.
  1233. if ($default['has_all'] == 'Yes') {
  1234. $submit_disabled = FALSE;
  1235. return $form;
  1236. }
  1237. // If this table has a field that maps to a cvterm record then we want
  1238. // to ask if the user wants to use that field.
  1239. tripal_chado_field_storage_bundle_mapping_form_add_type($form,
  1240. $form_state, $term, $default);
  1241. // If the type_column is set then we're done! We know the deafult table
  1242. // and column to map this data type.
  1243. if (!empty($default['type_column']) and $default['type_column'] != 'none') {
  1244. $submit_disabled = FALSE;
  1245. return $form;
  1246. }
  1247. // Let's set the names of the linker and prop table for use below.
  1248. $linker_table = $default['table'] . '_cvterm';
  1249. $prop_table = $default['table']. 'prop';
  1250. $linker_exists = chado_table_exists($linker_table);
  1251. $prop_exists = chado_table_exists($prop_table);
  1252. // Ask the user if they want to use a linker table if it exists.
  1253. if ($prop_exists) {
  1254. tripal_chado_field_storage_bundle_mapping_form_add_prop($form,
  1255. $form_state, $term, $prop_table, $default);
  1256. // If the user wants to use the property table then we're done!
  1257. if ($default['use_prop'] == 'Yes') {
  1258. $submit_disabled = FALSE;
  1259. return $form;
  1260. }
  1261. // Ask if the user wants to use the linker table if it exists.
  1262. if ($linker_exists) {
  1263. tripal_chado_field_storage_bundle_mapping_form_add_linker($form,
  1264. $form_state, $term, $linker_table, $default);
  1265. // If the user wants to use the linker table then we're done!
  1266. if ($default['use_linker'] == 'Yes') {
  1267. $submit_disabled = FALSE;
  1268. return $form;
  1269. }
  1270. }
  1271. }
  1272. // A prop table doesn't exist then ask the user if they want
  1273. // to use the linker table to associate records.
  1274. else if ($linker_exists) {
  1275. tripal_chado_field_storage_bundle_mapping_form_add_prop($form,
  1276. $form_state, $term, $prop_table, $default);
  1277. // If the user wants to use the linker table then we're done!
  1278. if ($default['use_linker'] == 'Yes') {
  1279. $submit_disabled = FALSE;
  1280. return $form;
  1281. }
  1282. }
  1283. $form['notice'] = array(
  1284. '#type' => 'item',
  1285. '#markup' => '<font color="red">Unfortunately, the options selected above do not allow for mapping of this content type to records in Chado.</font>'
  1286. );
  1287. return $form;
  1288. }
  1289. */
  1290. /**
  1291. * Adds form elements for step 1: Chado type field selection.
  1292. */
  1293. function tripal_chado_field_storage_bundle_mapping_form_type_select(&$form, &$form_state, $term, $default) {
  1294. $term_name = $term->name;
  1295. $column_options = tripal_chado_field_storage_bundle_mapping_form_get_type_fks_options($default['table']);
  1296. $default_column = !empty($default['type_column']) ? $default['type_column'] : 'type_id';
  1297. $form['type_column'] = [
  1298. '#type' => 'select',
  1299. '#title' => 'Type Column',
  1300. '#options' => $column_options,
  1301. '#description' => 'Please select the column in the "' .
  1302. $default['table'] . '" table that will identify a record as being of type "' .
  1303. $term_name . '". If you select "--None--" then you will be asked
  1304. if the type can be identified using a property or linker table.
  1305. Only fields that have a foreign key to the cvterm table will be listed.',
  1306. '#default_value' => $default_column,
  1307. ];
  1308. $form['chado-type-select-continue'] = [
  1309. '#type' => 'submit',
  1310. '#value' => t('Continue'),
  1311. '#name' => 'chado-type-select-continue',
  1312. '#validate' => [
  1313. 'tripal_chado_field_storage_bundle_mapping_form_validate',
  1314. 'tripal_admin_add_type_form_validate',
  1315. ],
  1316. ];
  1317. }
  1318. /**
  1319. *
  1320. */
  1321. function tripal_chado_field_storage_bundle_mapping_form_prop_select(&$form, &$form_state, $term, $default) {
  1322. $prop_table = $default['table'] . 'prop';
  1323. $form['chado_type_use_prop'] = [
  1324. '#type' => 'radios',
  1325. '#title' => 'Do you want to use the "' . $prop_table . '" table
  1326. to distinguish between content types?',
  1327. '#options' => [
  1328. 'Yes' => 'Yes',
  1329. 'No' => 'No',
  1330. ],
  1331. '#description' => t('Sometimes records can be distinguished
  1332. using a value in property table, especially if there is no column
  1333. in the specified Chado table to identify the
  1334. record type. In these cases the property table can be used.'),
  1335. '#default_value' => $default['use_prop'],
  1336. ];
  1337. $form['chado-prop-select-continue'] = [
  1338. '#type' => 'submit',
  1339. '#value' => t('Continue'),
  1340. '#name' => 'chado-prop-select-continue',
  1341. '#validate' => [
  1342. 'tripal_chado_field_storage_bundle_mapping_form_validate',
  1343. 'tripal_admin_add_type_form_validate',
  1344. ],
  1345. ];
  1346. }
  1347. function tripal_chado_field_storage_bundle_mapping_form_prop_settings(&$form, &$form_state, $term, $default) {
  1348. $prop_term_value = $default['prop_term_value'];
  1349. $base_type_column = tripal_chado_field_storage_bundle_mapping_form_get_base_type_column($default['table']);
  1350. $validate = [
  1351. 'tripal_chado_field_storage_bundle_mapping_form_validate',
  1352. 'tripal_admin_add_type_form_validate',
  1353. ];
  1354. if ($base_type_column) {
  1355. $description = t('The ' . $default['table'] . ' table of Chado requires
  1356. that each record have a type. You have indicated that this
  1357. content type is not distinguished by a column in the base tabe but
  1358. instead by a property. However, the ' . $default['table'] . ' table requires
  1359. that a value be given for the ' . $base_type_column . '. Please indicate
  1360. what this type should be. For example, suppose this content type is a SNP (SO:0000694) and
  1361. you store SNPs in Chado in the feature table as "genetic_marker" (SO:0001645), however
  1362. the record is defined as a SNP through a corresponding record in the featurprop
  1363. table that has a type of "type" (rdfs:type) and a value of "SNP".
  1364. In this case the content type should be SNP (SO:0001645) and here the
  1365. "genetic_marker" (SO:0001645) term should be provided. The "type" term
  1366. should be used in the Property Type field below and "SNP" as the Property Value.');
  1367. tripal_get_term_lookup_form($form, $form_state, '',
  1368. 'Base Type', $description, FALSE, '', 1,
  1369. 'tripal_admin_add_type_form_ajax_callback', 'tripal-add-type-form',
  1370. $validate, -2);
  1371. }
  1372. // We need a term lookup form for the property type.
  1373. $description = t('The content type "' . $term->name . '" must be
  1374. associated with a property type. The property type must be the
  1375. name of a term in a controlled vocabulary and the controlled
  1376. vocabulary should already be loaded into Tripal. Please select
  1377. the property type that will be used to identify records of this
  1378. type');
  1379. tripal_get_term_lookup_form($form, $form_state, '',
  1380. 'Property Type', $description, FALSE, '', 2,
  1381. 'tripal_admin_add_type_form_ajax_callback', 'tripal-add-type-form',
  1382. $validate, -8);
  1383. $form['prop_term_value'] = [
  1384. '#type' => 'textfield',
  1385. '#title' => 'Property Value',
  1386. '#description' => t('All properties have a type and a value. Above you
  1387. indicated the type of property. Now you must specify the value
  1388. that this properly must have in order to be considered of
  1389. type "' . $term->name . '".'),
  1390. '#default_value' => $prop_term_value,
  1391. ];
  1392. $form['chado-prop-settings-continue'] = [
  1393. '#type' => 'submit',
  1394. '#value' => t('Continue'),
  1395. '#name' => 'chado-prop-settings-continue',
  1396. '#validate' => [
  1397. 'tripal_chado_field_storage_bundle_mapping_form_validate',
  1398. 'tripal_admin_add_type_form_validate',
  1399. ],
  1400. ];
  1401. }
  1402. /**
  1403. *
  1404. */
  1405. function tripal_chado_field_storage_bundle_mapping_form_linker_select(&$form, &$form_state, $term, $default) {
  1406. $linker_table = $default['table'] . '_cvterm';
  1407. $form['chado_type_use_linker'] = [
  1408. '#type' => 'radios',
  1409. '#title' => 'Do you want to use the "' . $linker_table . '" table
  1410. to distinguish between content types?',
  1411. '#options' => [
  1412. 'Yes' => 'Yes',
  1413. 'No' => 'No',
  1414. ],
  1415. '#description' => t('Sometimes records can be distinguished
  1416. using a linker table, especially if there is no column
  1417. in the specified Chado table to identify the
  1418. record type. In these cases the linker table can be used.'),
  1419. '#default_value' => $default['use_linker'],
  1420. ];
  1421. $form['chado-linker-select-continue'] = [
  1422. '#type' => 'submit',
  1423. '#value' => t('Continue'),
  1424. '#name' => 'chado-linker-select-continue',
  1425. '#validate' => [
  1426. 'tripal_chado_field_storage_bundle_mapping_form_validate',
  1427. 'tripal_admin_add_type_form_validate',
  1428. ],
  1429. ];
  1430. }
  1431. /**
  1432. *
  1433. */
  1434. function tripal_chado_field_storage_bundle_mapping_form_linker_settings(&$form, &$form_state, $term, $default) {
  1435. $base_type_column = tripal_chado_field_storage_bundle_mapping_form_get_base_type_column($default['table']);
  1436. $validate = [
  1437. 'tripal_chado_field_storage_bundle_mapping_form_validate',
  1438. 'tripal_admin_add_type_form_validate',
  1439. ];
  1440. $description = t('The ' . $default['table'] . ' table of Chado requires
  1441. that each record have a type. You have indicated that this
  1442. content type is not distinguished by a column in the base tabe but
  1443. instead by a cvterm linker table. However, the ' . $default['table'] . ' table requires
  1444. that a value be given for the ' . $base_type_column . '. Please indicate
  1445. what this type should be.');
  1446. tripal_get_term_lookup_form($form, $form_state, '',
  1447. 'Base Type', $description, FALSE, '', 1,
  1448. 'tripal_admin_add_type_form_ajax_callback', 'tripal-add-type-form',
  1449. $validate, -10);
  1450. $form['chado-linker-settings-continue'] = [
  1451. '#type' => 'submit',
  1452. '#value' => t('Continue'),
  1453. '#name' => 'chado-linker-settings-continue',
  1454. '#validate' => [
  1455. 'tripal_chado_field_storage_bundle_mapping_form_validate',
  1456. 'tripal_admin_add_type_form_validate',
  1457. ],
  1458. ];
  1459. }
  1460. /**
  1461. *
  1462. */
  1463. function tripal_chado_field_storage_bundle_mapping_form_get_type_fks_options($table) {
  1464. // Get the list of columns in the default table.
  1465. $schema = chado_get_schema($table);
  1466. $column_options = ['none' => '--None--'];
  1467. $cvt_fkeys = (isset($schema['foreign keys']['cvterm'])) ? array_keys($schema['foreign keys']['cvterm']['columns']) : [];
  1468. foreach ($schema['fields'] as $column_name => $column_details) {
  1469. if (in_array($column_name, $cvt_fkeys)) {
  1470. $column_options[$column_name] = $column_name;
  1471. }
  1472. }
  1473. return $column_options;
  1474. }
  1475. /**
  1476. *
  1477. */
  1478. function tripal_chado_field_storage_bundle_mapping_form_get_base_type_column($table) {
  1479. // If a base table has a 'type_id' field then we must have the user
  1480. // specify the base table type as well.
  1481. $base_type_column = FALSE;
  1482. $schema = chado_get_schema($table);
  1483. foreach ($schema['foreign keys'] as $fk_id => $details) {
  1484. if ($details['table'] == 'cvterm') {
  1485. foreach ($details['columns'] as $fk_left => $fk_right) {
  1486. if ($fk_left == 'type_id') {
  1487. $base_type_column = 'type_id';
  1488. }
  1489. }
  1490. }
  1491. }
  1492. return $base_type_column;
  1493. }
  1494. /**
  1495. * Provides the option form needed for a cvterm-based content type.
  1496. */
  1497. function tripal_chado_field_storage_bundle_mapping_form_add_cvterm(&$form,
  1498. &$form_state, $term, &$default) {
  1499. $form['chado_type_use_cv'] = [
  1500. '#type' => 'radios',
  1501. '#title' => 'How do you want to distinguish the "' . $term->name . '" records
  1502. within the cvterm table?',
  1503. '#options' => [
  1504. 'cv' => 'All records that belong to a single controlled vocabulary?',
  1505. // SPF: Commenting out until we can write the code to deal with
  1506. // using a parent term to indicate child elements that belong to the
  1507. // content type.
  1508. // 'parent' => 'All child records of the specified term?'
  1509. ],
  1510. '#description' => t('Records in the cvterm table are often not used for content
  1511. types. The records in the cvterm table are meant to be vocabulary terms.
  1512. However, there are times when records in the cvterm table may be
  1513. used for a content type. One example is for trait "pages" that
  1514. use phenotype values stored in the phenotype table. The vocabulary that
  1515. contains the "trait" terms should then be provided.'),
  1516. '#default_value' => $default['use_cvterm'],
  1517. ];
  1518. if ($default['use_cvterm'] == 'cv') {
  1519. $cvs = chado_get_cv_select_options();
  1520. $form['chado_type_cv_id'] = [
  1521. '#type' => 'select',
  1522. '#options' => $cvs,
  1523. '#title' => t('Select a controlled vocabulary'),
  1524. '#default_value' => $default['cv_id'],
  1525. ];
  1526. }
  1527. $form['chado-cvterm-settings-continue'] = [
  1528. '#type' => 'submit',
  1529. '#value' => t('Continue'),
  1530. '#name' => 'chado-cvterm-settings-continue',
  1531. '#validate' => [
  1532. 'tripal_chado_field_storage_bundle_mapping_form_validate',
  1533. 'tripal_admin_add_type_form_validate',
  1534. ],
  1535. ];
  1536. }
  1537. /**
  1538. * Implements hook_field_stoage_bundle_mapping_form_validate().
  1539. */
  1540. function tripal_chado_field_storage_bundle_mapping_form_validate($form, &$form_state) {
  1541. // Don't do validation on an ajax callback.
  1542. if (array_key_exists('#ajax', $form_state['triggering_element'])) {
  1543. return;
  1544. }
  1545. // Get the form values.
  1546. $default = tripal_chado_field_storage_bundle_mapping_form_get_defaults($form, $form_state);
  1547. $clicked_button = $form_state['clicked_button']['#name'];
  1548. // Check if prop and linker tables exist. We'll use these values later.
  1549. $prop_table = $default['table'] . 'prop';
  1550. $prop_exists = chado_table_exists($prop_table);
  1551. $linker_table = $default['table'] . '_cvterm';
  1552. $linker_exists = chado_table_exists($linker_table);
  1553. //
  1554. // Valiate the table select stage
  1555. //
  1556. if ($clicked_button == 'chado-table-select-continue') {
  1557. // Make sure a default table is selected
  1558. if (!$default['table']) {
  1559. form_set_error('base_chado_table', 'Please select a default table.');
  1560. return;
  1561. }
  1562. $form_state['chado-stage'] = 'has-all-select';
  1563. }
  1564. //
  1565. // Valiate the has all select stage
  1566. //
  1567. if ($clicked_button == 'chado-has-all-select-continue') {
  1568. if ($default['has_all'] == 'Yes') {
  1569. $form_state['chado-stage'] = 'complete';
  1570. }
  1571. else {
  1572. $column_options = tripal_chado_field_storage_bundle_mapping_form_get_type_fks_options($default['table']);
  1573. // If this table has fields that link to the cvterm table then
  1574. // let the user select the type column. The column_options is empty if
  1575. // it equals 1 because it has the --none-- element.
  1576. if (count($column_options) > 1) {
  1577. $form_state['chado-stage'] = 'type-select';
  1578. }
  1579. // Otherwise, see if there are other options....
  1580. else {
  1581. // If there is no type_id or the type_id column should not be
  1582. // used then we need to let the user select if they want to
  1583. // use the prop table or a cvterm linking table. We'll offer the
  1584. // prop table first.
  1585. if ($prop_exists) {
  1586. $form_state['chado-stage'] = 'prop-select';
  1587. }
  1588. else {
  1589. if ($linker_exists) {
  1590. $form_state['chado-stage'] = 'linker-select';
  1591. }
  1592. else {
  1593. $form_state['chado-stage'] = 'stop';
  1594. }
  1595. }
  1596. }
  1597. }
  1598. }
  1599. //
  1600. // Valiate the type select stage
  1601. //
  1602. if ($clicked_button == 'chado-type-select-continue') {
  1603. if ($default['type_column'] != 'none') {
  1604. $form_state['chado-stage'] = 'complete';
  1605. }
  1606. else {
  1607. // If there is no type_id or the type_id column should not be
  1608. // used then we need to let the user select if they want to
  1609. // use the prop table or a cvterm linking table. We'll offer the
  1610. // prop table first.
  1611. if ($prop_exists) {
  1612. $form_state['chado-stage'] = 'prop-select';
  1613. }
  1614. else {
  1615. if ($linker_exists) {
  1616. $form_state['chado-stage'] = 'linker-select';
  1617. }
  1618. else {
  1619. $form_state['chado-stage'] = 'stop';
  1620. }
  1621. }
  1622. }
  1623. }
  1624. //
  1625. // Valiate the property select stage
  1626. //
  1627. if ($clicked_button == 'chado-prop-select-continue') {
  1628. if ($default['use_prop'] == 'Yes') {
  1629. $form_state['chado-stage'] = 'prop-settings';
  1630. }
  1631. else {
  1632. if ($default['use_prop'] == 'No') {
  1633. if ($linker_exists) {
  1634. $form_state['chado-stage'] = 'linker-select';
  1635. }
  1636. else {
  1637. $form_state['chado-stage'] = 'stop';
  1638. }
  1639. }
  1640. else {
  1641. form_set_error('chado_type_use_prop', 'Please select a value.');
  1642. }
  1643. }
  1644. }
  1645. //
  1646. // Validate the property settings stage
  1647. //
  1648. if ($clicked_button == 'chado-prop-settings-continue') {
  1649. // If a base type was needed then make sure we have a value selected.
  1650. $valid = TRUE;
  1651. if (array_key_exists('term_name1', $form_state['values'])) {
  1652. $base_selected = $default['base_selected_term'];
  1653. if (count($base_selected) == 0) {
  1654. form_set_error('term_match1][term_name1', 'Please select a base table type vocabulary term.');
  1655. $valid = FALSE;
  1656. }
  1657. if (count($base_selected) > 1) {
  1658. form_set_error('term_match1][term_name1', 'Please select only one base table type vocabulary term.');
  1659. $valid = FALSE;
  1660. }
  1661. }
  1662. // Make sure we only have one property type selected
  1663. $prop_selected = $default['prop_selected_term'];
  1664. if (count($prop_selected) == 0) {
  1665. form_set_error('term_match2][term_name2', 'Please select a prpoerty type vocabulary term.');
  1666. $valid = FALSE;
  1667. }
  1668. if (count($prop_selected) > 1) {
  1669. form_set_error('term_match2][term_name2', 'Please select only one property type vocabulary term.');
  1670. $valid = FALSE;
  1671. }
  1672. if (!$default['prop_term_value']) {
  1673. form_set_error('prop_term_value', 'Please provide a property value.');
  1674. $valid = FALSE;
  1675. }
  1676. if (!$valid) {
  1677. return;
  1678. }
  1679. // If we're here then all validations passed and we are good to go!
  1680. $form_state['chado-stage'] = 'complete';
  1681. }
  1682. //
  1683. // Validate the linker settings stage
  1684. //
  1685. if ($clicked_button == 'chado-linker-select-continue') {
  1686. if ($default['use_linker'] == 'Yes') {
  1687. // If our base table has a type_id then we need need to make the
  1688. // user provide a value for it, otherwise we're done.
  1689. $base_type_column = tripal_chado_field_storage_bundle_mapping_form_get_base_type_column($default['table']);
  1690. if ($base_type_column) {
  1691. $form_state['chado-stage'] = 'linker-settings';
  1692. }
  1693. else {
  1694. $form_state['chado-stage'] = 'complete';
  1695. }
  1696. }
  1697. else {
  1698. if ($default['use_linker'] == 'No') {
  1699. $form_state['chado-stage'] = 'stop';
  1700. }
  1701. else {
  1702. form_set_error('chado_type_use_linker', 'Please select a value.');
  1703. }
  1704. }
  1705. }
  1706. if ($clicked_button == 'chado-linker-settings-continue') {
  1707. $valid = TRUE;
  1708. if (array_key_exists('term_name1', $form_state['values'])) {
  1709. $base_selected = $default['base_selected_term'];
  1710. if (count($base_selected) == 0) {
  1711. form_set_error('term_match1][term_name1', 'Please select a base table type vocabulary term.');
  1712. $valid = FALSE;
  1713. }
  1714. if (count($base_selected) > 1) {
  1715. form_set_error('term_match1][term_name1', 'Please select only one base table type vocabulary term.');
  1716. $valid = FALSE;
  1717. }
  1718. }
  1719. if (!$valid) {
  1720. return;
  1721. }
  1722. // If we're here then all validations passed and we are good to go!
  1723. $form_state['chado-stage'] = 'complete';
  1724. }
  1725. if ($clicked_button == 'chado-cvterm-settings-continue') {
  1726. // If the user chose to differentiate type based on cv_id and supplied a cv_id
  1727. // then we're done!
  1728. if ($form_state['values']['chado_type_use_cv'] == 'cv') {
  1729. if (isset($form_state['values']['chado_type_cv_id']) AND ($form_state['values']['chado_type_cv_id'] > 0)) {
  1730. $form_state['chado-stage'] = 'complete';
  1731. }
  1732. else {
  1733. if (isset($form['chado_type_cv_id'])) {
  1734. form_set_error('chado_type_cv_id', 'Please select a controlled vocabulary which contains records for this content type.');
  1735. }
  1736. }
  1737. }
  1738. // If the user chose to diffentiate type based on the current term,
  1739. // then we're done!
  1740. if ($form_state['values']['chado_type_use_cv'] == 'parent') {
  1741. $form_state['chado-stage'] = 'complete';
  1742. }
  1743. }
  1744. //
  1745. // Check for a reset.
  1746. //
  1747. if ($clicked_button == 'chado-settings-reset') {
  1748. $form_state['chado-stage'] = 'table-select';
  1749. $valid = FALSE;
  1750. }
  1751. }
  1752. /**
  1753. * Implements hook_field_stoage_bundle_mapping_form_submit().
  1754. *
  1755. * Here we do no action on submit other than set the storage_args
  1756. * variable that is passed by reference.
  1757. */
  1758. function tripal_chado_field_storage_bundle_mapping_form_submit($form,
  1759. &$form_state, $term, &$storage_args) {
  1760. $default = tripal_chado_field_storage_bundle_mapping_form_get_defaults($form, $form_state);
  1761. // If we have a type_column then we know this type uses a column in the
  1762. // base table, so we can set the storage args and return.
  1763. if ($default['type_column'] and $default['type_column'] != 'none') {
  1764. $storage_args['data_table'] = $default['table'];
  1765. $storage_args['type_column'] = $default['type_column'];
  1766. return;
  1767. }
  1768. // If the user indicated that all the records in a table are of this type.
  1769. if ($default['has_all'] == 'Yes') {
  1770. $storage_args['data_table'] = $default['table'];
  1771. $storage_args['type_id'] = $form_state['values']['selected_cvterm_id'];
  1772. $storage_args['type_linker_table'] = '';
  1773. $storage_args['type_column'] = '';
  1774. $storage_args['type_value'] = '';
  1775. }
  1776. // If the user indicated they wanted to use the prop table then we'll
  1777. // set the storage args and return.
  1778. else {
  1779. if ($default['use_prop'] == 'Yes') {
  1780. $storage_args['data_table'] = $default['table'];
  1781. $storage_args['type_linker_table'] = $default['table'] . 'prop';
  1782. $storage_args['type_column'] = 'type_id';
  1783. $storage_args['type_id'] = $form_state['values']['prop_term'][0]->cvterm_id;
  1784. $storage_args['type_value'] = $default['prop_term_value'];
  1785. if (is_array($form_state['values']['base_term'])) {
  1786. $storage_args['base_type_id'] = $form_state['values']['base_term'][0]->cvterm_id;
  1787. }
  1788. }
  1789. // If the user indicated they wanted to use the linker tables then we'll
  1790. // set the storage args and return.
  1791. else {
  1792. if ($default['use_linker'] == 'Yes') {
  1793. $storage_args['data_table'] = $default['table'];
  1794. $storage_args['type_id'] = $form_state['values']['selected_cvterm_id'];
  1795. $storage_args['type_linker_table'] = $default['table'] . '_cvterm';
  1796. $storage_args['type_column'] = 'cvterm_id';
  1797. if (is_array($form_state['values']['base_term'])) {
  1798. $storage_args['base_type_id'] = $form_state['values']['base_term'][0]->cvterm_id;
  1799. }
  1800. }
  1801. // If the user indicated they want to use a cv_id to control which cvterm
  1802. // records become pages then we'll set the stroage args and return.
  1803. else {
  1804. if (($default['use_cvterm'] == 'cv') AND ($default['cv_id'])) {
  1805. $storage_args['data_table'] = 'cvterm';
  1806. $storage_args['type_id'] = $form_state['values']['selected_cvterm_id'];
  1807. $storage_args['type_value'] = $default['cv_id'];
  1808. }
  1809. // If the user indicated they want to use a cvterm_id to control which cvterm
  1810. // records become pages then we'll set the stroage args and return.
  1811. elseif ($default['use_cvterm'] == 'cvterm') {
  1812. // @todo @lacey implement this case
  1813. drupal_set_message('This case currently is not supported.', 'warning');
  1814. }
  1815. }
  1816. }
  1817. }
  1818. }