custom_tables.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. <?php
  2. /**
  3. * @file
  4. * Contains functions for the Custom Tables API
  5. * @defgroup tripal_custom_tables_api Core Module Custom Tables API
  6. * @{
  7. * Provides an application programming interface (API) to manage custom tables in Chado.
  8. * @}
  9. * @ingroup tripal_api
  10. */
  11. /**
  12. * Edits a custom table in the chado database. It supports
  13. * using the Drupal Schema API array.
  14. *
  15. * @param $table_id
  16. * The table_id of the table to edit
  17. * @param $table_name
  18. * The name of the custom table
  19. * @param $schema
  20. * Use the Schema API array to define the custom table.
  21. * @param $skip_creation
  22. * Set as TRUE to skip dropping and re-creation of the table. This is
  23. * useful if the table was already created through another means and you
  24. * simply want to make Tripal aware of the table schema.
  25. *
  26. * @ingroup tripal_custom_tables_api
  27. */
  28. function tripal_core_edit_custom_table($table_id, $table_name, $schema, $skip_creation = 0) {
  29. // Create a new record
  30. $record = new stdClass();
  31. $record->table_id = $table_id;
  32. $record->table_name = $table_name;
  33. $record->schema = serialize($schema);
  34. // get the current custom table record
  35. $sql = "SELECT * FROM {tripal_custom_tables} WHERE table_id = %d";
  36. $custom_table = db_fetch_object(db_query($sql, $table_id));
  37. // drop the table from chado if it exists
  38. if(!$skip_creation){
  39. if (db_table_exists($custom_table->table_name)) {
  40. chado_query("DROP TABLE %s", $custom_table->table_name);
  41. drupal_set_message(t("Custom Table '%name' dropped", array('%name' => $custom_table->table_name)));
  42. }
  43. }
  44. // update the custom table record and re-create the table in Chado
  45. if (drupal_write_record('tripal_custom_tables', $record, 'table_id')) {
  46. // drop the table from chado if it exists
  47. if(!$skip_creation){
  48. if (db_table_exists($custom_table->table_name)) {
  49. chado_query("DROP TABLE %s", $custom_table->table_name);
  50. drupal_set_message(t("Custom Table '%name' dropped", array('%name' => $custom_table->table_name)));
  51. }
  52. // re-create the table
  53. if (!tripal_core_create_custom_table ($ret, $table_name, $schema)) {
  54. drupal_set_message(t("Could not create the custom table. Check Drupal error report logs."));
  55. }
  56. else {
  57. drupal_set_message(t("Custom table '%name' created", array('%name' => $table_name)));
  58. }
  59. }
  60. // TODO: add FK constraints
  61. }
  62. }
  63. /**
  64. * Add a new table to the Chado schema. This function is simply a wrapper for
  65. * the db_create_table() function of Drupal, but ensures the table is created
  66. * inside the Chado schema rather than the Drupal schema. If the table already
  67. * exists then it will be dropped and recreated using the schema provided.
  68. * However, it will only drop a table if it exsits in the tripal_custom_tables
  69. * table. This way the function cannot be used to accidentally alter existing
  70. * non custom tables. If $skip_creation is set then the table is simply
  71. * added to the tripal_custom_tables and no table is created in Chado.
  72. *
  73. * @param $ret
  74. * Array to which query results will be added.
  75. * @param $table
  76. * The name of the table to create.
  77. * @param $schema
  78. * A Drupal-style Schema API definition of the table
  79. * @param $skip_creation
  80. * Set as TRUE to skip dropping and re-creation of the table. This is
  81. * useful if the table was already created through another means and you
  82. * simply want to make Tripal aware of the table schema.
  83. *
  84. * @return
  85. * A database query result resource for the new table, or FALSE if table was not constructed.
  86. *
  87. * @ingroup tripal_custom_tables_api
  88. */
  89. function tripal_core_create_custom_table(&$ret, $table, $schema, $skip_creation = 0) {
  90. $ret = array();
  91. // see if the table entry already exists
  92. $sql = "SELECT * FROM {tripal_custom_tables} WHERE table_name = '%s'";
  93. $centry = db_fetch_object(db_query($sql, $table));
  94. // If the table exits in Chado but not in the tripal_custom_tables field
  95. // then call an error. if the table exits in the tripal_custom_tables but
  96. // not in Chado then create the table and replace the entry.
  97. $previous_db = tripal_db_set_active('chado'); // use chado database
  98. $exists = db_table_exists($table);
  99. tripal_db_set_active($previous_db); // now use drupal database
  100. if (!$exists) {
  101. $previous_db = tripal_db_set_active('chado'); // use chado database
  102. db_create_table($ret, $table, $schema);
  103. tripal_db_set_active($previous_db); // now use drupal database
  104. if (count($ret)==0) {
  105. watchdog('tripal_core', "Error adding custom table '!table_name'.",
  106. array('!table_name' => $table), WATCHDOG_ERROR);
  107. return FALSE;
  108. }
  109. }
  110. if ($exists and !$centry and !$skip_creation) {
  111. watchdog('tripal_core', "Could not add custom table '!table_name'. It ".
  112. "already exists but is not known to Tripal as being a custom table.",
  113. array('!table_name' => $table), WATCHDOG_WARNING);
  114. return FALSE;
  115. }
  116. if ($exists and $centry and !$skip_creation) {
  117. // drop the table we'll recreate it with the new schema
  118. $previous_db = tripal_db_set_active('chado'); // use chado database
  119. db_drop_table($ret, $table);
  120. db_create_table($ret, $table, $schema);
  121. tripal_db_set_active($previous_db); // now use drupal database
  122. }
  123. // add an entry in the tripal_custom_table
  124. $record = new stdClass();
  125. $record->table_name = $table;
  126. $record->schema = serialize($schema);
  127. // if an entry already exists then remove it
  128. if ($centry) {
  129. $sql = "DELETE FROM {tripal_custom_tables} WHERE table_name = '%s'";
  130. db_query($sql, $table);
  131. }
  132. $success = drupal_write_record('tripal_custom_tables', $record);
  133. if (!$success) {
  134. watchdog('tripal_core', "Error adding custom table.",
  135. array('!table_name' => $table), WATCHDOG_ERROR);
  136. return FALSE;
  137. }
  138. return $ret;
  139. }
  140. /**
  141. * Retrieve the custom table id given the name
  142. *
  143. * @param $table_name
  144. * The name of the custom table
  145. *
  146. * @return
  147. * The unique identifier for the given table
  148. *
  149. * @ingroup tripal_custom_tables_api
  150. */
  151. function tripal_custom_tables_get_table_id($table_name) {
  152. $sql = "SELECT * FROM {tripal_custom_tables} ".
  153. "WHERE table_name = '%s'";
  154. if (db_table_exists('tripal_custom_tables')) {
  155. $custom_table = db_fetch_object(db_query($sql, $table_name));
  156. if ($custom_table) {
  157. return $custom_table->table_id;
  158. }
  159. }
  160. return FALSE;
  161. }
  162. /**
  163. * A template function which returns markup to display details for the custom table
  164. *
  165. * @param $table_id
  166. * The unique ID of the custom table
  167. *
  168. * @ingroup tripal_custom_tables_api
  169. */
  170. function tripal_custom_table_view($table_id) {
  171. // get this custom_table details
  172. $sql = "SELECT * FROM {tripal_custom_tables} WHERE table_id = %d";
  173. $custom_table = db_fetch_object(db_query($sql, $table_id));
  174. // create a table with each row containig stats for
  175. // an individual job in the results set.
  176. $return_url = url("admin/tripal/custom_tables/");
  177. $output .= "<p><a href=\"$return_url\">" . t("Return to list of custom tables") . "</a></p>";
  178. $output .= "<br />";
  179. $output .= "<p>Details for <b>$custom_table->table_name</b>:</p>";
  180. $output .= "<br />";
  181. $output .= "<table class=\"border-table\">";
  182. if ($custom_table->table_name) {
  183. $output .= " <tr>".
  184. " <th>Table Name</th>".
  185. " <td>$custom_table->table_name</td>".
  186. " </tr>";
  187. }
  188. if ($custom_table->schema) {
  189. $output .= " <tr>".
  190. " <th>Table Field Definitions</th>".
  191. " <td><pre>" . var_export(unserialize($custom_table->schema),1) . "</pre></td>".
  192. " </tr>";
  193. }
  194. // build the URLs using the url function so we can handle installations where
  195. // clean URLs are or are not used
  196. $delete_url = url("admin/tripal/custom_tables/action/delete/$custom_table->table_id");
  197. $edit_url = url("admin/tripal/custom_tables/edit/$custom_table->table_id");
  198. $output .= "<tr><th>Actions</th>".
  199. "<td>".
  200. " <a href='$edit_url'>Edit</a>, ".
  201. " <a href='$delete_url'>Delete</a></td></tr>";
  202. $output .= "</table>";
  203. return $output;
  204. }
  205. /**
  206. * A template function to render a listing of all Custom tables
  207. *
  208. * @ingroup tripal_custom_tables_api
  209. */
  210. function tripal_custom_tables_list() {
  211. $header = array('', 'Table Name', 'Description');
  212. $rows = array();
  213. $custom_tables = db_query("SELECT * FROM {tripal_custom_tables} ORDER BY table_name");
  214. while ($custom_table = db_fetch_object($custom_tables)) {
  215. $rows[] = array(
  216. l(t('View'), "admin/tripal/custom_tables/view/$custom_table->table_id") ." | ".
  217. l(t('Edit'), "admin/tripal/custom_tables/edit/$custom_table->table_id") ." | ".
  218. $custom_table->table_name,
  219. $custom_table->comment,
  220. l(t('Delete'), "admin/tripal/custom_tables/action/delete/$custom_table->table_id"),
  221. );
  222. }
  223. $rows[] = array(
  224. 'data' => array(
  225. array('data' => l(t('Create a new custom table.'), "admin/tripal/custom_tables/new"),
  226. 'colspan' => 6),
  227. )
  228. );
  229. $page = theme('table', $header, $rows);
  230. return $page;
  231. }
  232. /**
  233. * A Form to Create/Edit a Custom table
  234. *
  235. * @param $form_state
  236. * The current state of the form (Form API)
  237. * @param $table_id
  238. * The unique ID of the Custom table to Edit or NULL if creating a new table
  239. *
  240. * @return
  241. * A form array (Form API)
  242. *
  243. * @ingroup tripal_core
  244. */
  245. function tripal_custom_tables_form(&$form_state = NULL, $table_id = NULL) {
  246. if (!$table_id) {
  247. $action = 'Add';
  248. }
  249. else {
  250. $action = 'Edit';
  251. }
  252. // get this requested table
  253. if (strcmp($action, 'Edit')==0) {
  254. $sql = "SELECT * FROM {tripal_custom_tables} WHERE table_id = %d ";
  255. $custom_table = db_fetch_object(db_query($sql, $table_id));
  256. // set the default values. If there is a value set in the
  257. // form_state then let's use that, otherwise, we'll pull
  258. // the values from the database
  259. $default_schema = $form_state['values']['schema'];
  260. $default_skip = $form_state['values']['skip_creation'];
  261. if (!$default_table_name) {
  262. $default_table = $custom_table->table_name;
  263. }
  264. if (!$default_schema) {
  265. $default_schema = var_export(unserialize($custom_table->schema),1);
  266. }
  267. }
  268. // Build the form
  269. $form['action'] = array(
  270. '#type' => 'value',
  271. '#value' => $action
  272. );
  273. $form['table_id'] = array(
  274. '#type' => 'value',
  275. '#value' => $table_id
  276. );
  277. $form['instructions']= array(
  278. '#type' => 'markup',
  279. '#value' => t('At times it is necessary to add a custom table to the Chado schema.
  280. These are not offically sanctioned tables but may be necessary for local data requirements.
  281. Avoid creating custom tables when possible as other GMOD tools may not recognize these tables
  282. nor the data in them. Linker tables are often a good candidate for
  283. a custom table. For example a table to link stocks and libraries (e.g. library_stock). If the
  284. table already exists it will be dropped and re-added using the definition supplied below. All
  285. data in the table will be lost. However, If you
  286. are certain the schema definition you provide is correct for an existing table, select the checkbox
  287. below to skip creation of the table.
  288. '),
  289. );
  290. $form['skip_creation']= array(
  291. '#type' => 'checkbox',
  292. '#title' => t('Skip Table Creation'),
  293. '#description' => t('If your table already exists, check this box to prevent it from being dropped and re-created.'),
  294. '#default_value' => $default_skip,
  295. );
  296. $form['schema']= array(
  297. '#type' => 'textarea',
  298. '#title' => t('Schema Array'),
  299. '#description' => t('Please enter the Drupal Schema API compatible array that defines the table.'),
  300. '#required' => FALSE,
  301. '#default_value' => $default_schema,
  302. '#rows' => 25,
  303. );
  304. if ($action == 'Edit') {
  305. $value = 'Save';
  306. }
  307. if ($action == 'Add') {
  308. $value = 'Add';
  309. }
  310. $form['submit'] = array(
  311. '#type' => 'submit',
  312. '#value' => t($value),
  313. '#executes_submit_callback' => TRUE,
  314. );
  315. $form['#redirect'] = 'admin/tripal/custom_tables';
  316. $form['example']= array(
  317. '#type' => 'markup',
  318. '#value' => "<br>Example library_stock table: <pre>
  319. array (
  320. 'table' => 'library_stock',
  321. 'fields' => array (
  322. 'library_stock_id' => array(
  323. 'type' => serial,
  324. 'not null' => TRUE,
  325. ),
  326. 'library_id' => array(
  327. 'type' => 'int',
  328. 'not null' => TRUE,
  329. ),
  330. 'stock_id' => array(
  331. 'type' => 'int',
  332. 'not null' => TRUE,
  333. ),
  334. ),
  335. 'primary key' => array(
  336. 'library_stock_id'
  337. ),
  338. 'unique keys' => array(
  339. 'library_stock_c1' => array(
  340. 'library_id',
  341. 'stock_id'
  342. ),
  343. ),
  344. 'foreign keys' => array(
  345. 'library' => array(
  346. 'table' => 'library',
  347. 'columns' => array(
  348. 'library_id' => 'library_id',
  349. ),
  350. ),
  351. 'stock' => array(
  352. 'table' => 'stock',
  353. 'columns' => array(
  354. 'stock_id' => 'stock_id',
  355. ),
  356. ),
  357. ),
  358. )
  359. </pre>",
  360. );
  361. return $form;
  362. }
  363. /**
  364. * Validate the Create/Edit custom table form
  365. * Implements hook_form_validate().
  366. *
  367. * @ingroup tripal_core
  368. */
  369. function tripal_custom_tables_form_validate($form, &$form_state) {
  370. $action = $form_state['values']['action'];
  371. $table_id = $form_state['values']['table_id'];
  372. $schema = $form_state['values']['schema'];
  373. if (!$schema) {
  374. form_set_error($form_state['values']['schema'],
  375. t('Schema array field is required.'));
  376. }
  377. // make sure the array is valid
  378. $schema_array = array();
  379. if ($schema) {
  380. $success = eval("\$schema_array = $schema;");
  381. if ($success === FALSE) {
  382. $error = error_get_last();
  383. form_set_error($form_state['values']['schema'],
  384. t("The schema array is improperly formatted. Parse Error : " . $error["message"]));
  385. }
  386. if (is_array($schema_array) and !array_key_exists('table', $schema_array)) {
  387. form_set_error($form_state['values']['schema'],
  388. t("The schema array must have key named 'table'"));
  389. }
  390. // TODO: add in more validation checks of the array to help the user
  391. }
  392. }
  393. /**
  394. * Submit the Create/Edit Custom table form
  395. * Implements hook_form_submit().
  396. *
  397. * @ingroup tripal_core
  398. */
  399. function tripal_custom_tables_form_submit($form, &$form_state) {
  400. $ret = array();
  401. $action = $form_state['values']['action'];
  402. $table_id = $form_state['values']['table_id'];
  403. $schema = $form_state['values']['schema'];
  404. $skip_creation = $form_state['values']['skip_creation'];
  405. // conver the schema into a PHP array
  406. $schema_arr = array();
  407. eval("\$schema_arr = $schema;");
  408. if (strcmp($action, 'Edit') == 0) {
  409. tripal_core_edit_custom_table($table_id, $schema_arr['table'], $schema_arr, $skip_creation);
  410. }
  411. elseif (strcmp($action, 'Add') == 0) {
  412. tripal_core_create_custom_table($ret, $schema_arr['table'], $schema_arr, $skip_creation);
  413. }
  414. else {
  415. drupal_set_message(t("No action performed."));
  416. }
  417. return '';
  418. }
  419. /**
  420. * Does the specified action for the specified custom table
  421. *
  422. * @param $op
  423. * The action to be taken. Currenly only delete is available
  424. * @param $table_id
  425. * The unique ID of the custom table for the action to be performed on
  426. * @param $redirect
  427. * TRUE/FALSE depending on whether you want to redirect the user to admin/tripal/custom_tables
  428. *
  429. * @ingroup tripal_core
  430. */
  431. function tripal_custom_tables_action($op, $table_id, $redirect = FALSE) {
  432. global $user;
  433. $args = array("$table_id");
  434. if (!$table_id) {
  435. return '';
  436. }
  437. // get this table details
  438. $sql = "SELECT * FROM {tripal_custom_tables} WHERE table_id = %d";
  439. $custom_table = db_fetch_object(db_query($sql, $table_id));
  440. if ($op == 'delete') {
  441. // remove the entry from the tripal_custom tables table
  442. $sql = "DELETE FROM {tripal_custom_tables} ".
  443. "WHERE table_id = $table_id";
  444. db_query($sql);
  445. // drop the table from chado if it exists
  446. if (db_table_exists($custom_table->table_name)) {
  447. $success = chado_query("DROP TABLE %s", $custom_table->table_name);
  448. if($success){
  449. drupal_set_message(t("Custom Table '%name' dropped", array('%name' => $custom_table->table_name)));
  450. }
  451. }
  452. }
  453. // Redirect the user
  454. if ($redirect) {
  455. drupal_goto("admin/tripal/custom_tables");
  456. }
  457. }