function chado_expand_var
2.x tripal_core.chado_variables.api.inc | chado_expand_var($object, $type, $to_expand, $table_options = array()) |
3.x tripal_chado.variables.api.inc | chado_expand_var($object, $type, $to_expand, $table_options = array()) |
Retrieves fields, or tables that were excluded by default from a variable.
The chado_generate_var() function automatically excludes some fields and tables from the default form of a variable. Fields that are extremely long, such as text fields are automatically excluded to prevent long page loads. Linking tables that have a many-to-one relationship with the record are also excluded. This function allows for custom expansion of the record created by chado_generate_var() by specifyin the field and tables that should be added.
Example Usage:
// Get a chado object to be expanded
$values = array(
'name' => 'Medtr4g030710'
);
$features = chado_generate_var('feature', $values);
// Expand the feature.residues field
$feature = chado_expand_var($feature, 'field', 'feature.residues');
// Expand the feature properties (featureprop table)
$feature = chado_expand_var($feature, 'table', 'featureprop');
If a field is requested, it's value is added where it normally is expected in the record. If a table is requested then a new key/value element is added to the record. The key is the table's name and the value is an array of records (of the same type created by chado_generate_var()). For example, expanding a 'feature' record to include a 'pub' record via the 'feature_pub' table. The following provides a simple example for how the 'feature_pub' table is added.
<?php
array(
'feature_id' => 1
'name' => 'blah',
'uniquename' => 'blah',
....
'feature_pub => array(
[pub object],
[pub object],
[pub object],
[pub object],
)
)
?>
where [pub object] is a record of a publication as created by chado_generate_var().
If the requested table has multiple foreign keys, such as the 'featureloc' or 'feature_genotype' tables, then an additional level is added to the array where the foreign key column names are added. An example feature record with an expanded featureloc table is shown below:
<?php
array(
'feature_id' => 1
'name' => 'blah',
'uniquename' => 'blah',
....
'featureloc => array(
'srcfeature_id' => array(
[feature object],
...
)
'feature_id' => array(
[feature object],
...
)
)
)
?>
Parameters
$object: This must be an object generated using chado_generate_var()
$type: Indicates what is being expanded. Must be one of 'field', 'foreign_key', 'table', 'node'. While field and node are self-explanitory, it might help to note that 'table' refers to tables that have a foreign key pointing to the current table (ie: featureprop is a table that can be expanded for features) and 'foreign_key' expands a foreign key in the current table that might have been excluded (ie: feature.type_id for features).
$to_expand: The name of the field/foreign_key/table/node to be expanded
$table_options:
- order_by: An array containing options for the base table. For example, an option of 'order_by' may be used to sort results in the base table if more than one are returned. The options must be compatible with the options accepted by the chado_select_record() function.
- return_array: Additionally, The option 'return_array' can be provided to force the function to expand tables as an array. Default behavior is to expand a table as single record if only one record exists or to expand as an array if multiple records exist.
- include_fk: an array of FK relationships to follow. By default, the chado_expand_var function will follow all FK relationships but this may generate more queries then is desired slowing down this function call when there are lots of FK relationships to follow. Provide an array specifying the fields to include. For example, if expanding a property table (e.g. featureprop) and you want the CV and accession but do not want the DB the following array would work: $table_options = array( 'include_fk' => array( 'type_id' => array( 'cv_id' => 1, 'dbxref_id' => 1, ) ) );
The above array will expand the 'type_id' of the property table but only further expand the cv_id and the dbxref_id and will go no further.
- pager: Use this option if it is desired to return only a subset of results so that they may be shown within a Drupal-style pager. This should be an array with two keys: 'limit' and 'element'. The value of 'limit' should specify the number of records to return and 'element' is a unique integer to differentiate between pagers when more than one appear on a page. The 'element' should start with zero and increment by one for each pager. This only works when type is a 'table'.
- filter: This options is only used where type=table and allows you to expand only a subset of results based on the given criteria. Criteria should provided as an array of [field name] => [value] similar to the values array provided to chado_generate_var(). For example, when expanding the featureprop table for a feature, you will already get only properties for that feature, this option allows you to further get only properties of a given type by passing in array('type_id' => array('name' => [name of type]))
Return value
A chado object supplemented with the field/table/node requested to be expanded. If the type is a table and it has already been expanded no changes is made to the returned object
Related topics
- chado_analysis_form in legacy/
tripal_analysis/ includes/ tripal_analysis.chado_node.inc - Implements hook_form(). When editing or creating a new node of type 'chado_analysis' we need a form. This function creates the form that will be used for this.
- chado_analysis_load in legacy/
tripal_analysis/ includes/ tripal_analysis.chado_node.inc - Implements hook_load(). When a node is requested by the user this function is called to allow us to add auxiliary data to the node object.
- chado_contact_load in legacy/
tripal_contact/ includes/ tripal_contact.chado_node.inc - Implements hook_load().
- chado_featuremap_form in legacy/
tripal_featuremap/ includes/ tripal_featuremap.chado_node.inc - When editing or creating a new node of type 'chado_featuremap' we need a form. This function creates the form that will be used for this.
- chado_featuremap_load in legacy/
tripal_featuremap/ includes/ tripal_featuremap.chado_node.inc - Implements hook_load().
- tripal_core_expand_chado_vars in legacy/
tripal_core/ api/ tripal_core.DEPRECATED.inc
File
- tripal_chado/
api/ tripal_chado.variables.api.inc, line 620 - This API generates objects containing the full details of a record(s) in chado.
Code
function chado_expand_var($object, $type, $to_expand, $table_options = array()) {
// Make sure we have a value.
if (!$object) {
tripal_report_error('tripal_chado',
TRIPAL_ERROR,
'Cannot pass non array as argument, $object, to chado_expand_var function.',
array());
return $object;
}
// Check to see if we are expanding an array of objects.
if (is_array($object)) {
foreach ($object as $index => $o) {
$object[$index] = chado_expand_var($o, $type, $to_expand);
}
return $object;
}
// Get the base table name.
$base_table = $object->tablename;
switch ($type) {
case "field": //------------------------------------------------------------
if (preg_match('/(\w+)\.(\w+)/', $to_expand, $matches)) {
$tablename = $matches[1];
$fieldname = $matches[2];
$table_desc = chado_get_schema($tablename);
// BASE CASE: the field is from the current table.
if ($base_table == $tablename) {
// Use the table description to fully describe the current object
// in a $values array to be used to select the field from chado.
$values = array();
foreach ($table_desc['primary key'] as $key) {
if (property_exists($object, $key)) {
$values[$key] = $object->{$key};
}
}
// Retrieve the field from Chado.
$results = chado_select_record($tablename, array($fieldname), $values);
// Check that the field was retrieved correctly.
if (isset($results[0])) {
$object->{$fieldname} = $results[0]->{$fieldname};
$object->expanded = $to_expand;
}
// If it wasn't retrieved correctly, we need to warn the administrator.
}
// RECURSIVE CASE: the field is in a nested object.
else {
// We want to look at each field and if it's an object then we want to
// attempt to expand the field in it via recursion.
foreach ((array) $object as $field_name => $field_value) {
if (is_object($field_value)) {
$object->{$field_name} = chado_expand_var(
$field_value,
'field',
$to_expand
);
}
} // End of for each field in the current object.
}
}
// Otherwise we weren't able to extract the parts of the field to expand
// Thus we will warn the administrator.
else {
tripal_report_error('tripal_chado', TRIPAL_ERROR,
'chado_expand_var: Field (%field) not in the right format. " .
"It should be <tablename>.<fieldname>', array('%field' => $to_expand));
}
break;
case "foreign_key": //-----------------------------------------------------
if (preg_match('/(\w+)\.(\w+) => (\w+)/', $to_expand, $matches)) {
$table_name = $matches[1];
$field_name = $matches[2];
$foreign_table = $matches[3];
$table_desc = chado_get_schema($table_name);
// BASE CASE: The foreign key is from the current table.
if ($base_table == $table_name) {
// Get the value of the foreign key from the object
$field_value = $object->{$field_name};
// Get the name of the field in the foreign table using the table
// description For example, with the
// feature.type_id => cvterm.cvterm_id we need cvterm_id
$foreign_field_name = FALSE;
foreach ($table_desc['foreign keys'][$foreign_table]['columns'] as $left => $right) {
if ($right == $field_name) {
$foreign_field_name = $left;
}
}
// Check that we were able to determine the field name in the foreign
// table.
if ($foreign_field_name) {
// Generate a chado variable of the foreign key
// For example, if the foreign key to expand is feature.type_id
// then we want to generate a chado cvterm variable that matches the
// feature.type_id.
$foreign_var = chado_generate_var(
$foreign_table, // thus in the example above, generate a cvterm var
array($foreign_field_name => $field_value), // where the cvterm.cvterm_id = feature.type_id value
$table_options //pass in the same options given to this function
);
// Check that the foreign object was returned.
if ($foreign_var) {
// It was so now we can add this chado variable to our current
// object in place of the key value.
$object->{$field_name} = $foreign_var;
$object->expanded = $to_expand;
}
// Otherwise we weren't able to expand the foreign key
else {
tripal_report_error('tripal_chado', TRIPAL_ERROR,
'chado_expand_var: unable to retrieve the object desribed by the foreign key
while trying to expand %fk.',
array('%fk' => $to_expand));
}
}
// Else we were unable to determine the field name in the foreign table.
else {
tripal_report_error('tripal_chado', TRIPAL_ERROR,
'chado_expand_var: unable to determine the field name in the table the foreign
key points to while trying to expand %fk.',
array('%fk' => $to_expand));
}
}
// RECURSIVE CASE: Check any nested objects.
else {
foreach ((array) $object as $field_name => $field_value) {
if (is_object($field_value)) {
$object->{$field_name} = chado_expand_var(
$field_value,
'foreign_key',
$to_expand
);
}
} //End of for each field in the current object.
}
}
// Otherwise we weren't able to extract the parts of the foreign key to
// expand thus we will warn the administrator.
else {
tripal_report_error('tripal_chado', TRIPAL_ERROR,
'chado_expand_var: foreign_key (%fk) not in the right format. " .
"It should be <tablename>.<fieldname>', array('%fk' => $to_expand));
}
break;
case "table": //------------------------------------------------------------
$foreign_table = $to_expand;
// BASE CASE: don't expand the table it already is expanded
if (array_key_exists($foreign_table, $object)) {
return $object;
}
$foreign_table_desc = chado_get_schema($foreign_table);
// TODO: if we don't get a foreign_table (which could happen of a custom
// table is not correctly defined or the table name is mispelled then we
// should return gracefully.
// BASE CASE: If it's connected to the base table via a FK constraint
// then we have all the information needed to expand it now.
if (array_key_exists($base_table, $foreign_table_desc['foreign keys'])) {
foreach ($foreign_table_desc['foreign keys'][$base_table]['columns'] as $left => $right) {
// if the FK value in the base table is not there then we can't expand
// it, so just skip it.
if (!$object->{$right}) {
continue;
}
// If the user wants to limit the results they expand, make sure
// those criteria are taken into account.
if (isset($table_options['filter'])) {
if (is_array($table_options['filter'])) {
$filter_criteria = $table_options['filter'];
$filter_criteria[$left] = $object->{$right};
}
else {
// If they supplied criteria but it's not in the correct format
// then warn them but proceed as though criteria was not supplied.
$filter_criteria = array($left => $object->{$right});
tripal_report_error('tripal_chado', TRIPAL_WARNING,
'chado_expand_var: unable to apply supplied filter criteria
since it should be an array. You supplied %criteria',
array('%criteria' => print_r($table_options['filter'], TRUE))
);
}
}
else {
$filter_criteria = array($left => $object->{$right});
}
// Generate a new object for this table using the FK values in the
// base table.
$new_options = $table_options;
$foreign_object = chado_generate_var($foreign_table, $filter_criteria, $new_options);
// If the generation of the object was successful, update the base
// object to include it.
if ($foreign_object) {
// In the case where the foreign key relationship exists more
// than once with the same table we want to alter the array
// structure to include the field name.
if (count($foreign_table_desc['foreign keys'][$base_table]['columns']) > 1) {
if (!property_exists($object, $foreign_table)) {
$object->{$foreign_table} = new stdClass();
}
$object->{$foreign_table}->{$left} = $foreign_object;
$object->expanded = $to_expand;
}
else {
if (!property_exists($object, $foreign_table)) {
$object->{$foreign_table} = new stdClass();
}
$object->{$foreign_table} = $foreign_object;
$object->expanded = $to_expand;
}
}
// If the object returned is NULL then handle that.
else {
// In the case where the foreign key relationship exists more
// than once with the same table we want to alter the array
// structure to include the field name.
if (count($foreign_table_desc['foreign keys'][$base_table]['columns']) > 1) {
if (!property_exists($object, $foreign_table)) {
$object->{$foreign_table} = new stdClass();
}
$object->{$foreign_table}->{$left} = NULL;
}
else {
$object->{$foreign_table} = NULL;
}
}
}
}
// RECURSIVE CASE: if the table is not connected directly to the current
// base table through a foreign key relationship, then maybe it has a
// relationship to one of the nested objects.
else {
// We need to recurse -the table has a relationship to one of the nested
// objects. We assume it's a nested object if the value of the field is
// an object.
$did_expansion = 0;
foreach ((array) $object as $field_name => $field_value) {
// CASE #1: This field is an already expanded foreign key and the
// table to be expanded is in the table referenced by the foreign key.
// First of all it can only be expanded if it's an object
// And if it's a foreign key it should have a tablename property.
if (is_object($field_value) AND property_exists($field_value, 'tablename')) {
$object->{$field_name} = chado_expand_var($field_value, 'table', $foreign_table);
}
// CASE #2: This field is an already expanded object (ie: the field is
// actually the expanded table name) and the table to be expanded is
// related to it.
// Check to see if the $field_name is a valid chado table, we don't
// need to call chado_expand_var on fields that aren't tables.
$check = chado_get_schema($field_name);
if ($check) {
$did_expansion = 1;
$object->{$field_name} = chado_expand_var($field_value, 'table', $foreign_table);
}
}
// If we did not expand this table we should return a message that the
// foreign tabl could not be expanded.
if (!$did_expansion) {
tripal_report_error('tripal_chado', TRIPAL_ERROR, 'chado_expand_var: Could not expand %table. ' .
'The table is either not related to the base object through a foreign key relationships or ' .
'it is already expanded. First check the object to ensure it doesn’t already contain the ' .
'data needed and otherwise check the table definition using chado_get_schema() to ensure ' .
'a proper foreign key relationship is present.',
array('%table' => $foreign_table));
}
}
break;
case "node": //-------------------------------------------------------------
// BASE CASE: if the node to be expanded is for our base table, then just
// expand it.
if ($object->tablename == $to_expand) {
// Load the node based on the current objects nid (node primary key).
$node = NULL;
if (property_exists($object, 'nid')) {
$node = node_load($object->nid);
}
// Try to get the nid based on the tablename.
else {
// Invoke all hook_node_info to avoid hard-coding the chado_$table
// assumption..
foreach (module_invoke_all('node_info') as $node_info) {
if (array_key_exists('chado_node_api', $node_info)) {
if ($node_info['chado_node_api']['base_table'] == $object->tablename) {
$key_name = $node_info['chado_node_api']['base_table'] . '_id';
$nid = chado_get_nid_from_id(
$node_info['chado_node_api']['base_table'],
$object->{$key_name},
$node_info['base']);
if ($nid > 0) {
$object->nid = $nid;
$node = node_load($nid);
break;
}
}
}
}
}
// If we have successfully loaded the node...
if ($node) {
// Move expandable arrays from the object into the node.
$object->expanded = $to_expand;
$node->expandable_fields = $object->expandable_fields;
unset($object->expandable_fields);
$node->expandable_tables = $object->expandable_tables;
unset($object->expandable_tables);
$node->expandable_nodes = $object->expandable_nodes;
unset($object->expandable_nodes);
// The node becomes the base object with the obejct added to it.
// For example, we may start with a feature object with a name,
// uniquename , type, etc. After expanding we will return the node and
// at $node->feature you will find the original object.
$node->{$base_table} = $object;
$object = $node;
}
// Else we were unable to load the node.
else {
// Warn the administrator
if (isset($object->nid)) {
tripal_report_error('tripal_chado', TRIPAL_ERROR, 'chado_expand_var: No node matches the nid (%nid) supplied.',
array('%nid' => $object->nid));
}
else {
tripal_report_error('tripal_chado', TRIPAL_NOTICE, 'chado_expand_var: There is no node for the current object: <pre>%object</pre>', array('%object' => print_r($object, TRUE)));
}
} //End of if node.
}
// RECURSIVE CASE: check to see if the node to be expanded associates with
// a chado table within one of the nested objects.
else {
// We need to recurse -the node to expand is one of the nested objects
// We assume it's a nested object if the field value is an object.
foreach ((array) $object as $field_name => $field_value) {
if (is_object($field_value)) {
$object->{$field_name} = chado_expand_var(
$field_value,
'node',
$to_expand
);
}
} // End of for each field in the current object.
}
break;
// The $type to be expanded is not yet supported.
default:
tripal_report_error('tripal_chado', TRIPAL_ERROR, 'chado_expand_var: Unrecognized type (%type). Should be one of "field", "table", "node".',
array('%type' => $type));
return FALSE;
}
// Move expandable arrays downwards -------------------------------
// If the type was either table or foreign key then a new chado variable was
// generated this variable will have it's own expandable array's which need to
// be moved down and merged with the base objects expandable arrays.
// Thus, check all nested objects for expandable arrays
// and if they have them, move them downwards.
foreach ((array) $object as $field_name => $field_value) {
if (is_object($field_value)) {
// The current nested object has expandable arrays.
if (isset($field_value->expandable_fields)) {
// Move expandable fields downwards.
if (isset($field_value->expandable_fields) and is_array($field_value->expandable_fields)) {
// If the current object has it's own expandable fields then merge them.
if (isset($object->expandable_fields)) {
$object->expandable_fields = array_merge(
$object->expandable_fields,
$object->{$field_name}->expandable_fields
);
unset($object->{$field_name}->expandable_fields);
}
// Otherwise, just move the expandable fields downwards.
else {
$object->expandable_fields = $object->{$field_name}->expandable_fields;
unset($object->{$field_name}->expandable_fields);
}
}
// Move expandable foreign keys downwards.
if (isset($field_value->expandable_foreign_keys) and is_array($field_value->expandable_foreign_keys)) {
// If the current object has it's own expandable foreign keys then
// merge them.
if (isset($object->expandable_foreign_keys)) {
$object->expandable_foreign_keys = array_merge(
$object->expandable_foreign_keys,
$object->{$field_name}->expandable_foreign_keys
);
unset($object->{$field_name}->expandable_foreign_keys);
}
// Otherwise, just move the expandable foreign keys downwards.
else {
$object->expandable_foreign_keys = $object->{$field_name}->expandable_foreign_keys;
unset($object->{$field_name}->expandable_foreign_keys);
}
}
// Move expandable tables downwards.
if (isset($field_value->expandable_tables) and is_array($field_value->expandable_tables)) {
// If the current object has it's own expandable tables then merge them.
if (isset($object->expandable_tables)) {
$object->expandable_tables = array_merge(
$object->expandable_tables,
$object->{$field_name}->expandable_tables
);
unset($object->{$field_name}->expandable_tables);
}
// Otherwise, just move the expandable tables downwards.
else {
$object->expandable_tables = $object->{$field_name}->expandable_tables;
unset($object->{$field_name}->expandable_tables);
}
}
// Move expandable nodes downwards.
if (isset($field_value->expandable_nodes) and is_array($field_value->expandable_nodes)) {
// If the current object has it's own expandable tables then merge them.
if (isset($object->expandable_nodes)) {
$object->expandable_nodes = array_merge(
$object->expandable_nodes,
$object->{$field_name}->expandable_nodes
);
unset($object->{$field_name}->expandable_nodes);
}
// Otherwise, just move the expandable tables downwards.
else {
$object->expandable_nodes = $object->{$field_name}->expandable_nodes;
unset($object->{$field_name}->expandable_nodes);
}
}
}
}
}
// Move extended array downwards ----------------------------------
// This tells us what we have expanded (ie: that we succeeded)
// and is needed to remove the entry from the expandable array.
// If there is no expanded field in the current object then check any of the
// nested objects and move it down.
if (!property_exists($object, 'expanded')) {
// It's a nested object if the value is an object.
foreach ((array) $object as $field_name => $field_value) {
if (is_object($field_value)) {
// Check if the current nested object has an expanded array.
if (isset($field_value->expanded)) {
// If so, then move it downwards.
$object->expanded = $field_value->expanded;
unset($field_value->expanded);
}
}
}
}
// Check again if there is an expanded field in the current object
// We check again because it might have been moved downwards above.
if (property_exists($object, 'expanded')) {
// If so, then remove the expanded identifier from the correct expandable
// array..
$expandable_name = 'expandable_' . $type . 's';
if (property_exists($object, $expandable_name) and $object->{$expandable_name}) {
$key_to_remove = array_search($object->expanded, $object->{$expandable_name});
unset($object->{$expandable_name}[$key_to_remove]);
unset($object->expanded);
}
}
// Finally, Return the object!
return $object;
}