function chado_generate_var

2.x tripal_core.chado_variables.api.inc chado_generate_var($table, $values, $base_options = array())

Generates an object containing the full details of a record(s) in Chado.

The object returned contains key/value pairs where the keys are the fields in the Chado table.

The returned object differs from the array returned by chado_select_record() as all foreign key relationships in the Chado table have been followed and those data are also included. This function automatically excludes some fields and tables. 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. Use the chado_expand_var() to manually add in excluded fields and data from linker tables.

Example Usage:

  $values = array(
    'name' => 'Medtr4g030710'
  );
  $feature = chado_generate_var('feature', $values);

The $values array passed to this fucntion can be of the same format used by the chado_select_record() function.

If a field is a foreign key then its value is an object that contains key/value pairs for that record. The following code provides examples for retrieving values associated with the record, either as columns in the original Chado table or as columns in linked records through foreign keys:

  // Get the feature name.
  $name = $feature->name;
  // Get the feature unique name.
  $uniquename = $feature->uniquename;
  // Get the feature type. Because the type name is obtained via
  // a foreign key with the cvterm table, the objects are nested
  // and we can follow the foreign key fields to retrieve those values
  $type = $feature->type_id->name;
  // Get the name of the vocabulary.
  $cv = $feature->type_id->cv_id->name;
  // Get the vocabulary id.
  $cv_id = $feature->type_id->cv_id->cv_id;

This will return an object if there is only one feature with the name Medtr4g030710 or it will return an array of feature objects if more than one feature has that name.

Note to Module Designers: Fields can be excluded by default from these objects by implementing one of the following hooks:

  • hook_exclude_field_from_tablename_by_default (where tablename is the name of the table): This hook allows you to add fields to be excluded on a per table basis. Simply implement this hook to return an array of fields to be excluded. The following example will ensure that feature.residues is excluded from a feature object by default:
     mymodule_exclude_field_from_feature_by_default() {
       return array('residues' => TRUE);
     }
   
  • hook_exclude_type_by_default: This hook allows you to exclude fields using conditional. This function should return an array of postgresql types mapped to criteria. If the field types of any table match the criteria then the field is excluded. Tokens available in criteria are >field_value< and >field_name<. The following example 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.
       mymodule_exclude_type_by_default() {
         return array('text' => 'length(>field_value< ) > 50');
       }
     

Parameters

$table: The name of the base table to generate a variable for

$values: A select values array that selects the records you want from the base table (this has the same form as chado_select_record)

$base_options: 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. Additionally, These options are available for this function: -return_array: can be provided to force the function to always return an array. Default behavior is to return a single record if only one record exists or to return an array if multiple records exist.

  • include_fk: an array of FK relationships to follow. By default, the chado_select_record 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.

Return value

Either an object (if only one record was selected from the base table) or an array of objects (if more than one record was selected from the base table). If the option 'return_array' is provided the function always returns an array.

Related topics

65 calls to chado_generate_var()
chado_analysis_load in 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 tripal_contact/includes/tripal_contact.chado_node.inc
Implements hook_load().
chado_example_load in tripal_example/includes/tripal_example.chado_node.inc
Implementation of hook_load(). This function is necessary to load into the $node object the fields of the table form Chado. For example for the example table, the chado_example_load() function adds in a example object which contains all of the fields…
chado_expand_var in tripal_core/api/tripal_core.chado_variables.api.inc
Retrieves fields, or tables that were excluded by default from a variable.
chado_featuremap_load in tripal_featuremap/includes/tripal_featuremap.chado_node.inc
Implements hook_load().

... See full list

1 string reference to 'chado_generate_var'

File

tripal_core/api/tripal_core.chado_variables.api.inc, line 135
This API generates objects containing the full details of a record(s) in chado.

Code

