tripal_core.chado_variables.api.inc 31 KB

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