tripal_core.chado_variables.api.inc 29 KB

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