tripal_core.chado_variables.api.inc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. <?php
  2. /**
  3. * @file
  4. * @defgroup tripal_chado_variables_api Chado Variables API
  5. * @ingroup tripal_chado_api
  6. * @{
  7. * This API generates objects containing the full details of a record(s) in chado.
  8. * These should be used in all theme templates.
  9. *
  10. * This differs from the objects returned by tripal_core_chado_select in so far as all foreign key
  11. * relationships have been followed meaning you have more complete details. Thus this function
  12. * should be used whenever you need a full variable and tripal_core_chado_select should be used if
  13. * you only case about a few columns.
  14. *
  15. * The initial variable is generated by the
  16. * tripal_core_generate_chado_var([table], [filter criteria], [optional options])
  17. * function. An example of how to use this function is:
  18. * @code
  19. $values = array(
  20. 'name' => 'Medtr4g030710'
  21. );
  22. $features = tripal_core_generate_chado_var('feature', $values);
  23. * @endcode
  24. * This will return an object if there is only one feature with the name Medtr4g030710 or it will
  25. * return an array of feature objects if more than one feature has that name.
  26. *
  27. * Some tables and fields are excluded by default. To have those tables & fields added to
  28. * your variable you can use the
  29. * tripal_core_expand_chado_vars([chado variable], [type], [what to expand], [optional options])
  30. * function. An example of how to use this function is:
  31. * @code
  32. // Get a chado object to be expanded
  33. $values = array(
  34. 'name' => 'Medtr4g030710'
  35. );
  36. $features = tripal_core_generate_chado_var('feature', $values);
  37. // Expand the organism node
  38. $feature = tripal_core_expand_chado_vars($feature, 'node', 'organism');
  39. // Expand the feature.residues field
  40. $feature = tripal_core_expand_chado_vars($feature, 'field', 'feature.residues');
  41. // Expand the feature properties (featureprop table)
  42. $feature = tripal_core_expand_chado_vars($feature, 'table', 'featureprop');
  43. * @endcode
  44. */
  45. /**
  46. * Implements hook_exclude_type_by_default()
  47. *
  48. * This hooks allows fields of a specified type that match a specified criteria to be excluded by
  49. * default from any table when tripal_core_generate_chado_var() is called. Keep in mind that if
  50. * fields are excluded by default they can always be expanded at a later date using
  51. * tripal_core_expand_chado_vars().
  52. *
  53. * Criteria are php strings that evaluate to either TRUE or FALSE. These strings are evaluated using
  54. * drupal_eval() which suppresses syntax errors and throws watchdog entries of type php. There are
  55. * also watchdog entries of type tripal_core stating the exact criteria evaluated. Criteria can
  56. * contain the following tokens:
  57. * - &gt;field_name&lt;
  58. * Replaced by the name of the field to be excluded
  59. * - &gt;field_value&lt;
  60. * Replaced by the value of the field in the current record
  61. * Also keep in mind that if your criteria doesn't contain the &gt;field_value&lt; token then it will be
  62. * evaluated before the query is executed and if the field is excluded it won't be included in the
  63. * query.
  64. *
  65. * @return
  66. * An array of type => criteria where the type is excluded if the criteria evaluates to TRUE
  67. *
  68. * @ingroup tripal_chado_variables_api
  69. */
  70. function tripal_core_exclude_type_by_default() {
  71. return array('text' => 'strlen("&gt;field_value&lt; ") > 100');
  72. }
  73. /**
  74. * Implements hook_exclude_field_from_<tablename>_by_default()
  75. *
  76. * This hooks allows fields from a specified table that match a specified criteria to be excluded by
  77. * default from any table when tripal_core_generate_chado_var() is called. Keep in mind that if
  78. * fields are excluded by default they can always be expanded at a later date using
  79. * tripal_core_expand_chado_vars().
  80. *
  81. * Criteria are php strings that evaluate to either TRUE or FALSE. These strings are evaluated using
  82. * drupal_eval() which suppresses syntax errors and throws watchdog entries of type php. There are
  83. * also watchdog entries of type tripal_core stating the exact criteria evaluated. Criteria can
  84. * contain the following tokens:
  85. * - &gt;field_name&lt;
  86. * Replaced by the name of the field to be excluded
  87. * - &gt;field_value&lt;
  88. * Replaced by the value of the field in the current record
  89. * Also keep in mind that if your criteria doesn't contain the &gt;field_value&lt; token then it will be
  90. * evaluated before the query is executed and if the field is excluded it won't be included in the
  91. * query.
  92. *
  93. * @return
  94. * An array of type => criteria where the type is excluded if the criteria evaluates to TRUE
  95. *
  96. * @ingroup tripal_chado_variables_api
  97. */
  98. function tripal_core_exclude_field_from_feature_by_default() {
  99. return array();
  100. }
  101. /**
  102. * Generates an object containing the full details of a record(s) in chado.
  103. *
  104. * This differs from the objects returned by tripal_core_chado_select in so far as all foreign key
  105. * relationships have been followed meaning you have more complete details. Thus this function
  106. * should be used whenever you need a full variable and tripal_core_chado_select should be used if
  107. * you only case about a few columns.
  108. *
  109. * @param $table
  110. * The name of the base table to generate a variable for
  111. * @param $values
  112. * A select values array that selects the records you want from the base table
  113. * (this has the same form as tripal_core_chado_select)
  114. * @param $base_options
  115. * An array containing options for the base table. For example, an
  116. * option of 'order_by' may be used to sort results in the base table
  117. * if more than one are returned. The options must be compatible with
  118. * the options accepted by the tripal_core_chado_select() function.
  119. * Additionally, These options are available for this function:
  120. * -return_array:
  121. * can be provided to force the function to always return an array. Default
  122. * behavior is to return a single record if only one record exists or to return
  123. * an array if multiple records exist.
  124. * - include_fk:
  125. * an array of FK relationships to follow. By default, the
  126. * tripal_core_chado_select function will follow all FK relationships but this
  127. * may generate more queries then is desired slowing down this function call when
  128. * there are lots of FK relationships to follow. Provide an array specifying the
  129. * fields to include. For example, if expanding a property table (e.g. featureprop)
  130. * and you want the CV and accession but do not want the DB the following
  131. * array would work:
  132. *
  133. * $table_options = array(
  134. * 'include_fk' => array(
  135. * 'type_id' => array(
  136. * 'cv_id' => 1,
  137. * 'dbxref_id' => 1,
  138. * )
  139. * )
  140. * );
  141. *
  142. * The above array will expand the 'type_id' of the property table but only
  143. * further expand the cv_id and the dbxref_id and will go no further.
  144. * - pager:
  145. * Use this option if it is desired to return only a subset of results
  146. * so that they may be shown within a Drupal-style pager. This should be
  147. * an array with two keys: 'limit' and 'element'. The value of 'limit'
  148. * should specify the number of records to return and 'element' is a
  149. * unique integer to differentiate between pagers when more than one
  150. * appear on a page. The 'element' should start with zero and increment by
  151. * one for each pager. This only works when type is a 'table'.
  152. * @return
  153. * Either an object (if only one record was selected from the base table)
  154. * or an array of objects (if more than one record was selected from the base table).
  155. * If the option 'return_array' is provided the function always returns an array.
  156. *
  157. * Example Usage:
  158. * @code
  159. $values = array(
  160. 'name' => 'Medtr4g030710'
  161. );
  162. $features = tripal_core_generate_chado_var('feature', $values);
  163. * @endcode
  164. * This will return an object if there is only one feature with the name Medtr4g030710 or it will
  165. * return an array of feature objects if more than one feature has that name.
  166. *
  167. * Note to Module Designers: Fields can be excluded by default from these objects by implementing
  168. * one of the following hooks:
  169. * - hook_exclude_field_from_tablename_by_default (where tablename is the name of the table):
  170. * This hook allows you to add fields to be excluded on a per table basis. Simply implement
  171. * this hook to return an array of fields to be excluded. For example:
  172. * @code
  173. mymodule_exclude_field_from_feature_by_default() {
  174. return array('residues' => TRUE);
  175. }
  176. * @endcode
  177. * will ensure that feature.residues is ecluded from a feature object by default.
  178. * - hook_exclude_type_by_default:
  179. * This hook allows you to exclude fields from all tables that are of a given postgresql field
  180. * type. Simply implement this hook to return an array of postgresql types mapped to criteria.
  181. * Then all fields of that type where the criteria supplied returns TRUE will be excluded from
  182. * any table. Tokens available in criteria are &gt;field_value&lt; and &gt;field_name&lt; . For example:
  183. * @code
  184. mymodule_exclude_type_by_default() {
  185. return array('text' => 'length(&gt;field_value&lt; ) > 50');
  186. }
  187. * @endcode
  188. * will exclude all text fields with a length > 50. Thus if $feature.residues is longer than 50 * it will be excluded, otherwise it will be added.
  189. *
  190. * @ingroup tripal_chado_variables_api
  191. */
  192. function tripal_core_generate_chado_var($table, $values, $base_options = array()) {
  193. $all = new stdClass();
  194. $return_array = 0;
  195. if (array_key_exists('return_array', $base_options)) {
  196. $return_array = 1;
  197. }
  198. $include_fk = 0;
  199. if (array_key_exists('include_fk', $base_options)) {
  200. $include_fk = $base_options['include_fk'];
  201. }
  202. $pager = array();
  203. if (array_key_exists('pager', $base_options)) {
  204. $pager = $base_options['pager'];
  205. }
  206. // get description for the current table----------------------------------------------------------
  207. $table_desc = tripal_core_get_chado_table_schema($table);
  208. if (!$table_desc or count($table_desc) == 0) {
  209. watchdog('tripal_core', "tripal_core_generate_chado_var: The table '%table' has not been defined. " .
  210. "and cannot be expanded. If this is a custom table, please add it using the Tripal " .
  211. "custom table interface.", array('%table' => $table), WATCHDOG_ERROR);
  212. if ($return_array) {
  213. return array();
  214. }
  215. return FALSE;
  216. }
  217. $table_primary_key = $table_desc['primary key'][0];
  218. $table_columns = array_keys($table_desc['fields']);
  219. // Expandable fields without value needed for criteria--------------------------------------------
  220. $all->expandable_fields = array();
  221. if (array_key_exists('referring_tables', $table_desc) and $table_desc['referring_tables']) {
  222. $all->expandable_tables = $table_desc['referring_tables'];
  223. }
  224. else {
  225. $all->expandable_tables = array();
  226. }
  227. $all->expandable_nodes = array();
  228. /*
  229. // Get fields to be removed by name.................................
  230. $fields_to_remove = module_invoke_all('exclude_field_from_' . $table . '_by_default');
  231. foreach ($fields_to_remove as $field_name => $criteria) {
  232. //replace &gt;field_name&lt; with the current field name &
  233. $criteria = preg_replace('/&gt;field_name&lt; /', addslashes($field_name), $criteria);
  234. // if field_value needed we can't deal with this field yet
  235. if (preg_match('/&gt;field_value&lt; /', $criteria)) {
  236. break;
  237. }
  238. //if criteria then remove from query
  239. // @coder-ignore: only module designers can populate $criteria -not security risk
  240. $success = php_eval('<?php return ' . $criteria . '; ?>');
  241. if ($success) {
  242. unset($table_columns[array_search($field_name, $table_columns)]);
  243. unset($fields_to_remove[$field_name]);
  244. $all->expandable_fields[] = $table . '.' . $field_name;
  245. }
  246. }
  247. //Get fields to be removed by type................................
  248. $types_to_remove = module_invoke_all('exclude_type_by_default');
  249. $field_types = array();
  250. foreach ($table_desc['fields'] as $field_name => $field_array) {
  251. $field_types[$field_array['type']][] = $field_name;
  252. }
  253. foreach ($types_to_remove as $field_type => $criteria) {
  254. // if there are fields of that type to remove
  255. if (is_array($field_types[$field_type])) {
  256. //replace &gt;field_name&lt; with the current field name &
  257. $criteria = preg_replace('/&gt;field_name&lt; /', addslashes($field_name), $criteria);
  258. foreach ($field_types[$field_type] as $field_name) {
  259. // if field_value needed we can't deal with this field yet
  260. if (preg_match('/&gt;field_value&lt; /', $criteria)) {
  261. $fields_to_remove[$field_name] = $criteria;
  262. continue;
  263. }
  264. // if field_value needed we can't deal with this field yet
  265. if (preg_match('/&gt;field_value&lt; /', $criteria)) {
  266. break;
  267. }
  268. //if criteria then remove from query
  269. // @coder-ignore: only module designers can populate $criteria -not security risk
  270. $success = php_eval('<?php return ' . $criteria . '; ?>');
  271. if ($success) {
  272. unset($table_columns[array_search($field_name, $table_columns)]);
  273. $all->expandable_fields[] = $table . '.' . $field_name;
  274. }
  275. } //end of foreach field of that type
  276. }
  277. } //end of foreach type to be removed
  278. */
  279. // get the values for the record in the current table---------------------------------------------
  280. $results = tripal_core_chado_select($table, $table_columns, $values, $base_options);
  281. if ($results) {
  282. foreach ($results as $key => $object) {
  283. // Add empty expandable_x arrays
  284. $object->expandable_fields = $all->expandable_fields;
  285. $object->expandable_tables = $all->expandable_tables;
  286. $object->expandable_nodes = $all->expandable_nodes;
  287. // add curent table
  288. $object->tablename = $table;
  289. // check if the current table maps to a node type-----------------------------------------------
  290. // if this table is connected to a node there will be a chado_tablename table in drupal
  291. if (db_table_exists('chado_' . $table)) {
  292. // that has a foreign key to this one ($table_desc['primary key'][0]
  293. // and to the node table (nid)
  294. $sql = "
  295. SELECT $table_primary_key, nid
  296. FROM {chado_$table}
  297. WHERE $table_primary_key = :$table_primary_key
  298. ";
  299. $mapping = db_query($sql, array(":$table_primary_key" => $object->{$table_primary_key}))->fetchObject();
  300. if ($mapping and $mapping->$table_primary_key) {
  301. $object->nid = $mapping->nid;
  302. $object->expandable_nodes[] = $table;
  303. }
  304. }
  305. // remove any fields where criteria needs to be evalulated---------------------------------------
  306. /* foreach ($fields_to_remove as $field_name => $criteria) {
  307. if (!isset($object->{$field_name})) {
  308. break;
  309. }
  310. $criteria = preg_replace('/&gt;field_value&lt; /', addslashes($object->{$field_name}), $criteria);
  311. $success = php_eval('<?php return ' . $criteria . '; ?>');
  312. if ($success) {
  313. unset($object->{$field_name});
  314. $object->expandable_fields[] = $table . '.' . $field_name;
  315. }
  316. }
  317. */
  318. // recursively follow foreign key relationships nesting objects as we go------------------------
  319. if ($table_desc['foreign keys']) {
  320. foreach ($table_desc['foreign keys'] as $foreign_key_array) {
  321. $foreign_table = $foreign_key_array['table'];
  322. foreach ($foreign_key_array['columns'] as $foreign_key => $primary_key) {
  323. // Note: Foreign key is the field in the current table whereas primary_key is the field in
  324. // the table referenced by the foreign key
  325. //Dont do anything if the foreign key is empty
  326. if (empty($object->{$foreign_key})) {
  327. continue;
  328. }
  329. if ($include_fk) {
  330. // don't recurse if the callee has supplied an $fk_include list and this
  331. // FK table is not in the list.
  332. if (is_array($include_fk) and !array_key_exists($foreign_key, $include_fk)) {
  333. continue;
  334. }
  335. // if we have the option but it is not an array then we don't recurse any furutehr
  336. if (!is_array($include_fk)) {
  337. continue;
  338. }
  339. }
  340. // get the record from the foreign table
  341. $foreign_values = array($primary_key => $object->{$foreign_key});
  342. $options = array();
  343. if (is_array($include_fk)) {
  344. $options['include_fk'] = $include_fk[$foreign_key];
  345. }
  346. $foreign_object = tripal_core_generate_chado_var($foreign_table, $foreign_values, $options);
  347. // add the foreign record to the current object in a nested manner
  348. $object->{$foreign_key} = $foreign_object;
  349. // Flatten expandable_x arrays so only in the bottom object
  350. if (property_exists($object->{$foreign_key}, 'expandable_fields') and
  351. is_array($object->{$foreign_key}->expandable_fields)) {
  352. $object->expandable_fields = array_merge(
  353. $object->expandable_fields,
  354. $object->{$foreign_key}->expandable_fields
  355. );
  356. unset($object->{$foreign_key}->expandable_fields);
  357. }
  358. if (property_exists($object->{$foreign_key}, 'expandable_tables') and
  359. is_array($object->{$foreign_key}->expandable_tables)) {
  360. $object->expandable_tables = array_merge(
  361. $object->expandable_tables,
  362. $object->{$foreign_key}->expandable_tables
  363. );
  364. unset($object->{$foreign_key}->expandable_tables);
  365. }
  366. if (property_exists($object->{$foreign_key}, 'expandable_nodes') and
  367. is_array($object->{$foreign_key}->expandable_nodes)) {
  368. $object->expandable_nodes = array_merge(
  369. $object->expandable_nodes,
  370. $object->{$foreign_key}->expandable_nodes
  371. );
  372. unset($object->{$foreign_key}->expandable_nodes);
  373. }
  374. }
  375. }
  376. $results[$key] = $object;
  377. }
  378. }
  379. }
  380. // convert the results into an array
  381. $results_arr = array();
  382. foreach ($results as $record) {
  383. $results_arr[] = $record;
  384. }
  385. // check only one result returned
  386. if (!$return_array) {
  387. if (sizeof($results_arr) == 1) {
  388. // add results to object
  389. return $results_arr[0];
  390. }
  391. elseif (!empty($results_arr)) {
  392. return $results_arr;
  393. }
  394. else {
  395. // no results returned
  396. }
  397. }
  398. // the caller has requested results are always returned as
  399. // an array
  400. else {
  401. if (!$results_arr) {
  402. return array();
  403. }
  404. else {
  405. return $results_arr;
  406. }
  407. }
  408. }
  409. /**
  410. * Retrieves fields/tables/nodes that were excluded by default from a variable and adds them
  411. *
  412. * This function exists to allow tripal_core_generate_chado_var() to excldue some
  413. * fields/tables/nodes from the default form of a variable without making it extremely difficult for
  414. * the tripal admin to get at these variables if he/she wants them.
  415. *
  416. * @param $object
  417. * This must be an object generated using tripal_core_generate_chado_var()
  418. * @param $type
  419. * Must be one of 'field', 'table', 'node'. Indicates what is being expanded.
  420. * @param $to_expand
  421. * The name of the field/table/node to be expanded
  422. * @param $table_options
  423. * - order_by:
  424. * An array containing options for the base table. For example, an
  425. * option of 'order_by' may be used to sort results in the base table
  426. * if more than one are returned. The options must be compatible with
  427. * the options accepted by the tripal_core_chado_select() function.
  428. * - return_array:
  429. * Additionally, The option 'return_array' can be provided to force
  430. * the function to expand tables as an array. Default behavior is to expand
  431. * a table as single record if only one record exists or to expand as an array if
  432. * multiple records exist.
  433. * - include_fk:
  434. * an array of FK relationships to follow. By default, the
  435. * tripal_core_expand_chado_vars function will follow all FK relationships but this
  436. * may generate more queries then is desired slowing down this function call when
  437. * there are lots of FK relationships to follow. Provide an array specifying the
  438. * fields to include. For example, if expanding a property table (e.g. featureprop)
  439. * and you want the CV and accession but do not want the DB the following
  440. * array would work:
  441. * $table_options = array(
  442. * 'include_fk' => array(
  443. * 'type_id' => array(
  444. * 'cv_id' => 1,
  445. * 'dbxref_id' => 1,
  446. * )
  447. * )
  448. * );
  449. *
  450. * The above array will expand the 'type_id' of the property table but only
  451. * further expand the cv_id and the dbxref_id and will go no further.
  452. * - pager:
  453. * Use this option if it is desired to return only a subset of results
  454. * so that they may be shown within a Drupal-style pager. This should be
  455. * an array with two keys: 'limit' and 'element'. The value of 'limit'
  456. * should specify the number of records to return and 'element' is a
  457. * unique integer to differentiate between pagers when more than one
  458. * appear on a page. The 'element' should start with zero and increment by
  459. * one for each pager. This only works when type is a 'table'.
  460. * @return
  461. * A chado object supplemented with the field/table/node requested to be expanded.
  462. * If the type is a table and it has already been expanded no changes is made to the
  463. * returned object
  464. *
  465. * Example Usage:
  466. * @code
  467. // Get a chado object to be expanded
  468. $values = array(
  469. 'name' => 'Medtr4g030710'
  470. );
  471. $features = tripal_core_generate_chado_var('feature', $values);
  472. // Expand the organism node
  473. $feature = tripal_core_expand_chado_vars($feature, 'node', 'organism');
  474. // Expand the feature.residues field
  475. $feature = tripal_core_expand_chado_vars($feature, 'field', 'feature.residues');
  476. // Expand the feature properties (featureprop table)
  477. $feature = tripal_core_expand_chado_vars($feature, 'table', 'featureprop');
  478. * @endcode
  479. *
  480. * @ingroup tripal_chado_variables_api
  481. */
  482. function tripal_core_expand_chado_vars($object, $type, $to_expand, $table_options = array()) {
  483. // make sure we have a value
  484. if (!$object) {
  485. watchdog('tripal_core', 'Cannot pass non array as argument, $object, to tripal_core_expand_chado_vars function.', array(), WATCHDOG_ERROR);
  486. return $object;
  487. }
  488. // check to see if we are expanding an array of objects
  489. if (is_array($object)) {
  490. foreach ($object as $index => $o) {
  491. $object[$index] = tripal_core_expand_chado_vars($o, $type, $to_expand);
  492. }
  493. return $object;
  494. }
  495. // get the base table name
  496. $base_table = $object->tablename;
  497. switch ($type) {
  498. case "field": //--------------------------------------------------------------------------------
  499. if (preg_match('/(\w+)\.(\w+)/', $to_expand, $matches)) {
  500. $tablename = $matches[1];
  501. $fieldname = $matches[2];
  502. $table_desc = tripal_core_get_chado_table_schema($tablename);
  503. $values = array();
  504. foreach ($table_desc['primary key'] as $key) {
  505. if(property_exists($object, $key)) {
  506. $values[$key] = $object->{$key};
  507. }
  508. }
  509. if ($base_table == $tablename) {
  510. //get the field
  511. $results = tripal_core_chado_select($tablename, array($fieldname), $values);
  512. $object->{$fieldname} = $results[0]->{$fieldname};
  513. $object->expanded = $to_expand;
  514. }
  515. else {
  516. //We need to recurse -the field is in a nested object
  517. foreach ((array) $object as $field_name => $field_value) {
  518. if (is_object($field_value)) {
  519. $object->{$field_name} = tripal_core_expand_chado_vars(
  520. $field_value,
  521. 'field',
  522. $to_expand
  523. );
  524. }
  525. } //end of for each field in the current object
  526. }
  527. }
  528. else {
  529. watchdog('tripal_core', 'tripal_core_expand_chado_vars: Field (%field) not in the right format. " .
  530. "It should be <tablename>.<fieldname>', WATCHDOG_ERROR);
  531. }
  532. break;
  533. case "table": //--------------------------------------------------------------------------------
  534. $foreign_table = $to_expand;
  535. // don't expand the table it already is expanded
  536. if (array_key_exists($foreign_table, $object)) {
  537. return $object;
  538. }
  539. $foreign_table_desc = tripal_core_get_chado_table_schema($foreign_table);
  540. // If it's connected to the base table via a FK constraint
  541. if (array_key_exists($base_table, $foreign_table_desc['foreign keys'])) {
  542. foreach ($foreign_table_desc['foreign keys'][$base_table]['columns'] as $left => $right) {
  543. // if the FK value in the base table is not there then we can't expand it, so just skip it.
  544. if (!$object->{$right}) {
  545. continue;
  546. }
  547. // generate a new object for this table using the FK values in the base table.
  548. $new_options = $table_options;
  549. $foreign_object = tripal_core_generate_chado_var($foreign_table, array($left => $object->{$right}), $new_options);
  550. // if the generation of the object was successful, update the base object to include it.
  551. if ($foreign_object) {
  552. // in the case where the foreign key relationship exists more
  553. // than once with the same table we want to alter the array structure to
  554. // include the field name.
  555. if (count($foreign_table_desc['foreign keys'][$base_table]['columns']) > 1) {
  556. if (!property_exists($object, $foreign_table)) {
  557. $object->{$foreign_table} = new stdClass();
  558. }
  559. $object->{$foreign_table}->{$left} = $foreign_object;
  560. $object->expanded = $to_expand;
  561. }
  562. else {
  563. if (!property_exists($object, $foreign_table)) {
  564. $object->{$foreign_table} = new stdClass();
  565. }
  566. $object->{$foreign_table} = $foreign_object;
  567. $object->expanded = $to_expand;
  568. }
  569. }
  570. // if the object returned is NULL then handle that
  571. else {
  572. // in the case where the foreign key relationship exists more
  573. // than once with the same table we want to alter the array structure to
  574. // include the field name.
  575. if (count($foreign_table_desc['foreign keys'][$base_table]['columns']) > 1) {
  576. if (!property_exists($object, $foreign_table)) {
  577. $object->{$foreign_table} = new stdClass();
  578. }
  579. $object->{$foreign_table}->{$left} = NULL;
  580. }
  581. else {
  582. $object->{$foreign_table} = NULL;
  583. }
  584. }
  585. }
  586. }
  587. // if the foreign table is not connected to the base table through a FK constraint
  588. else {
  589. // We need to recurse -the table has a relationship to one of the nested objects
  590. $did_expansion = 0;
  591. foreach ((array) $object as $field_name => $field_value) {
  592. // if we have a nested object ->expand the table in it
  593. // check to see if the $field_name is a valid chado table, we don't need
  594. // to call tripal_core_expand_chado_vars on fields that aren't tables
  595. $check = tripal_core_get_chado_table_schema($field_name);
  596. if ($check) {
  597. $did_expansion = 1;
  598. $object->{$field_name} = tripal_core_expand_chado_vars($field_value, 'table', $foreign_table);
  599. }
  600. }
  601. // if we did not expand this table we should return a message that the foreign table
  602. // could not be expanded
  603. if (!$did_expansion) {
  604. watchdog('tripal_core', 'tripal_core_expand_chado_vars: Could not expand table, %table. It is ' .
  605. 'not in a foreign key relationship with the base object nor with any other expanded table. ' .
  606. 'Check the table definition to ensure that a proper foreign key relationship is present.',
  607. array('%table' => $foreign_table), WATCHDOG_ERROR);
  608. }
  609. }
  610. break;
  611. case "node": //---------------------------------------------------------------------------------
  612. //if the node to be expanded is for our base table, then just expand it
  613. if ($object->tablename == $to_expand) {
  614. $node = node_load($object->nid);
  615. if ($node) {
  616. $object->expanded = $to_expand;
  617. $node->expandable_fields = $object->expandable_fields;
  618. unset($object->expandable_fields);
  619. $node->expandable_tables = $object->expandable_tables;
  620. unset($object->expandable_tables);
  621. $node->expandable_nodes = $object->expandable_nodes;
  622. unset($object->expandable_nodes);
  623. $node->{$base_table} = $object;
  624. $object = $node;
  625. }
  626. else {
  627. watchdog('tripal_core', 'tripal_core_expand_chado_vars: No node matches the nid (%nid) supplied.',
  628. array('%nid' => $object->nid), WATCHDOG_ERROR);
  629. } //end of if node
  630. }
  631. else {
  632. //We need to recurse -the node to expand is one of the nested objects
  633. foreach ((array) $object as $field_name => $field_value) {
  634. if (is_object($field_value)) {
  635. $object->{$field_name} = tripal_core_expand_chado_vars(
  636. $field_value,
  637. 'node',
  638. $to_expand
  639. );
  640. }
  641. } //end of for each field in the current object
  642. }
  643. break;
  644. default:
  645. watchdog('tripal_core', 'tripal_core_expand_chado_vars: Unrecognized type (%type). Should be one of "field", "table", "node".',
  646. array('%type' => $type), WATCHDOG_ERROR);
  647. return FALSE;
  648. }
  649. // move extended array downwards
  650. if (!property_exists($object, 'expanded')) {
  651. //if there's no extended field then go hunting for it
  652. foreach ( (array)$object as $field_name => $field_value) {
  653. if (is_object($field_value)) {
  654. if (isset($field_value->expanded)) {
  655. $object->expanded = $field_value->expanded;
  656. unset($field_value->expanded);
  657. }
  658. }
  659. }
  660. }
  661. //try again becasue now we might have moved it down
  662. if (property_exists($object, 'expanded')) {
  663. $expandable_name = 'expandable_' . $type . 's';
  664. if (property_exists($object, $expandable_name) and $object->{$expandable_name}) {
  665. $key_to_remove = array_search($object->expanded, $object->{$expandable_name});
  666. unset($object->{$expandable_name}[$key_to_remove]);
  667. unset($object->expanded);
  668. }
  669. else {
  670. // if there is an expandable array then we've reached the base object
  671. // if we get here and don't have anything expanded then something went wrong
  672. // watchdog(
  673. // 'tripal_core',
  674. // 'tripal_core_expand_chado_vars: Unable to expand the %type %to_expand',
  675. // array('%type'=>$type, '%to_expand'=>$to_expand),
  676. // WATCHDOG_ERROR
  677. // );
  678. } //end of it we've reached the base object
  679. }
  680. return $object;
  681. }