tripal_core.chado_variables.api.inc 30 KB

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