tripal_core.chado_variables.api.inc 30 KB

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