function tripal_chado_field_storage_query

3.x tripal_chado.field_storage.inc tripal_chado_field_storage_query($query)

Implements hook_field_storage_query().

File

tripal_chado/includes/tripal_chado.field_storage.inc, line 462

Code

function tripal_chado_field_storage_query($query) {

  // Initialize the result array.
  $result = array(
    'TripalEntity' => array()
  );

  // There must always be an entity filter that includes the bundle. Otherwise
  // it would be too overwhelming to search every table of every content type
  // for a matching field.
  if (!array_key_exists('bundle', $query->entityConditions)) {
    return $result;
  }
  $bundle = tripal_load_bundle_entity(array('name' => $query->entityConditions['bundle']));
  if (!$bundle) {
    return;
  }
  $data_table = $bundle->data_table;
  $type_column = $bundle->type_column;
  $type_id = $bundle->type_id;
  $schema = chado_get_schema($data_table);
  $pkey = $schema['primary key'][0];

  // Initialize the Query object.
  $cquery = chado_db_select($data_table, 'base');
  $cquery->fields('base', array($pkey));

  // Iterate through all the conditions and add to the filters array
  // a chado_select_record compatible set of filters.
  foreach ($query->fieldConditions as $index => $condition) {
    $field = $condition['field'];
    $field_name = $field['field_name'];
    $field_type = $field['type'];

    // Skip conditions that don't belong to this storage type.
    if ($field['storage']['type'] != 'field_chado_storage') {
      continue;
    }

    // The Chado settings for a field are part of the instance and each bundle
    // can have the same field but with different Chado mappings. Therefore,
    // we need to iterate through the bundles to get the field instances.
    foreach ($field['bundles']['TripalEntity'] as $bundle_name) {

      // If there is a bundle filter for the entity and if the field is not
      // associated with the bundle then skip it.
      if (array_key_exists('bundle', $query->entityConditions)) {
        if (strtolower($query->entityConditions['bundle']['operator']) == 'in' and 
          !array_key_exists($bundle_name, $query->entityConditions['bundle']['value'])) {
          continue;
        }
        else if ($query->entityConditions['bundle']['value'] != $bundle_name) {
          continue;
        }
      }

      // Allow the field to update the query object.
      $instance = field_info_instance('TripalEntity', $field['field_name'], $bundle_name);
      if (tripal_load_include_field_class($field_type)) {
        $field_obj = new $field_type($field, $instance);
        $field_obj->query($cquery, $condition);
      }
      // If there is not a ChadoField class for this field then add the
      // condition as is.
      else {
        $alias = $field['field_name'];
        $chado_table = $instance['settings']['chado_table'];
        $base_table = $instance['settings']['base_table'];
        $bschema = chado_get_schema($base_table);
        $bpkey = $bschema['primary key'][0];

        if ($chado_table == $base_table) {
          // Get the base table column that is associated with the term
          // passed as $condition['column'].
          $base_field = chado_get_semweb_column($chado_table, $condition['column']);

          $matches = array();
          $key = $condition['value'];
          $op = '=';
          if (preg_match('/^(.+);(.+)$/', $key, $matches)) {
            $key = $matches[1];
            $op = $matches[2];
          }
          switch ($op) {
            case 'eq':
              $op = '=';
              break;
            case 'gt':
              $op = '>';
              break;
            case 'gte':
              $op = '>=';
              break;
            case 'lt':
              $op = '<';
              break;
            case 'lte':
              $op = '<=';
              break;
            case 'ne':
              $op = '<>';
              break;
            case 'contains':
              $op = 'LIKE';
              $key = '%' . $key . '%';
              break;
            case 'starts':
              $op = 'LIKE';
              $key = $key . '%';
              break;
            default:
              $op = '=';
          }
          $cquery->condition('base.' . $base_field, $key, $op);
        }
        if ($chado_table != $base_table) {
          // TODO: I don't think we'll get here because linker fields will
          // always have a custom field that should implement a query()
          // function. But just in case here's a note to handle it.
        }
      }
    }
  } // end foreach ($query->fieldConditions as $index => $condition) {

  // Now join with the chado entity table to get published records only.
  $chado_entity_table = chado_get_bundle_entity_table($bundle);
  $cquery->join($chado_entity_table, 'CE', "CE.record_id = base.$pkey");
  $cquery->join('tripal_entity', 'TE', "CE.entity_id = TE.id");
  $cquery->fields('CE', array('entity_id'));
  $cquery->fields('TE', array('bundle'));
  if (array_key_exists('start', $query->range)) {
    $cquery->range($query->range['start'], $query->range['length']);
  }
  // Make sure we only get records of the correct entity type
  $cquery->condition('TE.bundle', $query->entityConditions['bundle']['value']);

  // Iterate through all the property conditions. These will be filters
  // on the tripal_entity table.
  foreach ($query->propertyConditions as $index => $condition) {
    $cquery->condition('TE.' . $condition['column'], $condition['value']);
  }

  // If there is an entity_id filter then apply that.
  if (array_key_exists('entity_id', $query->entityConditions)) {
    $value = $query->entityConditions['entity_id']['value'];
    $op = '';
    if (array_key_exists('op', $query->entityConditions['entity_id'])) {
      $op = $query->entityConditions['entity_id']['op'];
    }
    // Handle a single entity_id
    if (!is_array($value)) {
      switch ($op) {
        case 'CONTAINS':
          $op = 'LIKE';
          $value = '%' . $value . '%';
          break;
        case 'STARTS_WITH':
          $op = 'LIKE';
          $value = $value . '%';
          break;
        case 'BETWEEN':
          // This case doesn't make sense for a single value. So, just set it
          // equal to the end.
          $op = '=';
        default:
          $op = $op ? $op : '=';
      }
    }
    // Handle multiple entitie IDs.
    else {
      switch ($op) {
        case 'CONTAINS':
          $op = 'IN';
          break;
        case 'STARTS_WITH':
          $op = 'IN';
          break;
        case 'BETWEEN':
          $op = 'BETWEEN';
        default:
          $op = 'IN';
      }
    }
    if ($op == 'BETWEEN') {
      $cquery->condition('TE.id', $value[0], '>');
      $cquery->condition('TE.id', $value[1], '<');
    }
    else {
      $cquery->condition('TE.id', $value, $op);
    }
  }

  // Now set any ordering.
  foreach ($query->order as $index => $sort) {
    // Add in property ordering.
    if ($sort['type'] == 'property') {
      // TODO: support ordering by bundle properties.
    }
    // Add in filter ordering
    if ($sort['type'] == 'field') {

      $field = $sort['specifier']['field'];
      $field_type = $field['type'];
      $field_name = $field['field_name'];

      // Skip sorts that don't belong to this storage type.
      if ($field['storage']['type'] != 'field_chado_storage') {
        continue;
      }
      $column = $sort['specifier']['column'];
      $direction = $sort['direction'];

      // The Chado settings for a field are part of the instance and each bundle
      // can have the same field but with different Chado mappings. Therefore,
      // we need to iterate through the bundles to get the field instances.
      foreach ($field['bundles']['TripalEntity'] as $bundle_name) {

        // If there is a bundle filter for the entity and if the field is not
        // associated with the bundle then skip it.
        if (array_key_exists('bundle', $query->entityConditions)) {
          if (strtolower($query->entityConditions['bundle']['operator']) == 'in' and 
            !array_key_exists($bundle_name, $query->entityConditions['bundle']['value'])) {
            continue;
          }
          else if ($query->entityConditions['bundle']['value'] != $bundle_name) {
            continue;
          }
        }

        // See if there is a ChadoField class for this instance. If not then do
        // our best to order the field.
        $instance = field_info_instance('TripalEntity', $field_name, $bundle_name);
        if (tripal_load_include_field_class($field_type)) {
          $field_obj = new $field_type($field, $instance);
          $field_obj->queryOrder($cquery, array('column' => $column, 'direction' => $direction));
        }
        // There is no class so do our best to order the data by this field
        else {
          $base_table = $instance['settings']['base_table'];
          $chado_table = $instance['settings']['chado_table'];
          $table_column = chado_get_semweb_column($chado_table, $column);
          if ($table_column) {
            if ($chado_table == $base_table) {
              $cquery->orderBy('base.' . $table_column, $direction);
            }
            else {
              // TODO: how do we handle a field that doesn't map to the base table.
              // We would expect that all of these would be custom field and
              // the ChadoField::queryOrder() function would be implemented.
            }
          }
          else {
            // TODO: handle when the name can't be matched to a table column.
          }
        }
      } // end foreach ($field['bundles']['TripalEntity'] as $bundle_name) {
    } // end if ($sort['type'] == 'field') {
  } // end foreach ($query->order as $index => $sort) {

  // dpm($cquery->__toString());
  // dpm($cquery->getArguments());

  $records = $cquery->execute();

  // If the query is a count query then just return the  total count.
  if ($query->count) {
    return $records->rowCount();
  }

  // If this is not a count query then return the results.
  $result = array();
  while ($record = $records->fetchObject()) {
    $ids = array($record->entity_id, 0, $record->bundle);
    $result['TripalEntity'][$record->entity_id] = entity_create_stub_entity('TripalEntity', $ids);
  }
  return $result;
}