tripal_chado.db.api.inc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. <?php
  2. /**
  3. * @file
  4. * Provides API functions specificially for managing external database reference
  5. * records in Chado.
  6. */
  7. /**
  8. * @defgroup tripal_chado_database_api Chado DB
  9. * @ingroup tripal_chado_api
  10. * @{
  11. * External databases can be used to indicate the source for a variety of data.
  12. * The most common use is with controlled vocabularies (CV). Chado expects that
  13. * every CV have an external database record, where the database name must be
  14. * the short name of the CV. In other cases, records such as features, stocks,
  15. * libraries, etc., can also be present in remote databases and these
  16. * associations can be made through dbxref linker tables. The API functions
  17. * provided here provide tools to easily work with external databases.
  18. * @}
  19. */
  20. /**
  21. * Retrieves a chado db variable.
  22. *
  23. * Example Usage:
  24. *
  25. * @code
  26. * $select_values = array(
  27. * 'name' => 'SOFP'
  28. * );
  29. * $db_object = chado_get_db($select_values);
  30. * @endcode
  31. *
  32. * The above code selects the SOFP db and returns the following object:
  33. * @code
  34. * $db_object = stdClass Object (
  35. * [db_id] => 49
  36. * [name] => SOFP
  37. * [description] =>
  38. * [urlprefix] =>
  39. * [url] =>
  40. * );
  41. * @endcode
  42. *
  43. * @param $identifier
  44. * An array with the key stating what the identifier is. Supported keys (only
  45. * on of the following unique keys is required):
  46. * - db_id: the chado db.db_id primary key.
  47. * - name: the chado db.name field (assume unique).
  48. * @param $options
  49. * An array of options. Supported keys include:
  50. * - Any keys supported by chado_generate_var(). See that function
  51. * definition for additional details.
  52. *
  53. * NOTE: the $identifier parameter can really be any array similar to $values
  54. * passed into chado_select_record(). It should fully specify the db record to
  55. * be returned.
  56. *
  57. * @return
  58. * If unique values were passed in as an identifier then an object describing
  59. * the cv will be returned (will be a chado variable from
  60. * chado_generate_var()). Otherwise, an array of objects will be returned.
  61. *
  62. * @ingroup tripal_chado_database_api
  63. */
  64. function chado_get_db($identifiers, $options = []) {
  65. // Set Defaults.
  66. if (!isset($options['include_fk'])) {
  67. // Tells chado_generate_var not to follow any foreign keys.
  68. $options['include_fk'] = [];
  69. }
  70. // Error Checking of parameters.
  71. if (!is_array($identifiers)) {
  72. tripal_report_error(
  73. 'tripal_chado_database_api',
  74. TRIPAL_ERROR,
  75. "chado_get_db: The identifier passed in is expected to be an array with the key
  76. matching a column name in the db table (ie: db_id or name). You passed in %identifier.",
  77. [
  78. '%identifier' => print_r($identifiers, TRUE),
  79. ]
  80. );
  81. }
  82. elseif (empty($identifiers)) {
  83. tripal_report_error(
  84. 'tripal_chado_database_api',
  85. TRIPAL_ERROR,
  86. "chado_get_db: You did not pass in anything to identify the db you want. The identifier
  87. is expected to be an array with the key matching a column name in the db table
  88. (ie: db_id or name). You passed in %identifier.",
  89. [
  90. '%identifier' => print_r($identifiers, TRUE),
  91. ]
  92. );
  93. }
  94. // Try to get the db.
  95. $db = chado_generate_var(
  96. 'db',
  97. $identifiers,
  98. $options
  99. );
  100. // Ensure the db is singular. If it's an array then it is not singular.
  101. if (is_array($db)) {
  102. tripal_report_error(
  103. 'tripal_chado_database_api',
  104. TRIPAL_ERROR,
  105. "chado_get_db: The identifiers you passed in were not unique. You passed in %identifier.",
  106. [
  107. '%identifier' => print_r($identifiers, TRUE),
  108. ]
  109. );
  110. }
  111. // Report an error if $db is FALSE since then chado_generate_var has failed.
  112. elseif ($db === FALSE) {
  113. tripal_report_error(
  114. 'tripal_chado_database_api',
  115. TRIPAL_ERROR,
  116. "chado_get_db: chado_generate_var() failed to return a db based on the identifiers
  117. you passed in. You should check that your identifiers are correct, as well as, look
  118. for a chado_generate_var error for additional clues. You passed in %identifier.",
  119. [
  120. '%identifier' => print_r($identifiers, TRUE),
  121. ]
  122. );
  123. }
  124. // Else, as far we know, everything is fine so give them their db :)
  125. else {
  126. return $db;
  127. }
  128. }
  129. /**
  130. * Create an options array to be used in a form element
  131. * which provides a list of all chado dbs.
  132. *
  133. * @return
  134. * An array(db_id => name) for each db in the chado db table.
  135. *
  136. * @ingroup tripal_chado_database_api
  137. */
  138. function chado_get_db_select_options() {
  139. $dbs = chado_query("SELECT db_id, name FROM {db} ORDER BY name");
  140. $options = [];
  141. $options[] = 'Select a Database';
  142. foreach ($dbs as $db) {
  143. $options[$db->db_id] = $db->name;
  144. }
  145. return $options;
  146. }
  147. /**
  148. * Retrieves a chado database reference variable.
  149. *
  150. * Example Usage:
  151. *
  152. * @code
  153. * $identifiers = array(
  154. * 'accession' => 'synonym',
  155. * 'db_id' => array(
  156. * 'name' => 'SOFP'
  157. * )
  158. * );
  159. * $dbxref_object = chado_get_dbxref($identifiers);
  160. * @endcode
  161. * The above code selects the synonym database reference and returns the
  162. * following object:
  163. * @code
  164. * $dbxref_object = stdClass Object (
  165. * [dbxref_id] => 2581
  166. * [accession] => synonym
  167. * [description] =>
  168. * [version] =>
  169. * [db_db_id] => 49
  170. * [db_name] => SOFP
  171. * [db_description] =>
  172. * [db_urlprefix] =>
  173. * [db_url] =>
  174. * );
  175. * @endcode
  176. *
  177. * @param $identifier
  178. * An array apropriate for use with the chado_generate_var for uniquely
  179. * identifying a dbxref record. Alternatively, there are also some specially
  180. * handled keys. They are:
  181. * - property: An array/object describing the property to select records for.
  182. * It should at least have either a type_name (if unique across cvs) or
  183. * type_id. Other supported keys include: cv_id/cv_name (of the type),
  184. * value and rank.
  185. * @param $options
  186. * An array of options. Supported keys include:
  187. * - Any keys supported by chado_generate_var(). See that function
  188. * definition for additional details.
  189. *
  190. * NOTE: the $identifier parameter can really be any array similar to $values
  191. * passed into chado_select_record(). It should fully specify the dbxref record
  192. * to be returned.
  193. *
  194. * @return
  195. * If unique values were passed in as an identifier then an object describing
  196. * the dbxref will be returned (will be a chado variable from
  197. * chado_generate_var()). Otherwise, FALSE will be returned.
  198. *
  199. * @ingroup tripal_chado_database_api
  200. */
  201. function chado_get_dbxref($identifiers, $options = []) {
  202. // Set Defaults.
  203. if (!isset($options['include_fk'])) {
  204. // Tells chado_generate_var not only expand the db.
  205. $options['include_fk'] = ['db_id' => TRUE];
  206. }
  207. // Error Checking of parameters.
  208. if (!is_array($identifiers)) {
  209. tripal_report_error('tripal_db_api', TRIPAL_ERROR,
  210. "chado_get_dbxref: The identifier passed in is expected to be an array with the key
  211. matching a column name in the dbxref table (ie: dbxref_id or name). You passed in %identifier.",
  212. ['%identifier' => print_r($identifiers, TRUE)]
  213. );
  214. }
  215. elseif (empty($identifiers)) {
  216. tripal_report_error('tripal_db_api', TRIPAL_ERROR,
  217. "chado_get_dbxref: You did not pass in anything to identify the dbxref you want. The identifier
  218. is expected to be an array with the key matching a column name in the dbxref table
  219. (ie: dbxref_id or name). You passed in %identifier.",
  220. ['%identifier' => print_r($identifiers, TRUE)]
  221. );
  222. }
  223. // If one of the identifiers is property then use chado_get_record_with_property().
  224. if (isset($identifiers['property'])) {
  225. $property = $identifiers['property'];
  226. unset($identifiers['property']);
  227. $dbxref = chado_get_record_with_property(
  228. ['table' => 'dbxref', 'base_records' => $identifiers],
  229. ['type_name' => $property],
  230. $options
  231. );
  232. }
  233. // Else we have a simple case and we can just use chado_generate_var to get
  234. // the analysis.
  235. else {
  236. $dbxref = chado_generate_var('dbxref', $identifiers, $options);
  237. }
  238. // Ensure the dbxref is singular. If it's an array then it is not singular.
  239. if (is_array($dbxref)) {
  240. tripal_report_error('tripal_db_api', TRIPAL_ERROR,
  241. "chado_get_dbxref: The identifiers you passed in were not unique. You passed in %identifier.",
  242. ['%identifier' => print_r($identifiers, TRUE)]
  243. );
  244. }
  245. // Report an error if $dbxref is FALSE since then chado_generate_var has
  246. // failed.
  247. elseif ($dbxref === FALSE) {
  248. tripal_report_error(
  249. 'tripal_db_api',
  250. TRIPAL_ERROR,
  251. "chado_get_dbxref: chado_generate_var() failed to return a dbxref based on the identifiers
  252. you passed in. You should check that your identifiers are correct, as well as, look
  253. for a chado_generate_var error for additional clues. You passed in %identifier.",
  254. [
  255. '%identifier' => print_r($identifiers, TRUE),
  256. ]
  257. );
  258. }
  259. // Else, as far we know, everything is fine so give them their dbxref :)
  260. else {
  261. return $dbxref;
  262. }
  263. }
  264. /**
  265. * Generates a URL for the controlled vocabulary term.
  266. *
  267. * If the URL and URL prefix are provided for the database record of a cvterm
  268. * then a URL can be created for the term. By default, the db.name and
  269. * dbxref.accession are concatenated and appended to the end of the
  270. * db.urlprefix. But Tripal supports the use of {db} and {accession} tokens
  271. * when if present in the db.urlprefix string will be replaced with the db.name
  272. * and dbxref.accession respectively.
  273. *
  274. * @param $dbxref
  275. * A dbxref object as created by the chado_generate_var() function.
  276. *
  277. * @return
  278. * A string containing the URL.
  279. *
  280. * @ingroup tripal_chado_database_api
  281. */
  282. function chado_get_dbxref_url($dbxref) {
  283. $final_url = '';
  284. // Create the URL for the term.
  285. if ($dbxref->db_id->urlprefix) {
  286. $db_count = 0;
  287. $acc_count = 0;
  288. $url = $dbxref->db_id->urlprefix;
  289. // If the URL prefix has replacement tokens then use those.
  290. $url = preg_replace('/\{db\}/', $dbxref->db_id->name, $url, -1, $db_count);
  291. $url = preg_replace('/\{accession\}/', $dbxref->accession, $url, -1, $acc_count);
  292. $final_url = $url;
  293. // If no replacements were made above then tokens weren't used and we can
  294. // default to just appending the db name and accession to the end.
  295. if (!$db_count and !$acc_count) {
  296. $final_url = $dbxref->db_id->urlprefix . $dbxref->db_id->name . ':' . $dbxref->accession;
  297. }
  298. // If the URL prefix is relative then convert it to a full URL.
  299. if (!preg_match('/^(http|https)/', $final_url)) {
  300. $final_url = url($final_url, ['absolute' => TRUE]);
  301. }
  302. }
  303. return $final_url;
  304. }
  305. /**
  306. * Adds a new database to the Chado DB table and returns the DB object.
  307. *
  308. * @param $values
  309. * An associative array of the values of the db (those to be inserted):
  310. * - name: The name of the database. This name is usually used as the prefix
  311. * for CV term accessions.
  312. * - description: (Optional) A description of the database. By default no
  313. * description is required.
  314. * - url: (Optional) The URL for the database.
  315. * - urlprefix: (Optional) The URL that is to be used as a prefix when
  316. * constructing a link to a database term.
  317. * @param $options
  318. * Optional. An associative array of options that can include:
  319. * - update_existing: Set this to '1' to force an update of the database if it
  320. * already exists. The default is to not update. If the database exists
  321. * then nothing is added.
  322. *
  323. * @return
  324. * An object populated with fields from the newly added database. If the
  325. * database already exists it returns the values in the current entry.
  326. *
  327. * @ingroup tripal_chado_database_api
  328. */
  329. function chado_insert_db($values, $options = []) {
  330. // Default Values.
  331. $dbname = $values['name'];
  332. $description = (isset($values['description'])) ? $values['description'] : '';
  333. $url = (isset($values['url'])) ? $values['url'] : '';
  334. $urlprefix = (isset($values['urlprefix'])) ? $values['urlprefix'] : '';
  335. $update = (isset($options['update_existing'])) ? $options['update_existing'] : TRUE;
  336. // Build the values array for inserting/updating.
  337. $ins_values = [
  338. 'name' => $dbname,
  339. 'description' => $description,
  340. 'url' => $url,
  341. 'urlprefix' => $urlprefix,
  342. ];
  343. // Get the database record if it already exists.
  344. $sel_values = ['name' => $dbname];
  345. $result = chado_select_record('db', ['*'], $sel_values);
  346. // If it does not exists then add it.
  347. if (count($result) == 0) {
  348. $ins_options = ['statement_name' => 'ins_db_nadeurur'];
  349. $success = chado_insert_record('db', $ins_values, $ins_options);
  350. if (!$success) {
  351. tripal_report_error('tripal_chado', TRIPAL_WARNING, "Cannot create db '$dbname'.", NULL);
  352. return 0;
  353. }
  354. $result = chado_select_record('db', ['*'], $sel_values);
  355. }
  356. // If it exists and update is enabled the do the update.
  357. elseif ($update) {
  358. $upd_options = ['statement_name' => 'upd_db_nadeurur'];
  359. $success = chado_update_record('db', $sel_values, $ins_values, $upd_options);
  360. if (!$success) {
  361. tripal_report_error('tripal_chado', TRIPAL_WARNING, "Cannot update db '$dbname'.", NULL);
  362. return 0;
  363. }
  364. $result = chado_select_record('db', ['*'], $sel_values);
  365. }
  366. // Return the database object.
  367. return $result[0];
  368. }
  369. /**
  370. * Add a database reference.
  371. *
  372. * @param $values
  373. * An associative array of the values to be inserted including:
  374. * - db_id: the database_id of the database the reference is from.
  375. * - accession: the accession.
  376. * - version: (Optional) The version of the database reference.
  377. * - description: (Optional) A description of the database reference.
  378. *
  379. * @return
  380. * The newly inserted dbxref as an object, similar to that returned by
  381. * the chado_select_record() function.
  382. *
  383. * @ingroup tripal_chado_database_api
  384. */
  385. function chado_insert_dbxref($values) {
  386. $db_id = $values['db_id'];
  387. $accession = $values['accession'];
  388. $version = (isset($values['version'])) ? $values['version'] : '';
  389. $description = (isset($values['description'])) ? $values['description'] : '';
  390. $ins_values = [
  391. 'db_id' => $db_id,
  392. 'accession' => $accession,
  393. 'version' => $version,
  394. 'description' => $description,
  395. ];
  396. // Check to see if the dbxref exists.
  397. $sel_values = [
  398. 'db_id' => $db_id,
  399. 'accession' => $accession,
  400. 'version' => $version,
  401. ];
  402. $result = chado_select_record('dbxref', ['*'], $sel_values);
  403. // If it doesn't already exist then add it.
  404. if (!$result) {
  405. $success = chado_insert_record('dbxref', $ins_values);
  406. if (!$success) {
  407. tripal_report_error('tripal_chado', TRIPAL_WARNING, "Failed to insert the dbxref record $accession", NULL);
  408. return 0;
  409. }
  410. $result = chado_select_record('dbxref', ['*'], $sel_values);
  411. }
  412. if (isset($result[0])) {
  413. return $result[0];
  414. }
  415. else {
  416. return FALSE;
  417. }
  418. }
  419. /**
  420. * Add a record to a database reference linking table (ie: feature_dbxref).
  421. *
  422. * @param $basetable
  423. * The base table for which the dbxref should be associated. Thus to associate
  424. * a dbxref with a feature the basetable=feature and dbxref_id is added to the
  425. * feature_dbxref table.
  426. * @param $record_id
  427. * The primary key of the basetable to associate the dbxref with. This should
  428. * be in integer.
  429. * @param $dbxref
  430. * An associative array describing the dbxref. Valid keys include:
  431. * 'accession' => the accession for the dbxref, 'db_name' => the name of the
  432. * database the dbxref belongs to.
  433. * 'db_id' => the primary key of the database the dbxref belongs to.
  434. * @param $options
  435. * An associative array of options. Valid keys include:
  436. * - insert_dbxref: Insert the dbxref if it doesn't already exist. TRUE is
  437. * the default.
  438. *
  439. * @ingroup tripal_chado_database_api
  440. */
  441. function chado_associate_dbxref($basetable, $record_id, $dbxref, $options = []) {
  442. $linking_table = $basetable . '_dbxref';
  443. $foreignkey_name = $basetable . '_id';
  444. // Default Values.
  445. $options['insert_dbxref'] = (isset($options['insert_dbxref'])) ? $options['insert_dbxref'] : TRUE;
  446. // If the dbxref_id is set then we know it already exists.
  447. // Otherwise, select to check.
  448. if (!isset($dbxref['dbxref_id'])) {
  449. $values = [
  450. 'accession' => $dbxref['accession'],
  451. ];
  452. if (isset($dbxref['db_id'])) {
  453. $values['db_id'] = $dbxref['db_id'];
  454. }
  455. elseif (isset($dbxref['db_name'])) {
  456. $values['db_id'] = [
  457. 'name' => $dbxref['db_name'],
  458. ];
  459. }
  460. else {
  461. tripal_report_error(
  462. 'tripal_chado_database_api',
  463. TRIPAL_WARNING,
  464. "chado_associate_dbxref: The dbxref needs to have either the db_name or db_id
  465. supplied. You were trying to associate a dbxref with the %base %record_id
  466. and supplied the dbxref values: %dbxref.",
  467. [
  468. '%base' => $basetable,
  469. '%record_id' => $record_id,
  470. '%dbxref' => print_r($dbxref, TRUE),
  471. ]
  472. );
  473. return FALSE;
  474. }
  475. $select = chado_select_record('dbxref', ['*'], $values);
  476. if ($select) {
  477. $dbxref['dbxref_id'] = $select[0]->dbxref_id;
  478. }
  479. elseif ($options['insert_dbxref']) {
  480. // Insert the dbxref.
  481. $insert = chado_insert_dbxref($values);
  482. if (isset($insert->dbxref_id)) {
  483. $dbxref['dbxref_id'] = $insert->dbxref_id;
  484. }
  485. else {
  486. tripal_report_error(
  487. 'tripal_chado_database_api',
  488. TRIPAL_WARNING,
  489. "chado_associate_dbxref: Unable to insert the dbxref using the dbxref values: %dbxref.",
  490. ['%dbxref' => print_r($dbxref, TRUE)]
  491. );
  492. return FALSE;
  493. }
  494. }
  495. else {
  496. tripal_report_error(
  497. 'tripal_api',
  498. TRIPAL_WARNING,
  499. "chado_associate_dbxref: The dbxref doesn't already exist. You supplied the dbxref values: %dbxref.",
  500. ['%dbxref' => print_r($dbxref, TRUE)]
  501. );
  502. return FALSE;
  503. }
  504. }
  505. // Now add the link between the record & dbxref.
  506. if ($dbxref['dbxref_id'] > 0) {
  507. $values = [
  508. 'dbxref_id' => $dbxref['dbxref_id'],
  509. $foreignkey_name => $record_id,
  510. ];
  511. $result = chado_select_record($linking_table, ['*'], $values);
  512. // If it doesn't already exist then add it.
  513. if (!$result) {
  514. $success = chado_insert_record($linking_table, $values);
  515. if (!$success) {
  516. tripal_report_error(
  517. 'tripal_api',
  518. TRIPAL_WARNING,
  519. "Failed to insert the %base record %accession",
  520. ['%base' => $linking_table, '%accession' => $dbxref['accession']]
  521. );
  522. return FALSE;
  523. }
  524. $result = chado_select_record($linking_table, ['*'], $values);
  525. }
  526. if (isset($result[0])) {
  527. return $result[0];
  528. }
  529. else {
  530. return FALSE;
  531. }
  532. }
  533. return FALSE;
  534. }
  535. /**
  536. * This function is intended to be used in autocomplete forms
  537. * for searching for accession that begin with the provided string.
  538. *
  539. * @param $db_id
  540. * The DB ID in which to search for the term.
  541. * @param $string
  542. * The string to search for.
  543. *
  544. * @return
  545. * A json array of terms that begin with the provided string.
  546. *
  547. * @ingroup tripal_chado_database_api
  548. */
  549. function chado_autocomplete_dbxref($db_id, $string = '') {
  550. if (!$db_id) {
  551. return drupal_json_output([]);
  552. }
  553. $sql = "
  554. SELECT dbxref_id, accession
  555. FROM {dbxref}
  556. WHERE db_id = :db_id and lower(accession) like lower(:accession)
  557. ORDER by accession
  558. LIMIT 25 OFFSET 0
  559. ";
  560. $results = chado_query($sql, [
  561. ':db_id' => $db_id,
  562. ':accession' => $string . '%',
  563. ]);
  564. $items = [];
  565. foreach ($results as $ref) {
  566. $items[$ref->accession] = $ref->accession;
  567. }
  568. drupal_json_output($items);
  569. }