function chado_generate_var($table, $values, $base_options = array()) {
  $all = new stdClass();

  $return_array = 0;
  if (array_key_exists('return_array', $base_options)) {
    $return_array = 1;
  }
  $include_fk = FALSE;
  if (array_key_exists('include_fk', $base_options)) {
    $include_fk = $base_options['include_fk'];
  }
  $pager = array();
  if (array_key_exists('pager', $base_options)) {
    $pager = $base_options['pager'];
  }
  // get description for the current table----------------------------------------------------------
  $table_desc = chado_get_schema($table);
  if (!$table_desc or count($table_desc) == 0) {
    tripal_report_error('tripal_core', TRIPAL_ERROR, 
    "chado_generate_var: The table '%table' has not been defined. " .
      "and cannot be expanded. If this is a custom table, please add it using the Tripal " .
      "custom table interface.", array('%table' => $table));
    if ($return_array) {
      return array();
    }
    return FALSE;
  }
  $table_primary_key = $table_desc['primary key'][0];
  $table_columns = array_keys($table_desc['fields']);

  // Expandable fields without value needed for criteria--------------------------------------------
  // Add in the default expandable arrays
  // These are used for later expanding fields, tables, foreign keys and nodes
  $all->expandable_fields = array();
  $all->expandable_foreign_keys = array();
  if (array_key_exists('referring_tables', $table_desc) and $table_desc['referring_tables']) {
    $all->expandable_tables = $table_desc['referring_tables'];
  }
  else {
    $all->expandable_tables = array();
  }
  $all->expandable_nodes = array();


  // Get fields to be removed by name.................................
  // This gets all implementations of hook_exclude_field_from_<table>_by_default()
  // where <table> is the current table a variable is being created for.

  // This allows modules to specify that some fields should be excluded by default
  // For example, tripal core provides a tripal_core_exclude_field_from_feature_by_default()
  // which says that we usually don't want to include the residues field by default since
  // it can be very large and cause performance issues.

  // If a field is excluded by default it can always be expanded at a later point by calling
  // chado_expand_var($chado_var, 'field', <field name as shown in expandable_fields array>);

  // First get an array of all the fields to be removed for the current table
  // module_invoke_all() is drupal's way of invoking all implementations of the specified
  // hook and merging all of the results.

  // $fields_to_remove should be an array with the keys matching field names
  // and the values being strings to be executed using php_eval() to determine whether
  // to exclude the field (evaluates to TRUE) or not (evaluates to FALSE)
  $fields_to_remove = module_invoke_all('exclude_field_from_' . $table . '_by_default');

  // Now, for each field to be removed
  foreach ($fields_to_remove as $field_name => $criteria) {

    //replace <field_name> with the current field name
    $criteria = preg_replace('/<field_name> /', addslashes($field_name), $criteria);
    // if field_value needed we can't deal with this field yet
    if (preg_match('/<field_value> /', $criteria)) {
      break;
    }

    //if criteria then remove from query
    // @coder-ignore: only module designers can populate $criteria -not a security risk
    $success = php_eval('<?php return ' . $criteria . '; ?>');
    if ($success) {
      unset($table_columns[array_search($field_name, $table_columns)]);
      unset($fields_to_remove[$field_name]);
      $all->expandable_fields[] = $table . '.' . $field_name;
    }
  }

  // Get fields to be removed by type................................
  // This gets all implementations of hook_exclude_type_by_default().

  // This allows modules to specify that some types of fields should be excluded by default
  // For example, tripal core provides a tripal_core_exclude_type_by_default() which says
  // that text fields are often very large and if they are longer than 250 characters then
  // we want to exclude them by default

  // If a field is excluded by default it can always be expanded at a later point by calling
  // chado_expand_var($chado_var, 'field', <field name as shown in expandable_fields array>);

  // First get an array of all the types of fields to be removed for the current table
  // module_invoke_all() is drupal's way of invoking all implementations of the specified
  // hook and merging all of the results.

  // $types_to_remove should be an array with the keys matching field names
  // and the values being strings to be executed using php_eval() to determine whether
  // to exclude the field (evaluates to TRUE) or not (evaluates to FALSE)
  // (ie: array('text' => 'strlen("<field_value> ") > 100');
  $types_to_remove = module_invoke_all('exclude_type_by_default');

  // Get a list of all the types of fields
  // the key is the type of field and the value is an array of fields of this type
  $field_types = array();
  foreach ($table_desc['fields'] as $field_name => $field_array) {
    $field_types[$field_array['type']][] = $field_name;
  }

  // We want to use the types to remove in conjunction with our table field descriptions
  // to determine which fields might need to be removed
  foreach ($types_to_remove as $field_type => $criteria) {

    // if there are fields of that type to remove
    if (isset($field_types[$field_type])) {

      // Do any processing needed on the php criteria
      //replace <field_name>  with the current field name
      $criteria = preg_replace('/<field_name> /', addslashes($field_name), $criteria);
      foreach ($field_types[$field_type] as $field_name) {
        // if field_value needed we can't deal with this field yet
        if (preg_match('/<field_value>/', $criteria)) {
          $fields_to_remove[$field_name] = $criteria;
          continue;
        }

        // if criteria then remove from query
        // (as long as <field_value> is not needed for the criteria to be evaluated)
        // @coder-ignore: only module designers can populate $criteria -not a security risk
        $success = php_eval('<?php return ' . $criteria . '; ?>');
        if ($success) {
          unset($table_columns[array_search($field_name, $table_columns)]);
          $all->expandable_fields[] = $table . '.' . $field_name;
        }
      } //end of foreach field of that type
    }
  } //end of foreach type to be removed

  // get the values for the record in the current table---------------------------------------------
  $results = chado_select_record($table, $table_columns, $values, $base_options);

  if ($results) {
    foreach ($results as $key => $object) {
      // Add empty expandable_x arrays
      $object->expandable_fields = $all->expandable_fields;
      $object->expandable_foreign_keys = $all->expandable_foreign_keys;
      $object->expandable_tables = $all->expandable_tables;
      $object->expandable_nodes = $all->expandable_nodes;
      // add curent table
      $object->tablename = $table;

      // check if the current table maps to a node type-----------------------------------------------
      // if this table is connected to a node there will be a chado_tablename table in drupal
      if (db_table_exists('chado_' . $table)) {
        // that has a foreign key to this one ($table_desc['primary key'][0]
        // and to the node table (nid)
        $sql = "
          SELECT $table_primary_key, nid
          FROM {chado_$table}
          WHERE $table_primary_key = :$table_primary_key
        ";
        $mapping = db_query($sql, array(":$table_primary_key" => $object->{$table_primary_key}))->fetchObject();
        if ($mapping and $mapping->$table_primary_key) {
          $object->nid = $mapping->nid;
          $object->expandable_nodes[] = $table;
        }
      }

      // remove any fields where criteria needs to be evalulated---------------------------------------
      // The fields to be removed can be populated by implementing either
      // hook_exclude_field_from_<table>_by_default() where <table> is the current table
      // OR hook_exclude_type_by_default() where there are fields of the specified type in the current table
      // It only reaches this point if the criteria specified for whether or not to
      // exclude the field includes <field_value> which means it has to be evaluated after
      // the query has been executed
      foreach ($fields_to_remove as $field_name => $criteria) {

        // If the field is an object then we don't support exclusion of it
        // For example, if the field is a foreign key
        if (!isset($object->{$field_name})) {
          break;
        }

        // replace <field_value> with the actual value of the field from the query
        $criteria = preg_replace('/<field_value>/', addslashes($object->{$field_name}), $criteria);

        // evaluate the criteria, if TRUE is returned then exclude the field
        // excluded fields can be expanded later by calling
        // chado_expand_var($var, 'field', <field name as shown in expandable_fields array>);
        $success = php_eval('<?php return ' . $criteria . '; ?>');
        if ($success) {
          unset($object->{$field_name});
          $object->expandable_fields[] = $table . '.' . $field_name;
        }
      }

      // recursively follow foreign key relationships nesting objects as we go------------------------
      if (array_key_exists('foreign keys', $table_desc) and $table_desc['foreign keys']) {
        foreach ($table_desc['foreign keys'] as $foreign_key_array) {
          $foreign_table = $foreign_key_array['table'];
          foreach ($foreign_key_array['columns'] as $foreign_key => $primary_key) {

            // Note: Foreign key is the field in the current table whereas primary_key is the field in
            // the table referenced by the foreign key
            //Dont do anything if the foreign key is empty
            if (empty($object->{$foreign_key})) {
              continue;
            }

            if (is_array($include_fk)) {
              // don't recurse if the callee has supplied an $fk_include list and this
              // FK table is not in the list.
              if (is_array($include_fk) and !array_key_exists($foreign_key, $include_fk)) {
                $object->expandable_foreign_keys[] = $table . '.' . $foreign_key . ' => ' . $foreign_table;
                continue;
              }
            }
            // if we have the option but it is not an array then we don't recurse any furutehr
            if ($include_fk === TRUE) {
              $object->expandable_foreign_keys[] = $table . '.' . $foreign_key . ' => ' . $foreign_table;
              continue;
            }

            // get the record from the foreign table
            $foreign_values = array($primary_key => $object->{$foreign_key});
            $options = array();
            if (is_array($include_fk)) {
              $options['include_fk'] = $include_fk[$foreign_key];
            }

            $foreign_object = chado_generate_var($foreign_table, $foreign_values, $options);

            // add the foreign record to the current object in a nested manner
            $object->{$foreign_key} = $foreign_object;
            // Flatten expandable_x arrays so only in the bottom object
            if (property_exists($object->{$foreign_key}, 'expandable_fields') and 
              is_array($object->{$foreign_key}->expandable_fields)) {
              $object->expandable_fields = array_merge(
              $object->expandable_fields, 
              $object->{$foreign_key}->expandable_fields
              );
              unset($object->{$foreign_key}->expandable_fields);
            }
            if (property_exists($object->{$foreign_key}, 'expandable_foreign_keys') and 
              is_array($object->{$foreign_key}->expandable_foreign_keys)) {
              $object->expandable_foreign_keys = array_merge(
              $object->expandable_foreign_keys, 
              $object->{$foreign_key}->expandable_foreign_keys
              );
              unset($object->{$foreign_key}->expandable_foreign_keys);
            }
            if (property_exists($object->{$foreign_key}, 'expandable_tables') and 
              is_array($object->{$foreign_key}->expandable_tables)) {
              $object->expandable_tables = array_merge(
              $object->expandable_tables, 
              $object->{$foreign_key}->expandable_tables
              );
              unset($object->{$foreign_key}->expandable_tables);
            }
            if (property_exists($object->{$foreign_key}, 'expandable_nodes') and 
              is_array($object->{$foreign_key}->expandable_nodes)) {
              $object->expandable_nodes = array_merge(
              $object->expandable_nodes, 
              $object->{$foreign_key}->expandable_nodes
              );
              unset($object->{$foreign_key}->expandable_nodes);
            }
          }
        }
        $results[$key] = $object;
      }
    }
  }

  // convert the results into an array
  $results_arr = array();
  foreach ($results as $record) {
    $results_arr[] = $record;
  }
  // check only one result returned
  if (!$return_array) {
    if (sizeof($results_arr) == 1) {
      // add results to object
      return $results_arr[0];
    }
    elseif (!empty($results_arr)) {
      return $results_arr;
    }
    else {
      // no results returned
    }
  }
  // the caller has requested results are always returned as
  // an array
  else {
    if (!$results_arr) {
      return array();
    }
    else {
      return $results_arr;
    }
  }
}