tripal_core.chado_variables.api.inc 30 KB

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