chado_linker__relationship.inc 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039
  1. <?php
  2. class chado_linker__relationship extends TripalField {
  3. // The default lable for this field.
  4. public static $default_label = 'Relationships';
  5. // The default description for this field.
  6. public static $default_description = 'Relationships between records.';
  7. // Add any default settings elements. If you override the fieldSettingsForm()
  8. // or the instanceSettingsForm() functions then you need to be sure that
  9. // any settings you want those functions to manage are listed in this
  10. // array.
  11. public static $default_settings = array(
  12. 'chado_table' => '',
  13. 'chado_column' => '',
  14. 'base_table' => '',
  15. 'semantic_web' => '',
  16. );
  17. // Set this to the name of the storage backend that by default will support
  18. // this field.
  19. public static $default_storage = 'field_chado_storage';
  20. /**
  21. * @see TripalField::can_attach()
  22. */
  23. protected function setCanAttach() {
  24. $table_name = isset($this->details['chado_table']) ? $this->details['chado_table'] : '';
  25. $type_table = isset($this->details['chado_type_table']) ? $this->details['chado_type_table'] : '';
  26. $type_field = isset($this->details['chado_type_column']) ? $this->details['chado_type_column'] : '';
  27. $cv_id = isset($this->details['chado_cv_id']) ? $this->details['chado_cv_id'] : '';
  28. $cvterm_id = isset($this->details['chado_cvterm_id']) ? $this->details['chado_cvterm_id'] : '';
  29. // If the linker table does not exists then we don't want to add attach.
  30. $rel_table = $table_name . '_relationship';
  31. if (chado_table_exists($rel_table)) {
  32. $this->can_attach = TRUE;
  33. return;
  34. }
  35. $this->can_attach = FALSE;
  36. }
  37. /**
  38. * @see TripalField::setFieldName()
  39. */
  40. protected function setFieldName() {
  41. $table_name = isset($this->details['chado_table']) ? $this->details['chado_table'] : '';
  42. $type_table = isset($this->details['chado_type_table']) ? $this->details['chado_type_table'] : '';
  43. $type_field = isset($this->details['chado_type_column']) ? $this->details['chado_type_column'] : '';
  44. $cv_id = isset($this->details['chado_cv_id']) ? $this->details['chado_cv_id'] : '';
  45. $cvterm_id = isset($this->details['chado_cvterm_id']) ? $this->details['chado_cvterm_id'] : '';
  46. $this->field_name = $table_name . '_relationship';
  47. }
  48. /**
  49. * @see TripalField::create_info()
  50. */
  51. function createInfo() {
  52. if (!$this->can_attach) {
  53. return;
  54. }
  55. $table_name = $this->details['chado_table'];
  56. $type_table = $this->details['chado_type_table'];
  57. $type_field = $this->details['chado_type_column'];
  58. $cv_id = $this->details['chado_cv_id'];
  59. $cvterm_id = $this->details['chado_cvterm_id'];
  60. $rel_table = $table_name . '_relationship';
  61. $schema = chado_get_schema($rel_table);
  62. $pkey = $schema['primary key'][0];
  63. return array(
  64. 'field_name' => $this->field_name,
  65. 'type' => 'chado_linker__relationship',
  66. 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
  67. 'locked' => FALSE,
  68. 'storage' => array(
  69. 'type' => 'field_chado_storage',
  70. ),
  71. 'settings' => array(
  72. 'chado_table' => $rel_table,
  73. 'chado_column' => $pkey,
  74. 'base_table' => $table_name,
  75. 'semantic_web' => 'SBO:0000374',
  76. ),
  77. );
  78. }
  79. /**
  80. * @see TripalField::createInstanceInfo()
  81. */
  82. function createInstanceInfo() {
  83. if (!$this->can_attach) {
  84. return;
  85. }
  86. $table_name = $this->details['chado_table'];
  87. $type_table = $this->details['chado_type_table'];
  88. $type_field = $this->details['chado_type_column'];
  89. $cv_id = $this->details['chado_cv_id'];
  90. $cvterm_id = $this->details['chado_cvterm_id'];
  91. return array(
  92. 'field_name' => $this->field_name,
  93. 'entity_type' => $this->entity_type,
  94. 'bundle' => $this->bundle->name,
  95. 'label' => 'Relationships',
  96. 'description' => 'Other records with relationships to this record.',
  97. 'required' => FALSE,
  98. 'settings' => array(
  99. 'auto_attach' => FALSE,
  100. 'child_relationship_types' => '',
  101. 'relationship_types' => '',
  102. ),
  103. 'widget' => array(
  104. 'type' => 'chado_linker__relationship_widget',
  105. 'settings' => array(
  106. 'display_label' => 1,
  107. ),
  108. ),
  109. 'display' => array(
  110. 'default' => array(
  111. 'label' => 'above',
  112. 'type' => 'chado_linker__relationship_formatter',
  113. 'settings' => array(),
  114. ),
  115. ),
  116. );
  117. }
  118. /**
  119. * @see TripalField::widgetInfo()
  120. */
  121. public static function widgetInfo() {
  122. return array(
  123. 'chado_linker__relationship_widget' => array(
  124. 'label' => t('Relationship Settings'),
  125. 'field types' => array('chado_linker__relationship')
  126. ),
  127. );
  128. }
  129. /**
  130. * @see TripalField::formatterInfo()
  131. */
  132. static function formatterInfo() {
  133. return array(
  134. 'chado_linker__relationship_formatter' => array(
  135. 'label' => t('Relationships'),
  136. 'field types' => array('chado_linker__relationship'),
  137. 'settings' => array(
  138. ),
  139. ),
  140. );
  141. }
  142. /**
  143. * @see TripalField::formatterView()
  144. */
  145. static function formatterView(&$element, $entity_type, $entity,
  146. $field, $instance, $langcode, $items, $display) {
  147. // Get the settings
  148. $settings = $display['settings'];
  149. $rows = array();
  150. $headers = array('Subject' ,'Type', 'Object');
  151. $headers = array('Relationship');
  152. foreach ($items as $delta => $item) {
  153. if (!$item['value']) {
  154. continue;
  155. }
  156. $subject_name = $item['value']['subject']['name'];
  157. $subject_type = $item['value']['subject']['type'];
  158. $object_name = $item['value']['object']['name'];
  159. $object_type = $item['value']['object']['type'];
  160. $phrase = $item['value']['phrase'];
  161. // Handle some special cases.
  162. // For mRNA objects we don't want to show the CDS, exons, 5' UTR, etc.
  163. // we want to show the parent gene and the protein.
  164. if ($object_type == 'mRNA' and ($subject_type != 'polypeptide')) {
  165. continue;
  166. }
  167. if ($subject_type == 'mRNA' and ($object_type != 'gene')) {
  168. continue;
  169. }
  170. $phrase = preg_replace("/$subject_type/", "<b>$subject_type</b>", $phrase);
  171. $phrase = preg_replace("/$object_type/", "<b>$object_type</b>", $phrase);
  172. if (array_key_exists('entity', $item['value']['object'])) {
  173. list($entity_type, $object_entity_id) = explode(':', $item['value']['object']['entity']);
  174. if ($object_entity_id != $entity->id) {
  175. $link = l($object_name, 'bio_data/' . $object_entity_id);
  176. $phrase = preg_replace("/$object_name/", $link, $phrase);
  177. }
  178. }
  179. if (array_key_exists('entity', $item['value']['subject'])) {
  180. list($entity_type, $subject_entity_id) = explode(':', $item['value']['subject']['entity']);
  181. if ($subject_entity_id != $entity->id) {
  182. $link = l($subject_name, 'bio_data/' . $subject_entity_id);
  183. $phrase = preg_replace("/$subject_name/", $link, $phrase);
  184. }
  185. }
  186. $rows[] = array($phrase);
  187. }
  188. // the $table array contains the headers and rows array as well as other
  189. // options for controlling the display of the table. Additional
  190. // documentation can be found here:
  191. // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
  192. $table = array(
  193. 'header' => $headers,
  194. 'rows' => $rows,
  195. 'attributes' => array(
  196. 'id' => 'chado-linker--relationship-table',
  197. 'class' => 'tripal-data-table'
  198. ),
  199. 'sticky' => FALSE,
  200. 'caption' => '',
  201. 'colgroups' => array(),
  202. 'empty' => 'There are no relationships',
  203. );
  204. // once we have our table array structure defined, we call Drupal's theme_table()
  205. // function to generate the table.
  206. if (count($items) > 0) {
  207. $element[0] = array(
  208. '#type' => 'markup',
  209. '#markup' => theme_table($table),
  210. );
  211. }
  212. }
  213. /**
  214. * @see TripalField::widgetForm()
  215. */
  216. static function widgetForm(&$widget, &$form, &$form_state, $field, $instance,
  217. $langcode, $items, $delta, $element) {
  218. $field_name = $field['field_name'];
  219. $field_type = $field['type'];
  220. $field_table = $field['settings']['chado_table'];
  221. $field_column = $field['settings']['chado_column'];
  222. // Get the FK column that links to the base table.
  223. $chado_table = $field['settings']['chado_table'];
  224. $base_table = $field['settings']['base_table'];
  225. $schema = chado_get_schema($chado_table);
  226. $pkey = $schema['primary key'][0];
  227. $fkeys = array_values($schema['foreign keys'][$base_table]['columns']);
  228. $fkey = $fkeys[0];
  229. // Get the field defaults.
  230. $record_id = '';
  231. $fkey_value = $element['#entity'] ? $element['#entity']->chado_record_id : '';
  232. $subject_id = '';
  233. $subject_uniquename = '';
  234. $type_id = '';
  235. $type = '';
  236. $object_id = '';
  237. $object_uniquename = '';
  238. $value = '';
  239. $rank = '';
  240. // If the field already has a value then it will come through the $items
  241. // array. This happens when editing an existing record.
  242. if (array_key_exists($delta, $items)) {
  243. $record_id = isset($items[$delta][$field_table . '__' . $pkey]) ? $items[$delta][$field_table . '__' . $pkey] : '';
  244. $subject_id = isset($items[$delta][$field_table . '__subject_id']) ? $items[$delta][$field_table . '__subject_id'] : '';
  245. $type_id = isset($items[$delta][$field_table . '__type_id']) ? $items[$delta][$field_table . '__type_id'] : '';
  246. $object_id = isset($items[$delta][$field_table . '__object_id']) ? $items[$delta][$field_table . '__object_id'] : '';
  247. if (isset($items[$delta][$field_table . '__value'])) {
  248. $value = $items[$delta][$field_table . '__value'];
  249. }
  250. if (isset($items[$delta][$field_table . '__rank'])) {
  251. $rank = $items[$delta][$field_table . '__rank'];
  252. }
  253. $object_uniquename = isset($items[$delta]['object_name']) ? $items[$delta]['object_name'] : '';
  254. $subject_uniquename = isset($items[$delta]['subject_name']) ? $items[$delta]['subject_name'] : '';
  255. $type = isset($items[$delta]['type_name']) ? $items[$delta]['type_name'] : '';
  256. }
  257. // Check $form_state['values'] to see if an AJAX call set the values.
  258. if (array_key_exists('values', $form_state) and array_key_exists($delta, $form_state['values'])) {
  259. // $record_id = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_table . '__' . $pkey);
  260. // $subject_id = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_table . '__subject_id');
  261. // $subject_uniquename = tripal_chado_get_field_form_values($field_name, $form_state, $delta, 'subject_name');
  262. // $type_id = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_table . '__type_id');
  263. // $type = tripal_chado_get_field_form_values($field_name, $form_state, $delta, 'type_name');
  264. // $object_id = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_table . '__object_id');
  265. // $object_uniquename = tripal_chado_get_field_form_values($field_name, $form_state, $delta, 'object_name');
  266. // $value = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_table . '__value');
  267. // $rank = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_table . '__rank');
  268. }
  269. $widget['#table_name'] = $chado_table;
  270. $widget['#fkeys'] = $schema['foreign keys'];
  271. $widget['#base_table'] = $base_table;
  272. $widget['#chado_record_id'] = isset($form['#entity']) ? $form['#entity']->chado_record_id : '';
  273. //$widget['#element_validate'] = array('chado_linker__relationship_validate');
  274. $widget['#theme'] = 'chado_linker__relationship_widget';
  275. $widget['#prefix'] = "<span id='$chado_table-$delta'>";
  276. $widget['#suffix'] = "</span>";
  277. $widget['value'] = array(
  278. '#type' => 'value',
  279. '#value' => array_key_exists($delta, $items) ? $items[$delta]['value'] : '',
  280. );
  281. $widget[$field_table . '__' . $pkey] = array(
  282. '#type' => 'value',
  283. '#default_value' => $record_id,
  284. );
  285. $widget[$field_table . '__subject_id'] = array(
  286. '#type' => 'value',
  287. '#default_value' => $subject_id,
  288. );
  289. $widget[$field_table . '__type_id'] = array(
  290. '#type' => 'value',
  291. '#default_value' => $type_id,
  292. );
  293. $widget[$field_table . '__object_id'] = array(
  294. '#type' => 'value',
  295. '#default_value' => $object_id,
  296. );
  297. if (array_key_exists('value', $schema['fields'])) {
  298. $widget[$field_table . '__value'] = array(
  299. '#type' => 'value',
  300. '#default_value' => $value,
  301. );
  302. }
  303. if (array_key_exists('rank', $schema['fields'])) {
  304. $widget[$field_table . '__rank'] = array(
  305. '#type' => 'value',
  306. '#default_value' => $rank,
  307. );
  308. }
  309. $widget['subject_name'] = array(
  310. '#type' => 'textfield',
  311. '#title' => t('Subject'),
  312. '#default_value' => $subject_uniquename,
  313. '#required' => $element['#required'],
  314. '#maxlength' => array_key_exists('length', $schema['fields']['subject_id']) ? $schema['fields']['subject_id']['length'] : 255,
  315. '#size' => 35,
  316. '#autocomplete_path' => "admin/tripal/storage/chado/auto_name/$base_table",
  317. );
  318. // Set up available cvterms for selection
  319. $vocs = tripal_get_cv_select_options();
  320. $cv_id = isset($form_state['values'][$field_name]['und'][0]['vocabulary']) ? $form_state['values'][$field_name]['und'][0]['vocabulary'] : 0;
  321. // Try getting the cv_id from cvterm for existing records
  322. $default_term = '';
  323. if (!$cv_id && $type_id) {
  324. $cvterm = tripal_get_cvterm(array('cvterm_id' => $type_id));
  325. if (isset($cvterm->cv_id->cv_id)) {
  326. $cv_id = $cvterm->cv_id->cv_id;
  327. $default_term = $cvterm->name;
  328. }
  329. }
  330. $widget['vocabulary'] = array(
  331. '#type' => 'select',
  332. '#title' => t('Vocabulary'),
  333. '#options' => $vocs,
  334. '#required' => $element['#required'],
  335. '#default_value' => $cv_id,
  336. '#ajax' => array(
  337. 'callback' => "chado_linker__relationship_widget_form_ajax_callback",
  338. 'wrapper' => "$chado_table-$delta",
  339. 'effect' => 'fade',
  340. 'method' => 'replace'
  341. ),
  342. );
  343. if ($cv_id) {
  344. $options = array();
  345. $widget['type_name'] = array(
  346. '#type' => 'textfield',
  347. '#title' => t('Relationship Type'),
  348. '#size' => 15,
  349. '#default_value' => $default_term,
  350. '#autocomplete_path' => "admin/tripal/storage/chado/auto_name/cvterm/$cv_id"
  351. );
  352. }
  353. $widget['object_name'] = array(
  354. '#type' => 'textfield',
  355. '#title' => t('Object'),
  356. '#default_value' => $object_uniquename,
  357. '#required' => $element['#required'],
  358. '#maxlength' => array_key_exists('length', $schema['fields']['object_id']) ? $schema['fields']['object_id']['length'] : 255,
  359. '#size' => 35,
  360. '#autocomplete_path' => "admin/tripal/storage/chado/auto_name/$base_table",
  361. );
  362. }
  363. /**
  364. * @see TripalField::validate()
  365. */
  366. function validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
  367. $field_name = $field['field_name'];
  368. $field_type = $field['type'];
  369. $field_table = $field['settings']['chado_table'];
  370. $field_column = $field['settings']['chado_column'];
  371. $base_table = $field['settings']['base_table'];
  372. $chado_record_id = $entity->chado_record_id;
  373. $schema = chado_get_schema($field_table);
  374. $fkeys = $schema['foreign keys'];
  375. foreach ($items as $delta => $item) {
  376. $subject_id = $item[$field_table . '__subject_id'];
  377. $object_id = $item[ $field_table . '__object_id'];
  378. $type_id = $item[$field_table . '__type_id'];
  379. $type_name = $item['type_name'];
  380. $subject_name = $item['subject_name'];
  381. $object_name = $item['object_name'];
  382. // If the row is empty then just continue, there's nothing to validate.
  383. if (!$type_name and !$subject_name and !$object_name) {
  384. continue;
  385. }
  386. // Make sure we have values for all of the fields.
  387. $form_error = FALSE;
  388. if (!$type_name) {
  389. $errors[$field['field_name']][$langcode][$delta][] = array(
  390. 'error' => 'chado_linker__relationship',
  391. 'message' => t("Please provide the type of relationship."),
  392. );
  393. }
  394. if (!$subject_name) {
  395. $errors[$field['field_name']][$langcode][$delta][] = array(
  396. 'error' => 'chado_linker__relationship',
  397. 'message' => t("Please provide the subject of the relationship."),
  398. );
  399. }
  400. if (!$object_name) {
  401. $errors[$field['field_name']][$langcode][$delta][] = array(
  402. 'error' => 'chado_linker__relationship',
  403. 'message' => t("Please provide the object of the relationship."),
  404. );
  405. }
  406. if ($form_error) {
  407. continue;
  408. }
  409. // Before submitting this form we need to make sure that our subject_id and
  410. // object_ids are real values. There are two ways to get the value, either
  411. // just with the text value or with an [id: \d+] string embedded. If the
  412. // later we will pull it out.
  413. $subject_id = '';
  414. $fkey_rcolumn = $fkeys[$base_table]['columns']['subject_id'];
  415. $matches = array();
  416. if (preg_match('/\[id: (\d+)\]/', $subject_name, $matches)) {
  417. $subject_id = $matches[1];
  418. $values = array($fkey_rcolumn => $subject_id);
  419. $subject = chado_select_record($base_table, array($fkey_rcolumn), $values);
  420. if (count($subject) == 0) {
  421. $errors[$field['field_name']][$langcode][$delta][] = array(
  422. 'error' => 'chado_linker__relationship',
  423. 'message' => t("The subject record cannot be found using the specified id (e.g. [id: xx])."),
  424. );
  425. }
  426. }
  427. else {
  428. $values = array('uniquename' => $subject_name);
  429. $subject = chado_select_record($base_table, array($fkey_rcolumn), $values);
  430. if (count($subject) == 0) {
  431. $errors[$field['field_name']][$langcode][$delta][] = array(
  432. 'error' => 'chado_linker__relationship',
  433. 'message' => t("The subject record cannot be found. Please check spelling."),
  434. );
  435. }
  436. elseif (count($subject) > 1) {
  437. $errors[$field['field_name']][$langcode][$delta][] = array(
  438. 'error' => 'chado_linker__relationship',
  439. 'message' => t("The subject is not unique and therefore the relationship cannot be made."),
  440. );
  441. }
  442. }
  443. // Now check for a matching object.
  444. $object_id = '';
  445. $fkey_rcolumn = $fkeys[$base_table]['columns']['object_id'];
  446. $matches = array();
  447. if (preg_match('/\[id: (\d+)\]/', $object_name, $matches)) {
  448. $object_id = $matches[1];
  449. $values = array($fkey_rcolumn => $object_id);
  450. $object = chado_select_record($base_table, array($fkey_rcolumn), $values);
  451. if (count($subject) == 0) {
  452. $errors[$field['field_name']][$langcode][$delta][] = array(
  453. 'error' => 'chado_linker__relationship',
  454. 'message' => t("The object record cannot be found using the specified id (e.g. [id: xx])."),
  455. );
  456. }
  457. }
  458. else {
  459. $values = array('uniquename' => $object_name);
  460. $object = chado_select_record($base_table, array($fkey_rcolumn), $values);
  461. if (count($object) == 0) {
  462. $errors[$field['field_name']][$langcode][$delta][] = array(
  463. 'error' => 'chado_linker__relationship',
  464. 'message' => t("The object record cannot be found. Please check spelling."),
  465. );;
  466. }
  467. elseif (count($object) > 1) {
  468. $errors[$field['field_name']][$langcode][$delta][] = array(
  469. 'error' => 'chado_linker__relationship',
  470. 'message' => t("The object is not unique and therefore the relationship cannot be made."),
  471. );
  472. }
  473. }
  474. // Make sure that either our object or our subject refers to the base record.
  475. if ($object_id != $chado_record_id and $subject_id != $chado_record_id) {
  476. $errors[$field['field_name']][$langcode][$delta][] = array(
  477. 'error' => 'chado_linker__relationship',
  478. 'message' => t("Either the subject or the object in the relationship must refer to this record."),
  479. );
  480. }
  481. // Make sure that the object and subject are not both the same thing.
  482. if ($object_id == $subject_id) {
  483. $errors[$field['field_name']][$langcode][$delta][] = array(
  484. 'error' => 'chado_linker__relationship',
  485. 'message' => t("The subject and the object in the relationship cannot both refer to the same record."),
  486. );
  487. }
  488. }
  489. }
  490. /**
  491. * @see TripalField::submit()
  492. */
  493. public function submit($entity_type, $entity, $field, $instance, $langcode,
  494. &$items, $form, &$form_state) {
  495. $field_name = $field['field_name'];
  496. $field_type = $field['type'];
  497. $field_table = $field['settings']['chado_table'];
  498. $field_column = $field['settings']['chado_column'];
  499. $base_table = $field['settings']['base_table'];
  500. $chado_record_id = $entity->chado_record_id;
  501. $schema = chado_get_schema($field_table);
  502. $fkeys = $schema['foreign keys'];
  503. foreach ($items as $delta => $item) {
  504. $subject_id = $item[$field_table . '__subject_id'];
  505. $object_id = $item[ $field_table . '__object_id'];
  506. $type_id = $item[$field_table . '__type_id'];
  507. $type_name = $item['type_name'];
  508. $subject_name = $item['subject_name'];
  509. $object_name = $item['object_name'];
  510. // If the row is empty then skip this one, there's nothing to validate.
  511. if (!$type_name and !$subject_name and !$object_name) {
  512. continue;
  513. }
  514. // Get the subject ID.
  515. $subject_id = '';
  516. $fkey_rcolumn = $fkeys[$base_table]['columns']['subject_id'];
  517. $matches = array();
  518. if (preg_match('/\[id: (\d+)\]/', $subject_name, $matches)) {
  519. $subject_id = $matches[1];
  520. }
  521. else {
  522. $values = array('uniquename' => $subject_name);
  523. $subject = chado_select_record($base_table, array($fkey_rcolumn), $values);
  524. $subject_id = $subject[0]->$fkey_rcolumn;
  525. }
  526. // Get the object ID.
  527. $object_id = '';
  528. $fkey_rcolumn = $fkeys[$base_table]['columns']['object_id'];
  529. $matches = array();
  530. if (preg_match('/\[id: (\d+)\]/', $object_name, $matches)) {
  531. $object_id = $matches[1];
  532. }
  533. else {
  534. $values = array('uniquename' => $object_name);
  535. $object = chado_select_record($base_table, array($fkey_rcolumn), $values);
  536. $object_id = $object[0]->$fkey_rcolumn;
  537. }
  538. // Set the IDs according to the values that were determined above.
  539. $items[$delta][$field_table . '__subject_id'] = $subject_id;
  540. $items[$delta][$field_table . '__object_id'] = $object_id;
  541. $items[$delta][$field_table . '__type_id'] = $type_name;
  542. $items[$delta][$field_table . '__rank'] = $item['_weight'];
  543. }
  544. }
  545. /**
  546. * @see TripalField::load()
  547. */
  548. static function load($field, $entity, $details = array()) {
  549. $settings = $field['settings'];
  550. $record = $details['record'];
  551. $field_name = $field['field_name'];
  552. $field_type = $field['type'];
  553. $field_table = $field['settings']['chado_table'];
  554. $field_column = $field['settings']['chado_column'];
  555. $base_table = $field['settings']['base_table'];
  556. // Get the PKey for this table
  557. $schema = chado_get_schema($field_table);
  558. $pkey = $schema['primary key'][0];
  559. // Get the Pkeys for the subject and object tables
  560. $subject_fkey_table = '';
  561. $object_fkey_table = '';
  562. $fkeys = $schema['foreign keys'];
  563. foreach ($fkeys as $fktable => $details) {
  564. foreach ($details['columns'] as $fkey_lcolumn => $fkey_rcolumn) {
  565. if ($fkey_lcolumn == 'subject_id') {
  566. $subject_fkey_table = $fktable;
  567. }
  568. if ($fkey_lcolumn == 'object_id') {
  569. $object_fkey_table = $fktable;
  570. }
  571. }
  572. }
  573. $subject_schema = chado_get_schema($subject_fkey_table);
  574. $object_schema = chado_get_schema($object_fkey_table);
  575. $subject_pkey = $subject_schema['primary key'][0];
  576. $object_pkey = $object_schema['primary key'][0];
  577. // Get the FK that links to the base record.
  578. $schema = chado_get_schema($field_table);
  579. $fkey_lcolumn = key($schema['foreign keys'][$base_table]['columns']);
  580. $fkey_rcolumn = $schema['foreign keys'][$base_table]['columns'][$fkey_lcolumn];
  581. // Set some defaults for the empty record.
  582. // TODO: don't hardcode the uniquename as all tables won't have that.
  583. $entity->{$field_name}['und'][0] = array(
  584. 'value' => array(),
  585. $field_table . '__' . $pkey => '',
  586. $field_table . '__subject_id' => '',
  587. $field_table . '__object_id' => '',
  588. $field_table . '__type_id' => '',
  589. // These elements don't need to follow the naming scheme above
  590. // becasue we don't need the chado_field_storage to try and
  591. // save these values.
  592. 'object_name' => '',
  593. 'subject_name' => '',
  594. 'type_name' => '',
  595. );
  596. // If the table has rank and value fields then add those to the default
  597. // value array.
  598. if (array_key_exists('value', $schema['fields'])) {
  599. $entity->{$field_name}['und'][0][$field_table . '__value'] = '';
  600. }
  601. if (array_key_exists('rank', $schema['fields'])) {
  602. $entity->{$field_name}['und'][0][$field_table . '__rank'] = '';
  603. }
  604. // If we have no record then just return.
  605. if (!$record) {
  606. return;
  607. }
  608. // Expand the object to include the relationships.
  609. $options = array(
  610. 'return_array' => 1,
  611. // we don't want to fully recurse we only need information about the
  612. // relationship type and the object and subject
  613. 'include_fk' => array(
  614. 'type_id' => 1,
  615. 'object_id' => array(
  616. 'type_id' => 1,
  617. ),
  618. 'subject_id' => array(
  619. 'type_id' => 1,
  620. ),
  621. ),
  622. );
  623. $rel_table = $base_table . '_relationship';
  624. $schema = chado_get_schema($rel_table);
  625. if (array_key_exists('rank', $schema['fields'])) {
  626. $options['order_by'] = array('rank' => 'ASC');
  627. }
  628. $record = chado_expand_var($record, 'table', $rel_table, $options);
  629. if (!$record->$rel_table) {
  630. return;
  631. }
  632. $srelationships = $record->$rel_table->subject_id;
  633. $orelationships = $record->$rel_table->object_id;
  634. $i = 0;
  635. if ($orelationships) {
  636. foreach ($orelationships as $relationship) {
  637. $rel_acc = $relationship->type_id->dbxref_id->db_id->name . ':' . $relationship->type_id->dbxref_id->accession;
  638. $rel_type = $relationship->type_id->name;
  639. $verb = self::get_rel_verb($rel_type);
  640. $subject_name = $relationship->subject_id->name;
  641. $subject_type = $relationship->subject_id->type_id->name;
  642. $object_name = $relationship->object_id->name;
  643. $object_type = $relationship->object_id->type_id->name;
  644. $entity->{$field_name}['und'][$i]['value'] = array(
  645. 'type' => $relationship->type_id->name,
  646. 'subject' => array(
  647. 'type' => $subject_type,
  648. 'name' => $subject_name,
  649. ),
  650. 'type' => $relationship->type_id->name,
  651. 'object' => array(
  652. 'type' => $object_type,
  653. 'name' => $object_name,
  654. 'entity' => 'TripalEntity:' . $entity->id,
  655. )
  656. );
  657. if (property_exists($relationship->subject_id, 'uniquename')) {
  658. $entity->{$field_name}['und'][$i]['value']['subject']['identifier'] = $relationship->subject_id->uniquename;;
  659. }
  660. if (property_exists($relationship->object_id, 'uniquename')) {
  661. $entity->{$field_name}['und'][$i]['value']['object']['identifier'] = $relationship->object_id->uniquename;
  662. }
  663. if (property_exists($relationship->subject_id, 'entity_id')) {
  664. $entity_id = $relationship->subject_id->entity_id;
  665. $entity->{$field_name}['und'][$i]['value']['subject']['entity'] = 'TripalEntity:' . $entity_id;
  666. }
  667. $rel_type_clean = lcfirst(preg_replace('/_/', ' ', $rel_type));
  668. $entity->{$field_name}['und'][$i]['value']['phrase'] = 'The ' . $subject_type . ', ' .
  669. $subject_name . ', ' . $verb . ' ' . $rel_type_clean . ' this ' .
  670. $object_type . '.';
  671. $entity->{$field_name}['und'][$i]['semantic_web'] = array(
  672. 'type' => $rel_acc,
  673. 'subject' => $relationship->subject_id->type_id->dbxref_id->db_id->name . ':' . $relationship->subject_id->type_id->dbxref_id->accession,
  674. 'object' => $relationship->object_id->type_id->dbxref_id->db_id->name . ':' . $relationship->object_id->type_id->dbxref_id->accession,
  675. );
  676. $entity->{$field_name}['und'][$i][$field_table . '__' . $pkey] = $relationship->$pkey;
  677. $entity->{$field_name}['und'][$i][$field_table . '__subject_id'] = $relationship->subject_id->$subject_pkey;
  678. $entity->{$field_name}['und'][$i][$field_table . '__type_id'] = $relationship->type_id->cvterm_id;
  679. $entity->{$field_name}['und'][$i][$field_table . '__object_id'] = $relationship->object_id->$object_pkey;
  680. $entity->{$field_name}['und'][$i]['type_name'] = $relationship->type_id->name;
  681. $entity->{$field_name}['und'][$i]['subject_name'] = $relationship->subject_id->name . ' [id: ' . $relationship->subject_id->$fkey_rcolumn . ']';
  682. $entity->{$field_name}['und'][$i]['object_name'] = $relationship->object_id->name . ' [id: ' . $relationship->object_id->$fkey_rcolumn . ']';
  683. if (array_key_exists('value', $schema['fields'])) {
  684. $entity->{$field_name}['und'][$i][$field_table . '__value'] = $relationship->value;
  685. }
  686. if (array_key_exists('rank', $schema['fields'])) {
  687. $entity->{$field_name}['und'][$i][$field_table . '__rank'] = $relationship->rank;
  688. }
  689. $i++;
  690. }
  691. }
  692. if ($srelationships) {
  693. foreach ($srelationships as $relationship) {
  694. $rel_acc = $relationship->type_id->dbxref_id->db_id->name . ':' . $relationship->type_id->dbxref_id->accession;
  695. $rel_type = $relationship->type_id->name;
  696. $verb = self::get_rel_verb($rel_type);
  697. $subject_name = $relationship->subject_id->name;
  698. $subject_type = $relationship->subject_id->type_id->name;
  699. $object_name = $relationship->object_id->name;
  700. $object_type = $relationship->object_id->type_id->name;
  701. $entity->{$field_name}['und'][$i]['value'] = array(
  702. '@type' => $relationship->type_id->name,
  703. 'subject' => array(
  704. 'type' => $subject_type,
  705. 'name' => $subject_name,
  706. 'entity' => 'TripalEntity:' . $entity->id,
  707. ),
  708. 'type' => $relationship->type_id->name,
  709. 'object' => array(
  710. 'type' => $object_type,
  711. 'name' => $object_name,
  712. )
  713. );
  714. if (property_exists($relationship->subject_id, 'uniquename')) {
  715. $entity->{$field_name}['und'][$i]['value']['subject']['identifier'] = $relationship->subject_id->uniquename;
  716. }
  717. if (property_exists($relationship->object_id, 'uniquename')) {
  718. $entity->{$field_name}['und'][$i]['value']['object']['identifier'] = $relationship->object_id->uniquename;
  719. }
  720. if (property_exists($relationship->object_id, 'entity_id')) {
  721. $entity_id = $relationship->object_id->entity_id;
  722. $entity->{$field_name}['und'][$i]['value']['object']['entity'] = 'TripalEntity:' . $entity_id;
  723. }
  724. $rel_type_clean = lcfirst(preg_replace('/_/', ' ', $rel_type));
  725. $entity->{$field_name}['und'][$i]['value']['phrase'] = 'This ' .
  726. $subject_type . ' ' . $verb . ' ' . $rel_type_clean . ' the ' .
  727. $object_type . ', ' . $object_name . '.';
  728. $entity->{$field_name}['und'][$i]['semantic_web'] = array(
  729. 'type' => $rel_acc,
  730. 'subject' => $relationship->subject_id->type_id->dbxref_id->db_id->name . ':' . $relationship->subject_id->type_id->dbxref_id->accession,
  731. 'object' => $relationship->object_id->type_id->dbxref_id->db_id->name . ':' . $relationship->object_id->type_id->dbxref_id->accession,
  732. );
  733. $entity->{$field_name}['und'][$i][$field_table . '__' . $pkey] = $relationship->$pkey;
  734. $entity->{$field_name}['und'][$i][$field_table . '__subject_id'] = $relationship->subject_id->$subject_pkey;
  735. $entity->{$field_name}['und'][$i][$field_table . '__type_id'] = $relationship->type_id->cvterm_id;
  736. $entity->{$field_name}['und'][$i][$field_table . '__object_id'] = $relationship->object_id->$object_pkey;
  737. $entity->{$field_name}['und'][$i]['type_name'] = $relationship->type_id->name;
  738. $entity->{$field_name}['und'][$i]['subject_name'] = $relationship->subject_id->name . ' [id: ' . $relationship->subject_id->$fkey_rcolumn . ']';
  739. $entity->{$field_name}['und'][$i]['object_name'] = $relationship->object_id->name . ' [id: ' . $relationship->object_id->$fkey_rcolumn . ']';
  740. if (array_key_exists('value', $schema['fields'])) {
  741. $entity->{$field_name}['und'][$i][$field_table . '__value'] = $relationship->value;
  742. }
  743. if (array_key_exists('rank', $schema['fields'])) {
  744. $entity->{$field_name}['und'][$i][$field_table . '__rank'] = $relationship->rank;
  745. }
  746. $i++;
  747. }
  748. }
  749. }
  750. /**
  751. * A helper function to define English verbs for relationship types.
  752. *
  753. * @param $rel_type
  754. * The vocabulary term name for the relationship.
  755. *
  756. * @return
  757. * The verb to use when creating a sentence of the relationship.
  758. */
  759. public static function get_rel_verb($rel_type) {
  760. $rel_type_clean = lcfirst(preg_replace('/_/', ' ', $rel_type));
  761. $verb = $rel_type_clean;
  762. switch ($rel_type_clean) {
  763. case 'integral part of':
  764. case 'instance of':
  765. $verb = 'is an';
  766. break;
  767. case 'proper part of':
  768. case 'transformation of':
  769. case 'genome of':
  770. case 'part of':
  771. $verb = 'is a';
  772. case 'position of':
  773. case 'sequence of':
  774. case 'variant of':
  775. $verb = 'is a';
  776. break;
  777. case 'derives from':
  778. case 'connects on':
  779. case 'contains':
  780. case 'finishes':
  781. case 'guides':
  782. case 'has origin':
  783. case 'has part':
  784. case 'has quality':
  785. case 'is consecutive sequence of':
  786. case 'maximally overlaps':
  787. case 'overlaps':
  788. case 'starts':
  789. break;
  790. default:
  791. $verb = 'is';
  792. }
  793. return $verb;
  794. }
  795. /**
  796. * @see TripalField::instanceSettingsForm()
  797. */
  798. public static function instanceSettingsForm($field, $instance) {
  799. $element = parent::instanceSettingsForm($field, $instance);
  800. $element['relationships'] = array(
  801. '#type' => 'fieldset',
  802. '#title' => 'Allowed Relationship Types',
  803. '#description' => t('There are three ways that relationship types
  804. can be limited for users who have permission to add new relationships.
  805. Please select the most appropriate for you use case. By default
  806. all vocabularies are provided to the user which allows use of any
  807. term for the relationship type.'),
  808. '#collapsed' => TRUE,
  809. '#collapsible' => TRUE,
  810. );
  811. // $element['instructions'] = array(
  812. // '#type' => 'item',
  813. // '#markup' => 'You may provide a list of terms that will be available in a select box
  814. // as the relationship types. This select box will replace the vocabulary select box if the
  815. // following value is set.'
  816. // );
  817. $vocs = tripal_get_cv_select_options();
  818. $element['relationships']['option1'] = array(
  819. '#type' => 'item',
  820. '#title' => 'Option #1',
  821. '#description' => t('Use this option to limit the vocabularies that a user .
  822. could use to specify relationship types. With this option any term in .
  823. the vocabulary can be used for the relationship type. You may select
  824. more than one vocabulary.'),
  825. );
  826. $element['relationships']['option1_vocabs'] = array(
  827. '#type' => 'select',
  828. '#multiple' => TRUE,
  829. '#options' => $vocs,
  830. '#size' => 6,
  831. '#default_value' => $instance['settings']['default_vocabs'],
  832. // TODO add ajax here so that the relationship autocomplete below works
  833. );
  834. $element['relationships']['option2'] = array(
  835. '#type' => 'item',
  836. '#title' => '<b>Option #2</b>',
  837. '#description' => 'Some vocabularies are heirarchichal (an ontology). Within this
  838. heirarchy groups of related terms typically fall under a common parent. If you
  839. wish to limit the list of terms that a user can use for the relationship type,
  840. you can provide the parent term here. Then, only that term\'s children will
  841. then be avilable for use as a relationship type.',
  842. );
  843. $element['relationships']['option2_vocab'] = array(
  844. '#type' => 'select',
  845. '#description' => 'Specify Default Vocabularies',
  846. '#multiple' => FALSE,
  847. '#options' => $vocs,
  848. '#default_value' => $instance['settings']['default_vocabs'],
  849. // TODO add ajax here so that the relationship autocomplete below works
  850. );
  851. $element['relationships']['option2_parent'] = array(
  852. '#type' => 'textfield',
  853. '#description' => 'Specify a Heirarchical Parent Term',
  854. '#default_value' => $instance['settings']['child_relationship_types'],
  855. '#autocomplete_path' => "admin/tripal/storage/chado/auto_name/cvterm/"
  856. );
  857. $element['relationships']['option3'] = array(
  858. '#type' => 'item',
  859. '#title' => 'Option #3',
  860. '#description' => 'Provide terms separated by a new line. The term provided should be
  861. unique and distinguishable by the name.',
  862. );
  863. $element['relationships']['relationship_types'] = array(
  864. '#type' => 'textarea',
  865. '#default_value' => $instance['settings']['relationship_types'],
  866. );
  867. return $element;
  868. }
  869. /**
  870. * @see TripalField::instanceSettingsFormValidate()
  871. */
  872. public static function instanceSettingsFormValidate($field, $instance, $form, &$form_state) {
  873. //dpm($form);dpm($form_state);
  874. $settings = $form_state['values']['instance']['settings'];
  875. // Make sure the supplied types are valid cvterms
  876. if (isset($settings['relationship_types'])) {
  877. $rel_types = explode(PHP_EOL, $settings['relationship_types']);
  878. foreach($rel_types AS $type) {
  879. // Ignore empty lines
  880. if (trim($type) == '') {
  881. continue;
  882. }
  883. $term = tripal_get_cvterm(array('name' => trim($type)));
  884. // Don't save the form if a term can not be found or it matches more than one cvterm
  885. if (!isset($term->cvterm_id)) {
  886. form_set_error(
  887. "instance][settings][relationships][relationship_types",
  888. t("The term '@type' can not be found or matches more than one term.", array('@type' => $type))
  889. );
  890. }
  891. }
  892. }
  893. // Make sure child relationship types are using a valid cvterm
  894. if (isset($settings['child_relationship_types'])) {
  895. $supertype = $settings['child_relationship_types'];
  896. $term = tripal_get_cvterm(array('name' => trim($supertype)));
  897. if (!isset($term->cvterm_id)) {
  898. form_set_error(
  899. "instance][settings][relationships][child_relationship_types",
  900. t("The term '@type' can not be found or matches more than one term.", array('@type' => $supertype))
  901. );
  902. }
  903. }
  904. }
  905. }
  906. /**
  907. * Theme function for the chado_linker__relationship_widget.
  908. */
  909. function theme_chado_linker__relationship_widget($variables) {
  910. $element = $variables['element'];
  911. $field_name = $element['#field_name'];
  912. $field = field_info_field($field_name);
  913. $field_type = $field['type'];
  914. $field_table = $field['settings']['chado_table'];
  915. $field_column = $field['settings']['chado_column'];
  916. $layout = "
  917. <div class=\"chado-linker--relationship-widget\">
  918. <div class=\"chado-linker--relationship-widget-item\">" .
  919. drupal_render($element['subject_name']) . "
  920. </div>
  921. <div class=\"chado-linker--relationship-widget-item\">" .
  922. drupal_render($element['vocabulary']) . "
  923. </div>
  924. <div class=\"chado-linker--relationship-widget-item\">" .
  925. drupal_render($element['type_name']) . "
  926. </div>
  927. <div class=\"chado-linker--relationship-widget-item\">" .
  928. drupal_render($element['object_name']) . "
  929. </div>
  930. </div>
  931. ";
  932. return $layout;
  933. }
  934. /**
  935. * An Ajax callback for the dbxref widget.
  936. */
  937. function chado_linker__relationship_widget_form_ajax_callback(&$form, $form_state) {
  938. // Get the triggering element
  939. $form_element_name = $form_state['triggering_element']['#name'];
  940. preg_match('/(.+?)\[(.+?)\]\[(.+?)\]/', $form_element_name, $matches);
  941. $field = $matches[1];
  942. $lang = $matches[2];
  943. $delta = $matches[3];
  944. // Return the widget that triggered the AJAX call
  945. if (isset($form[$field][$lang][$delta])) {
  946. return $form[$field][$lang][$delta];
  947. }
  948. // Alternatively, return the default value widget for the widget setting form
  949. else {
  950. return $form['instance']['default_value_widget'][$field];
  951. }
  952. }