Browse Source

Sorting in Views of TripalFields now working

Stephen Ficklin 7 years ago
parent
commit
8a9dca5510

+ 8 - 4
tripal/includes/TripalFieldQuery.inc

@@ -6,9 +6,14 @@
  */
 class TripalFieldQuery extends EntityFieldQuery {
 
-  protected $field_storage = array();
-
+  /**
+   * Holds a list of fields that should be included in the results
+   */
+  protected $includes = array();
 
+  /**
+   * Overrides the EntityFieldQuery::execute() function.
+   */
   public function execute() {
     // Give a chance for other modules to alter the query.
     drupal_alter('entity_query', $this);
@@ -73,7 +78,6 @@ class TripalFieldQuery extends EntityFieldQuery {
     }
   }
 
-
   /**
    * Determines the query callback to use for this entity query.
    *
@@ -89,7 +93,7 @@ class TripalFieldQuery extends EntityFieldQuery {
    *   A callback that can be used with call_user_func().
    *
    */
-  public function queryStorageCallback($storage) {
+  protected function queryStorageCallback($storage) {
     // Use the override from $this->executeCallback. It can be set either
     // while building the query, or using hook_entity_query_alter().
     if (function_exists($this->executeCallback)) {

+ 2 - 3
tripal/includes/TripalFields/TripalField.inc

@@ -305,8 +305,7 @@ class TripalField {
     );
     // Is the field sortable?
     if (array_key_exists('sortable', $field_details) and $field_details['sortable']) {
-      $data[$view_base_id][$field_name]['sort']['click handler'] = 'views_handler_sort';
-      $data[$view_base_id][$field_name]['field']['click sortable'] = TRUE;
+      $data[$view_base_id][$field_name]['sort']['handler'] = 'tripal_views_handler_sort';
     }
 
     // Is the field searchable?
@@ -368,7 +367,7 @@ class TripalField {
     );
     // Is the field sortable?
     if (array_key_exists('sortable', $element_details) and $element_details['sortable']) {
-      $data[$view_base_id][$field_name]['sort']['click handler'] = 'views_handler_sort';
+      $data[$view_base_id][$field_name]['sort']['handler'] = 'tripal_views_handler_sort';
       $data[$view_base_id][$field_name]['field']['click sortable'] = TRUE;
     }
 

+ 1 - 0
tripal/tripal.info

@@ -23,6 +23,7 @@ files[] = views_handlers/tripal_views_handler_filter_element_string.inc
 files[] = views_handlers/tripal_views_handler_filter_boolean_operator.inc
 files[] = views_handlers/tripal_views_handler_filter_entity_string.inc
 files[] = views_handlers/tripal_views_handler_filter_string_selectbox.inc
+files[] = views_handlers/tripal_views_handler_sort.inc
 files[] = views_handlers/tripal_views_handler_sort_entity_string.inc
 
 files[] = tripal_views_query.inc

+ 51 - 20
tripal/tripal.views.inc

@@ -36,6 +36,53 @@ function tripal_views_data() {
   return $data;
 }
 
+/**
+ * Implements hook views_data_alter()
+ *
+ * Ensures that all fields attached to TripalEntities use the proper
+ * handlers.
+ */
+function tripal_views_data_alter(&$data) {
+
+  $fields = field_info_fields();
+  $tripal_fields = tripal_get_field_types();
+
+  // Iterate through all of the fields.
+  foreach ($fields as $field) {
+
+    // Skip fields that aren't attached to TripalEntity entities.
+    if (!array_key_exists('TripalEntity', $field['bundles'])) {
+      continue;
+    }
+
+    // Iterate through the bundles to which this field is attached and
+    // if it is a TripalField field then we'll call the viewsData function.
+    $bundles = $field['bundles']['TripalEntity'];
+    $result = array();
+    foreach ($bundles as $bundle_name) {
+      $field_name = $field['field_name'];
+
+      // Skip fields that aren't setup for views with this bundle.
+      // Fields should be associated with the bundle's term identifier
+      // (i.e. [vocab]__[accession].
+      $bundle = tripal_load_bundle_entity(array('name' => $bundle_name));
+      $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
+      $bundle_term_id = $term->vocab->vocabulary . '__' . $term->accession;
+      if (!array_key_exists($bundle_term_id, $data)) {
+        continue;
+      }
+
+      // Skip fields implemented using a TripalField class. These fields
+      // should already have the proper handlers because the class sets
+      // them up automatically.
+      if (in_array($field_name, $tripal_fields)) {
+        continue;
+      }
+
+      $data[$bundle_term_id][$field_name]['sort']['handler'] = 'tripal_views_handler_sort';
+    }
+  }
+}
 /**
  * Integreates the Tripal fields with Views.
  */
@@ -50,6 +97,8 @@ function tripal_views_data_fields(&$data) {
       continue;
     }
 
+    // TODO: do we really need this hook? Just substitute the code here
+    // for what that hook does... it's only called in one plac.e
     // Call the hook_field_views_data() but only for the tripal module.
     // Otherwise the other modules will expect that this is an SQL-based
     // view.
@@ -120,7 +169,7 @@ function tripal_views_data_tripal_entity(&$data) {
         'handler' => 'tripal_views_handler_filter',
       ),
       'sort' => array(
-        'handler' => 'views_handler_sort',
+        'handler' => 'tripal_views_handler_sort',
       ),
     );
     $data[$table]['link'] = array(
@@ -129,12 +178,6 @@ function tripal_views_data_tripal_entity(&$data) {
       'field' => array(
         'handler' => 'tripal_views_handler_field_entity_link',
       ),
-      'filter' => array(
-        'handler' => 'tripal_views_handler_filter',
-      ),
-      'sort' => array(
-        'handler' => 'views_handler_sort',
-      ),
     );
     $data[$table]['edit_link'] = array(
       'title' => t('Edit Link'),
@@ -142,12 +185,6 @@ function tripal_views_data_tripal_entity(&$data) {
       'field' => array(
         'handler' => 'tripal_views_handler_field_entity_link_edit',
       ),
-      'filter' => array(
-        'handler' => 'tripal_views_handler_filter',
-      ),
-      'sort' => array(
-        'handler' => 'views_handler_sort',
-      ),
     );
     $data[$table]['delete_link'] = array(
       'title' => t('Delete Link'),
@@ -155,12 +192,6 @@ function tripal_views_data_tripal_entity(&$data) {
       'field' => array(
         'handler' => 'tripal_views_handler_field_entity_link_delete',
       ),
-      'filter' => array(
-        'handler' => 'tripal_views_handler_filter',
-      ),
-      'sort' => array(
-        'handler' => 'views_handler_sort',
-      ),
     );
     $data[$table]['status'] = array(
       'title' => t('Published'),
@@ -179,7 +210,7 @@ function tripal_views_data_tripal_entity(&$data) {
         'use equal' => TRUE, // Use status = 1 instead of status <> 0 in WHERE statment
       ),
       'sort' => array(
-        'handler' => 'views_handler_sort',
+        'handler' => 'tripal_views_handler_sort',
       ),
     );
   }

+ 93 - 40
tripal/tripal_views_query.inc

@@ -2,13 +2,34 @@
 
 class tripal_views_query extends views_plugin_query {
 
-  // The EntityFieldQuery object.
+  /**
+   * The EntityFieldQuery object used to perform the query
+   */
   var $query;
 
+  /**
+   * The EntityFieldQuery object used to perform the count query.
+   * It will not include the order by and only fields that are used in
+   * filters.
+   */
+  var $cquery;
+
+  /**
+   * The fields that are to be included in the query.
+   */
   var $fields;
 
+  /**
+   * The filters that are to be included in the query.
+   */
   var $filters;
 
+  /**
+   * The sort item that are to be included in the query.
+   */
+  var $sort;
+
+
 
   /**
    * Ensure a table exists in the queue.
@@ -45,6 +66,8 @@ class tripal_views_query extends views_plugin_query {
 
     // Creqte the TripalFieldQuery object.
     $this->query = new TripalFieldQuery();
+    $this->cquery = new TripalFieldQuery();
+    $this->cquery->count();
 
     // Convert the $base_table into the bundle table.  Because every
     // tripal site will have different bundle tables we have to do the
@@ -56,6 +79,9 @@ class tripal_views_query extends views_plugin_query {
     // Make sure we only query on the entities for this bundle type.
     $this->query->entityCondition('entity_type', 'TripalEntity');
     $this->query->entityCondition('bundle', $bundle->name);
+
+    $this->cquery->entityCondition('entity_type', 'TripalEntity');
+    $this->cquery->entityCondition('bundle', $bundle->name);
   }
   /**
    *
@@ -100,50 +126,78 @@ class tripal_views_query extends views_plugin_query {
       // Handle the bundle properties separate from real fields.
       if ($field_name == 'entity_id' or $field_name == 'status') {
         $this->query->propertyCondition($field_name, $value, $operator);
+        $this->cquery->propertyCondition($field_name, $value, $operator);
+        return;
       }
-      else if ($field_name == 'link' or $field_name == 'edit_link' or $field_name == 'delete_link') {
-        // TODO: not sure how to handle these just yet.
+
+      // If the field_name comes to us with a period in it then it means that
+      // we need to separate the field name from sub-element names.
+      $matches = array();
+      if (preg_match('/^(.+?)\.(.*)$/', $field_name, $matches)) {
+         $field_name = $matches[1];
+         $element_name = $matches[2];
+         if (tripal_load_include_field_class($field_name)) {
+           $field = field_info_field($field_name);
+           $instance = field_info_instance('TripalEntity', $field_name, $this->query->entityConditions['bundle']['value']);
+           $field_obj = new $field_name($field, $instance);
+           $element_name = $field_obj->getFieldTermID() . ',' . $element_name;
+           // Replace periods with commas.
+           $element_name = preg_replace('/\./', ',', $element_name);
+           $this->query->fieldCondition($field_name, $element_name, $value, $operator);
+           $this->cquery->fieldCondition($field_name, $element_name, $value, $operator);
+         }
+         else {
+           throw new Exception("Unkown element id: '$element_name'.");
+         }
       }
       else {
-        // If the field_name comes to us with a period in it then it means that
-        // we need to separate the field name from sub-element names.
-        $matches = array();
-        if (preg_match('/^(.+?)\.(.*)$/', $field_name, $matches)) {
-           $field_name = $matches[1];
-           $element_name = $matches[2];
-           if (tripal_load_include_field_class($field_name)) {
-             $field = field_info_field($field_name);
-             $instance = field_info_instance('TripalEntity', $field_name, $this->query->entityConditions['bundle']['value']);
-             $field_obj = new $field_name($field, $instance);
-             $element_name = $field_obj->getFieldTermID() . ',' . $element_name;
-             // Replace periods with commas.
-             $element_name = preg_replace('/\./', ',', $element_name);
-             $this->query->fieldCondition($field_name, $element_name, $value, $operator);
-           }
-           else {
-             throw new Exception("Unkown element id: '$element_name'.");
-           }
-        }
-        else {
-          $instance = field_info_instance('TripalEntity', $field_name, $this->query->entityConditions['bundle']['value']);
-          $field_term = $instance['settings']['term_vocabulary'] . ':' . $instance['settings']['term_accession'];
-          $this->query->fieldCondition($field_name, $field_term, $value, $operator);
-        }
+        $instance = field_info_instance('TripalEntity', $field_name, $this->query->entityConditions['bundle']['value']);
+        $field_term = $instance['settings']['term_vocabulary'] . ':' . $instance['settings']['term_accession'];
+        $this->query->fieldCondition($field_name, $field_term, $value, $operator);
+        $this->cquery->fieldCondition($field_name, $field_term, $value, $operator);
       }
     }
   }
 
-  public function add_orderby($table, $field = NULL, $order = 'ASC',
-      $alias = '', $params = array()) {
+  /**
+   * Overrides add_orderby().
+   */
+  public function add_orderby($table, $field_name = NULL, $order = 'ASC', $alias = '', $params = array()) {
+
+    if ($field_name) {
+      // Make sure we don't put the orderby in more than once.
+      foreach ($this->order as $index => $order_details) {
+        if ($order_details['field'] == $field_name) {
+          return;
+        }
+      }
+      $this->order[] = array(
+        'field' => $field_name,
+        'direction' => strtoupper($order)
+      );
 
-    $this->orderby[] = array(
-      'field' => $field,
-      'direction' => strtoupper($order)
-    );
+      // If the field_name comes to us with a period in it then it means that
+      // we need to separate the field name from sub-element names.
+      $matches = array();
+      if (preg_match('/^(.+?)\.(.*)$/', $field_name, $matches)) {
+        $field_name = $matches[1];
+        $element_name = $matches[2];
+        $field = field_info_field($field_name);
+        $instance = field_info_instance('TripalEntity', $field_name, $this->query->entityConditions['bundle']['value']);
+        $element_name = $instance['settings']['term_vocabulary'] . ':' . $instance['settings']['term_accession']  . ',' . $element_name;
+        $this->query->fieldOrderBy($field_name, $element_name, $order);
 
+      }
+      else {
+        $instance = field_info_instance('TripalEntity', $field_name, $this->query->entityConditions['bundle']['value']);
+        $field_term = $instance['settings']['term_vocabulary'] . ':' . $instance['settings']['term_accession'];
+        $this->query->fieldOrderBy($field_name, $field_term, $order);
+      }
+    }
   }
+
   /**
-   *
+   * Overrides build().
    */
   function build(&$view) {
     // Make the query distinct if the option was set.
@@ -159,9 +213,8 @@ class tripal_views_query extends views_plugin_query {
     // Let the pager modify the query to add limits.
     $this->pager->query();
 
-    $cquery = clone $this->query;
     $view->build_info['query'] = $this->query;
-    $view->build_info['count_query'] = $cquery->count();
+    $view->build_info['count_query'] = $this->cquery;
   }
   /**
    *
@@ -169,7 +222,7 @@ class tripal_views_query extends views_plugin_query {
    */
   function execute(&$view) {
     $query = $view->build_info['query'];
-    $count_query = $view->build_info['count_query'];
+    $cquery = $view->build_info['count_query'];
 
     if ($query) {
       $start = microtime(TRUE);
@@ -184,7 +237,8 @@ class tripal_views_query extends views_plugin_query {
           // really should create a new tripal_views_plugin_pager class
           // and call the corresponding function here, but due to time
           // constraints this is the shortcut.
-          $this->pager->total_items = $count_query->execute();
+          $total_items = $cquery->execute();
+          $this->pager->total_items = $total_items;
           if (!empty($this->pager->options['offset'])) {
             $this->pager->total_items -= $this->pager->options['offset'];
           };
@@ -201,11 +255,9 @@ class tripal_views_query extends views_plugin_query {
 
         // Get the IDs
         $results = $query->execute();
-
         $entity_ids = array_keys($results['TripalEntity']);
 
         $this->pager->post_execute($view->result);
-
         if ($this->pager->use_count_query() || !empty($view->get_total_rows)) {
           $view->total_rows = $this->pager->get_total_items();
         }
@@ -222,6 +274,7 @@ class tripal_views_query extends views_plugin_query {
           }
         }
 
+        // Get the entity IDs from the query.
         $entities = tripal_load_entity('TripalEntity', $entity_ids, FALSE, $field_ids);
         $i = 0;
         foreach ($entities as $entity_id => $entity) {

+ 12 - 0
tripal/views_handlers/tripal_views_handler_field.inc

@@ -22,6 +22,18 @@ class tripal_views_handler_field extends views_handler_field {
     $this->field_alias = $this->real_field;
   }
 
+  /**
+   * Overrides click_sort().
+   */
+  function click_sort($order) {
+    if (isset($this->field_alias)) {
+      // Since fields should always have themselves already added, just
+      // add a sort on the field.
+      $params = $this->options['group_type'] != 'group' ? array('function' => $this->options['group_type']) : array();
+      $this->query->add_orderby($this->table_alias, $this->real_field, $order, $this->field_alias, $params);
+    }
+  }
+
   /**
    * Get the value that's supposed to be rendered.
    *

+ 12 - 3
tripal/views_handlers/tripal_views_handler_field_element.inc

@@ -33,7 +33,6 @@ class tripal_views_handler_field_element extends tripal_views_handler_field {
    *   Optional name of the field where the value is stored.
    */
   function get_value($values, $field = NULL) {
-
     $field_name = $this->field_alias;
 
     if (preg_match('/^(.+?)\.(.*)$/', $field_name, $matches)) {
@@ -65,9 +64,19 @@ class tripal_views_handler_field_element extends tripal_views_handler_field {
     $value = $this->get_value($values);
 
     // Handle single value fields:
-    if (count($value == 1)) {
+    if (count($value) == 0) {
+      return '';
+    }
+    if (count($value) == 1) {
       return $this->sanitize_value($value[0]['value'][$element_name], 'xss');
     }
-    return '';
+    else {
+      dpm($value);
+      return t('Too many values to show.');
+    }
+  }
+
+  protected function get_element_value($value, $element_name) {
+
   }
 }

+ 0 - 10
tripal/views_handlers/tripal_views_handler_sort_entity_string.inc

@@ -1,15 +1,5 @@
 <?php
 
-/**
- * @file
- * @todo.
- */
-
-/**
- * @defgroup views_sort_handlers Views sort handlers
- * @{
- * Handlers to tell Views how to sort queries.
- */
 
 /**
  * Base sort handler that has no options and performs a simple sort.

+ 34 - 0
tripal_chado/includes/TripalFields/ChadoField.inc

@@ -88,6 +88,40 @@ class ChadoField extends TripalField {
 
   }
 
+  /**
+   * A convient way to join a table to a query without duplicates.
+   *
+   * @param $query
+   *   The SelectQuery object.
+   * @param $table
+   *   The table to join.
+   * @param $alias
+   *   The table alias to use.
+   * @param $condition
+   *   The join condition.
+   * @param $type
+   *   The type of join: INNER, LEFT OUTER, or RIGHT OUTER.
+   */
+  protected function queryJoinOnce($query, $table, $alias, $condition, $type = 'INNER') {
+    $joins = $query->getTables();
+
+    // If this join is already present then don't add it again.
+    if (in_array($alias, array_keys($joins))) {
+      return;
+    }
+
+    switch($type) {
+      case 'LEFT OUTER':
+        $query->leftjoin($table, $alias, $condition);
+        break;
+      case 'RIGHT OUTER':
+        $query->rightjoin($table, $alias, $condition);
+        break;
+      default:
+        $query->innerjoin($table, $alias, $condition);
+    }
+  }
+
   /**
    * @see TripalField::instanceSettingsForm()
    */

+ 2 - 2
tripal_chado/includes/TripalFields/chado_linker__contact/chado_linker__contact.inc

@@ -190,8 +190,8 @@ class chado_linker__contact extends ChadoField {
     $description_term = tripal_get_chado_semweb_term('contact', 'description');
 
     if ($condition['column'] == $name_term) {
-      $query->join($contact_linker, $alias, "base.$bpkey = $alias.$bpkey");
-      $query->join('contact', 'C', "C.contact_id = $alias.contact_id");
+      $this->queryJoinOnce($query, $contact_linker, $alias, "base.$bpkey = $alias.$bpkey");
+      $this->queryJoinOnce($query, 'contact', 'C', "C.contact_id = $alias.contact_id");
       $query->condition("C.name", $condition['value'], $operator);
     }
     if ($condition['column'] == 'contact.type') {

+ 1 - 1
tripal_chado/includes/TripalFields/chado_linker__prop/chado_linker__prop.inc

@@ -140,7 +140,7 @@ class chado_linker__prop extends ChadoField {
 
     $cvterm = tripal_get_cvterm(array('id' => $vocab . ':' . $accession));
 
-    $query->join($prop_linker, $alias, "base.$bpkey = $alias.$bpkey");
+    $this->queryJoinOnce($query, $prop_linker, $alias, "base.$bpkey = $alias.$bpkey");
     $query->condition("$alias.type_id", $cvterm->cvterm_id);
     $query->condition("$alias.value", $condition['value'], $operator);
 

+ 1 - 23
tripal_chado/includes/TripalFields/data__accession/data__accession.inc

@@ -36,28 +36,6 @@ class data__accession extends ChadoField {
     // to allow the site admin to change the term settings above.
     'term_fixed' => FALSE,
   );
-
-  // In order for this field to integrate with Drupal Views, a set of
-  // handlers must be specififed.  These include handlers for
-  // the field, for the filter, and the sort.  Within this variable,
-  // the key must be one of: field, filter, sort; and the value
-  // is the settings for those handlers as would be provided by
-  // a hook_views_data().  The following defaults make a field visible
-  // using the default formatter of the field, allow for filtering using
-  // a string value and sortable.  in order for filters to work you
-  // must implement the query() function.
-  public static $default_view_handlers = array(
-    'field' => array(
-      'handler' => 'tripal_views_handler_field',
-      'click sortable' => TRUE,
-    ),
-    'filter' => array(
-      'handler' => 'tripal_views_handler_filter_string',
-    ),
-    'sort' => array(
-      'handler' => 'views_handler_sort',
-    ),
-  );
   // The default widget for this field.
   public static $default_widget = 'data__accession_widget';
 
@@ -117,7 +95,7 @@ class data__accession extends ChadoField {
     // We don't offer any sub elements so the value coming in should
     // always be the field_name.
     if ($condition['column'] == 'data__accession') {
-      $query->join('dbxref', 'DBX', "DBX.dbxref_id = base.dbxref_id");
+      $this->queryJoinOnce($query, 'dbxref', 'DBX', "DBX.dbxref_id = base.dbxref_id");
       $query->condition("DBX.accession", $condition['value'], $operator);
     }
   }

+ 23 - 20
tripal_chado/includes/TripalFields/obi__organism/obi__organism.inc

@@ -78,7 +78,7 @@ class obi__organism extends ChadoField {
       if (!$organism_id or $organism_id == 0) {
         $errors[$field_name]['und'][0][] = array(
           'message' =>  t("Please specify an organism."),
-          'error' => 'chado_base__organism_id'
+          'error' => 'obi__organism_id'
         );
       }
     }
@@ -231,7 +231,7 @@ class obi__organism extends ChadoField {
             'searchable' => TRUE,
             'name' => 'scientfic_name',
             'operations' => array('eq', 'ne', 'contains', 'starts'),
-            'sortable' => TRUE,
+            'sortable' => FALSE,
           ),
           $genus_term => array(
             'searchable' => TRUE,
@@ -272,14 +272,14 @@ class obi__organism extends ChadoField {
     $alias = $this->field['field_name'];
     $operator = $condition['operator'];
 
-    //dpm($condition);
-
     $field_term_id = $this->getFieldTermID();
     $genus_term = $field_term_id . ',' . tripal_get_chado_semweb_term('organism', 'genus');
     $species_term = $field_term_id . ',' . tripal_get_chado_semweb_term('organism', 'species');
     $infraspecific_name_term = $field_term_id . ',' . tripal_get_chado_semweb_term('organism', 'infraspecific_name');
     $infraspecific_type_term = $field_term_id . ',' . tripal_get_chado_semweb_term('organism', 'type_id');
-    $query->join('organism', $alias, "base.organism_id = $alias.organism_id");
+
+    // Join to the organism table for this field.
+    $this->queryJoinOnce($query, 'organism', $alias, "base.organism_id = $alias.organism_id");
 
     // If the column is the field name then we're during a search on the full
     // scientific name.
@@ -289,7 +289,7 @@ class obi__organism extends ChadoField {
         $query->where("CONCAT($alias.genus, ' ', $alias.species) $operator :full_name",  array(':full_name' => $condition['value']));
       }
       else {
-        $query->leftJoin('cvterm', $alias . '_cvterm', 'base.infraspecific_type = ' . $alias . '_cvterm.type_id');
+        $this->queryJoinOnce($query, 'cvterm', $alias . '_cvterm', 'base.infraspecific_type = ' . $alias . '_cvterm.type_id', 'LEFT OUTER');
         $query->where("CONCAT($alias.genus, ' ', $alias.species, ' ', " . $alias . "'_cvterm.name', ' ', $alias.infraspecific_name) $operator :full_name",  array(':full_name' => $condition['value']));
       }
     }
@@ -305,7 +305,7 @@ class obi__organism extends ChadoField {
       $query->condition("$alias.infraspecific_name", $condition['value'], $operator);
     }
     if ($condition['column'] == $infraspecific_type_term) {
-      $query->join('cvterm', 'CVT', "base.type_id = CVT.cvterm_id");
+      $this->queryJoinOnce($query, 'cvterm', 'CVT', "base.type_id = CVT.cvterm_id");
       $query->condition("CVT.name", $condition['value'], $operator);
     }
   }
@@ -314,27 +314,30 @@ class obi__organism extends ChadoField {
    * @see ChadoField::queryOrder()
    */
   public function queryOrder($query, $order) {
+    $alias = $this->field['field_name'];
 
-    // If the table hasn't yet been joined then add it.
-    $joins = $query->getTables();
-    if (!in_array($this->field['field_name'], $joins)) {
-      $alias = $this->field['field_name'];
-      $query->join('organism', $alias, "base.organism_id = $alias.organism_id");
-    }
+    $field_term_id = $this->getFieldTermID();
+    $genus_term = $field_term_id . ',' . tripal_get_chado_semweb_term('organism', 'genus');
+    $species_term = $field_term_id . ',' . tripal_get_chado_semweb_term('organism', 'species');
+    $infraspecific_name_term = $field_term_id . ',' . tripal_get_chado_semweb_term('organism', 'infraspecific_name');
+    $infraspecific_type_term = $field_term_id . ',' . tripal_get_chado_semweb_term('organism', 'type_id');
+
+    // Join to the organism table for this field.
+    $this->queryJoinOnce($query, 'organism', $alias, "base.organism_id = $alias.organism_id");
 
     // Now perform the sort.
-    if ($order['column'] == 'organism.species') {
-      $query->orderBy("$alias.genus", $order['direction']);
-    }
-    if ($order['column'] == 'organism.genus') {
+    if ($order['column'] == $species_term) {
       $query->orderBy("$alias.species", $order['direction']);
     }
-    if ($order['column'] == 'organism.infraspecies') {
+    if ($order['column'] == $genus_term) {
+      $query->orderBy("$alias.genus", $order['direction']);
+    }
+    if ($order['column'] == $infraspecific_name_term) {
       $query->orderBy("$alias.infraspecific_name", $order['direction']);
     }
-    if ($order['column'] == 'organism.infraspecies') {
+    if ($order['column'] == $infraspecific_type_term) {
       if (!in_array('CVT', $joins)) {
-        $query->join('cvterm', 'CVT', "base.type_id = CVT.cvterm_id");
+        $this->queryJoinOnce($query, 'cvterm', 'CVT', "base.type_id = CVT.cvterm_id");
       }
       $query->orderBy("CVT.name", $order['direction']);
     }

+ 5 - 5
tripal_chado/includes/TripalFields/sbo__database_cross_reference/sbo__database_cross_reference.inc

@@ -179,14 +179,14 @@ class sbo__database_cross_reference extends ChadoField {
     $operator = $condition['operator'];
 
     if ($condition['column'] == 'database_cross_reference.database_name') {
-      $query->join($dbxref_linker, $alias, "base.$bpkey = $alias.$bpkey");
-      $query->join('dbxref', 'DBX', "DBX.dbxref_id = $alias.dbxref_id");
-      $query->join('db', 'DB', "DB.db_id = DBX.db_id");
+      $this->queryJoinOnce($query, $dbxref_linker, $alias, "base.$bpkey = $alias.$bpkey");
+      $this->queryJoinOnce($query, 'dbxref', 'DBX', "DBX.dbxref_id = $alias.dbxref_id");
+      $this->queryJoinOnce($query, 'db', 'DB', "DB.db_id = DBX.db_id");
       $query->condition("DB.name", $condition['value'], $operator);
     }
     if ($condition['column'] == 'database_cross_reference.accession') {
-      $query->join($dbxref_linker, $alias, "base.$bpkey = $alias.$bpkey");
-      $query->join('dbxref', 'DBX', "DBX.dbxref_id = $alias.dbxref_id");
+      $this->queryJoinOnce($query, $dbxref_linker, $alias, "base.$bpkey = $alias.$bpkey");
+      $this->queryJoinOnce($query, 'dbxref', 'DBX', "DBX.dbxref_id = $alias.dbxref_id");
       $query->condition("DBX.accession", $condition['value'], $operator);
     }
   }

+ 28 - 21
tripal_chado/includes/TripalFields/sbo__relationship/sbo__relationship.inc

@@ -80,8 +80,8 @@ class sbo__relationship extends ChadoField {
     return array(
       $field_term => array(
         'operations' => array('eq', 'contains', 'starts'),
-        'sortable' => TRUE,
-        'searchable' => TRUE,
+        'sortable' => FALSE,
+        'searchable' => FALSE,
         'type' => 'string',
         'elements' => array(
           'local:relationship_subject' => array(
@@ -93,13 +93,16 @@ class sbo__relationship extends ChadoField {
               'rdfs:type' => array(
                 'searchable' => TRUE,
                 'name' => 'subject_type',
+                'label' => 'Relationship Subject Type',
+                'help' => 'The subject\'s data type in a relationship clause',
                 'operations' => array('eq', 'ne', 'contains', 'starts'),
                 'sortable' => TRUE,
               ),
               'schema:name' => array(
                 'searchable' => TRUE,
                 'name' => 'subject_name',
-                'label' => 'Relationship clause subject name',
+                'label' => 'Relationship Subject Name',
+                'help' => 'The subject\'s name in a relationship clause',
                 'operations' => array('eq', 'ne', 'contains', 'starts'),
                 'sortable' => TRUE,
               ),
@@ -119,17 +122,21 @@ class sbo__relationship extends ChadoField {
             'searchable' => FALSE,
             'name' => 'species',
             'operations' => array('eq', 'ne', 'contains', 'starts'),
-            'sortable' => TRUE,
+            'sortable' => FALSE,
             'elements' => array(
               'rdfs:type' => array(
                 'searchable' => TRUE,
                 'name' => 'object_type',
+                'label' => 'Relationship Object Type',
+                'help' => 'The objects\'s data type in a relationship clause',
                 'operations' => array('eq', 'ne', 'contains', 'starts'),
                 'sortable' => TRUE,
               ),
               'schema:name' => array(
                 'searchable' => TRUE,
                 'name' => 'object_name',
+                'label' => 'Relationship Object Name',
+                'help' => 'The objects\'s name in a relationship clause',
                 'operations' => array('eq', 'ne', 'contains', 'starts'),
                 'sortable' => TRUE,
               ),
@@ -531,47 +538,47 @@ class sbo__relationship extends ChadoField {
 
     // Filter by the name of the subject or object.
     if ($condition['column'] == $rel_subject_name) {
-      $query->join($chado_table, $alias, "base.$bpkey = $alias.object_id");
-      $query->join($base_table, 'base2', "base2.$bpkey = $alias.subject_id");
+      $this->queryJoinOnce($query, $chado_table, $alias, "base.$bpkey = $alias.object_id");
+      $this->queryJoinOnce($query, $base_table, 'base2', "base2.$bpkey = $alias.subject_id");
       $query->condition("base2.name", $condition['value'], $operator);
     }
     if ($condition['column'] == $rel_object_name) {
-      $query->join($chado_table, $alias, "base.$bpkey = $alias.subject_id");
-      $query->join($base_table, 'base2', "base2.$bpkey = $alias.object_id");
+      $this->queryJoinOnce($query, $chado_table, $alias, "base.$bpkey = $alias.subject_id");
+      $this->queryJoinOnce($query, $base_table, 'base2', "base2.$bpkey = $alias.object_id");
       $query->condition("base2.name", $condition['value'], $operator);
     }
 
     // Filter by unique name of the subject or object.
     if ($condition['column'] == $rel_subject_identifier) {
-      $query->join($chado_table, $alias, "base.$bpkey = $alias.object_id");
-      $query->join($base_table, 'base2', "base2.$bpkey = $alias.subject_id");
+      $this->queryJoinOnce($query, $chado_table, $alias, "base.$bpkey = $alias.object_id");
+      $this->queryJoinOnce($query, $base_table, 'base2', "base2.$bpkey = $alias.subject_id");
       $query->condition("base2.uniquename", $condition['value'], $operator);
     }
     if ($condition['column'] == $rel_object_identifier) {
-      $query->join($chado_table, $alias, "base.$bpkey = $alias.subject_id");
-      $query->join($base_table, 'base2', "base2.$bpkey = $alias.object_id");
+      $this->queryJoinOnce($query, $chado_table, $alias, "base.$bpkey = $alias.subject_id");
+      $this->queryJoinOnce($query, $base_table, 'base2', "base2.$bpkey = $alias.object_id");
       $query->condition("base2.uniquename", $condition['value'], $operator);
     }
 
     // Filter by the type of the subject or object
     if ($condition['column'] == $rel_subject_type) {
-      $query->join($chado_table, $alias, "base.$bpkey = $alias.object_id");
-      $query->join($base_table, 'base2', "base2.$bpkey = $alias.subject_id");
-      $query->join('cvterm', 'SubjectCVT', "SubjectCVT.cvterm_id = base2.type_id");
-      $query->condition("SubjectCVT.name", $condition['value'], $operator);
+      $this->queryJoinOnce($query, $chado_table, $alias, "base.$bpkey = $alias.object_id");
+      $this->queryJoinOnce($query, $base_table, 'base2', "base2.$bpkey = $alias.subject_id");
+      $this->queryJoinOnce($query, 'cvterm', 'SubjectCVT', "SubjectCVT.cvterm_id = base2.type_id");
+      $this->queryJoinOnce($query, "SubjectCVT.name", $condition['value'], $operator);
     }
     if ($condition['column'] == $rel_object_type) {
-      $query->join($chado_table, $alias, "base.$bpkey = $alias.subject_id");
-      $query->join($base_table, 'base2', "base2.$bpkey = $alias.object_id");
-      $query->join('cvterm', 'ObjectCVT', "ObjectCVT.cvterm_id = base2.type_id");
+      $this->queryJoinOnce($query, $chado_table, $alias, "base.$bpkey = $alias.subject_id");
+      $this->queryJoinOnce($query, $base_table, 'base2', "base2.$bpkey = $alias.object_id");
+      $this->queryJoinOnce($query, 'cvterm', 'ObjectCVT', "ObjectCVT.cvterm_id = base2.type_id");
       $query->condition("ObjectCVT.name", $condition['value'], $operator);
     }
 
     // Filter by relationship type
     if ($condition['column'] == 'relationship.relationship_type') {
       // This filter commented out because it's way to slow...
-//       $query->join($chado_table, $alias, "base.$bpkey = $alias.subject_id OR base.$bpkey = $alias.object_id");
-//       $query->join('cvterm', 'RelTypeCVT', "RelTypeCVT.cvterm_id = $alias.type_id");
+//       $this->queryJoinOnce($query, $chado_table, $alias, "base.$bpkey = $alias.subject_id OR base.$bpkey = $alias.object_id");
+//       $this->queryJoinOnce($query, 'cvterm', 'RelTypeCVT', "RelTypeCVT.cvterm_id = $alias.type_id");
 //       $query->condition("RelTypeCVT.name", $condition['value'], $operator);
     }
   }

+ 2 - 2
tripal_chado/includes/TripalFields/schema__alternate_name/schema__alternate_name.inc

@@ -115,8 +115,8 @@ class schema__alternate_name extends ChadoField {
     $operator = $condition['operator'];
 
     if ($condition['column'] == 'alternatename') {
-      $query->join($syn_linker, $alias, "base.$bpkey = $alias.$bpkey");
-      $query->join('synonym', 'SYN', "SYN.synonym_id = $alias.synonym_id");
+      $this->queryJoinOnce($query, $syn_linker, $alias, "base.$bpkey = $alias.$bpkey");
+      $this->queryJoinOnce($query, 'synonym', 'SYN', "SYN.synonym_id = $alias.synonym_id");
       $query->condition("SYN.name", $condition['value']);
     }
   }

+ 6 - 6
tripal_chado/includes/TripalFields/schema__publication/schema__publication.inc

@@ -136,17 +136,17 @@ class schema__publication extends ChadoField {
     if ($condition['column'] == 'publication.database_cross_reference') {
       list($db_name, $accession) = explode(':', $condition['value']);
 
-      $query->join($pub_linker, $alias, "base.$bpkey = $alias.$bpkey");
-      $query->join('pub_dbxref', 'PDBX', "PDBX.pub_id = $alias.pub_id");
-      $query->join('dbxref', 'DBX', "DBX.dbxref_id = PDBX.dbxref_id");
-      $query->join('db', 'DB', "DB.db_id = DBX.db_id");
+      $this->queryJoinOnce($query, $pub_linker, $alias, "base.$bpkey = $alias.$bpkey");
+      $this->queryJoinOnce($query, 'pub_dbxref', 'PDBX', "PDBX.pub_id = $alias.pub_id");
+      $this->queryJoinOnce($query, 'dbxref', 'DBX', "DBX.dbxref_id = PDBX.dbxref_id");
+      $this->queryJoinOnce($query, 'db', 'DB', "DB.db_id = DBX.db_id");
       $query->condition("DB.name", $db_name);
       $query->condition("DBX.accession", $accession);
     }
 
     if ($condition['column'] == 'publication.title') {
-      $query->join($pub_linker, $alias, "base.$bpkey = $alias.$bpkey");
-      $query->join('pub', 'PUB', "PUB.pub_id = $alias.pub_id");
+      $this->queryJoinOnce($query, $pub_linker, $alias, "base.$bpkey = $alias.$bpkey");
+      $this->queryJoinOnce($query, 'pub', 'PUB', "PUB.pub_id = $alias.pub_id");
       $query->condition('PUB.title', $condition['value'], $operator);
     }
 

+ 2 - 2
tripal_chado/includes/TripalFields/sio__vocabulary/sio__vocabulary.inc

@@ -92,7 +92,7 @@ class sio__vocabulary extends ChadoField {
     $alias = $this->field['field_name'];
     $operator = $condition['operator'];
 
-    $query->join('cv', $alias, "base.cv_id = $alias.cv_id");
+    $this->queryJoinOnce($query, 'cv', $alias, "base.cv_id = $alias.cv_id");
     $query->condition("$alias.name", $condition['value'], $operator);
   }
 
@@ -105,7 +105,7 @@ class sio__vocabulary extends ChadoField {
     $joins = $query->getTables();
     if (!in_array($this->field['field_name'], $joins)) {
       $alias = $this->field['field_name'];
-      $query->join('cv', $alias, "base.cv_id = $alias.cv_id");
+      $this->queryJoinOnce($query, 'cv', $alias, "base.cv_id = $alias.cv_id");
       $query->orderBy("$alias.name", $order['direction']);
     }
   }