浏览代码

Joins on relationships with the tripal_entity table now work

Stephen Ficklin 6 年之前
父节点
当前提交
6070e5b156
共有 3 个文件被更改,包括 131 次插入44 次删除
  1. 103 6
      tripal/includes/TripalFieldQuery.inc
  2. 19 2
      tripal/includes/tripal.field_storage.inc
  3. 9 36
      tripal/tripal_views_query.inc

+ 103 - 6
tripal/includes/TripalFieldQuery.inc

@@ -11,14 +11,57 @@ class TripalFieldQuery extends EntityFieldQuery {
    */
   protected $includes = array();
 
+  /**
+   * These member variables keep track of join relationships with the
+   * tripal_entity table. This is useful for non Tripal Storage API fields that
+   * want to filter based on on other Drupal tables. The most important 
+   * example for this would be Drupal Views.  These variables are only meant
+   * to be used by the tripal_field_storage_query() function as that is the 
+   * only storage system that should be doing quries on the tripal_entity
+   * table itself.
+   */
+  public $relationships = [];
+  public $relationshipConditions = [];
 
   /**
-   * This function is for views integration.
-   *
-   *  
+   * Adds a relationsihp via a table join to the tripal_entity table
+   * 
+   * This is specific for Drupal schema tables and is useful for Views 
+   * integration when non Tripal Storage API fields are attached to an entity.
+   * 
+   * @param $table
+   *   The name of the table to join.
+   * @param $alias
+   *   The alias for the table.
+   * @param $field
+   *   The field to join on.
    */ 
-  public function entityRelationJoin($base_table, $base_field, $table, $field, $value, $op) {
+  public function addRelationship($table, $alias, $field) {    
+    $this->relationships[$alias] = [
+      'table' => $table,
+      'field' => $field,
+    ];
+  }
+  /**
+   * Adds a where statement to a relationship.
+   * 
+   * The relationship is added by the $table
+   */
+  public function relationshipCondition($table, $field, $value, $op) {
 
+    $table_alias = '';
+    // Get the alias for this table.
+    foreach ($this->relationships as $alias => $details) {
+      if ($details['table'] == $table) {
+        $table_alias = $alias;
+      }
+    }
+    
+    $this->relationshipConditions[$table_alias] = [
+      'field' => $field,
+      'value' => $value,
+      'op' => $op,      
+    ];
   }
 
   /**
@@ -74,12 +117,14 @@ class TripalFieldQuery extends EntityFieldQuery {
     // If there are no fields then default to the original
     // EntityFieldQuery() functionality.
     else {
-      $results = call_user_func($this->queryCallback(), $this);
+      $callback = $this->queryCallback();
+      $results = call_user_func($callback, $this);
     }
 
+    // If this is a count query then it should return a numeric value.
     if ($results and $this->count) {
       if (!is_numeric($results)) {
-        throw new Exception('Query callback function did not provide a numeric value: ' . $this->queryCallback());
+        throw new Exception('Query callback function did not provide a numeric count value: ' . $this->queryCallback());
       }
       return $results;
     }
@@ -87,6 +132,50 @@ class TripalFieldQuery extends EntityFieldQuery {
       return $results;
     }
   }
+  
+  /**
+   * Overides the EntityFieldQuery::queryCallback function.
+   */
+  public function queryCallback() {
+    
+    // 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)) {
+      return $this->executeCallback;
+    }
+    
+    // If we have relationships then we need to join on the tripal_entity
+    // table and that always occurs using the tripal_field_storage_query.
+    if (!empty($this->relationships)) {
+      return 'tripal_field_storage_query';
+    }
+    // If there are no field conditions and sorts, and no execute callback
+    // then we default to querying entity tables in SQL.
+    if (empty($this->fields)) {
+      return array(
+        $this,
+        'propertyQuery',
+      );
+    }
+    
+    // If no override, find the storage engine to be used.
+    foreach ($this->fields as $field) {
+      if (!isset($storage)) {
+        $storage = $field['storage']['module'];
+      }
+      elseif ($storage != $field['storage']['module']) {
+        throw new EntityFieldQueryException(t("Can't handle more than one field storage engine"));
+      }
+    }
+    if ($storage) {
+      
+      // Use hook_field_storage_query() from the field storage.
+      return $storage . '_field_storage_query';
+    }
+    else {
+      throw new EntityFieldQueryException(t("Field storage engine not found."));
+    }
+  }
 
   /**
    * Determines the query callback to use for this entity query.
@@ -104,11 +193,19 @@ class TripalFieldQuery extends EntityFieldQuery {
    *
    */
   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)) {
       return $this->executeCallback;
     }
+    
+    // If we have relationships then we need to join on the tripal_entity
+    // table and that always occurs using the tripal_field_storage_query.
+    if (!empty($this->relationships)) {
+      return 'tripal_field_storage_query';
+    }
+    
     // If there are no field conditions and sorts, and no execute callback
     // then we default to querying entity tables in SQL.
     if (empty($this->fields)) {

+ 19 - 2
tripal/includes/tripal.field_storage.inc

@@ -86,6 +86,18 @@ function tripal_field_storage_query($query) {
     }
   }
 
+  if ($query->relationshipConditions) {
+    foreach ($query->relationshipConditions as $table_alias => $reldetails) {
+      $field = $reldetails['field'];
+      $value = $reldetails['value'];
+      $op = $reldetails['op'];        
+      $relationship = $query->relationships[$table_alias];
+      $table = $relationship['table'];
+      $select->join($table, $table_alias, 'TE.id = ' . $table_alias . '.' . $relationship['field']);
+      $select->condition($table_alias . '.' . $field, $value, $op);
+    }
+  }
+
   // Add in any filters to the query.
   foreach ($query->fieldConditions as $index => $condition) {
     $field = $condition['field'];
@@ -119,14 +131,14 @@ function tripal_field_storage_query($query) {
   }
   
   // Add a range of records to retrieve
-  if (isset($query->range)) {
+  if ($query->range) {
     $select->range($query->range['start'], $query->range['length']);
   }
   
   // Only include records that are deleted.  Tripal doesn't keep track of
   // records that are deleted that need purging separately so we can do nothing
   // with this.
-  if (isset($query->deleted)) {
+  if (property_exists($query, 'deleted') and $query->deleted) {
     // There won't ever be field data marked as deleted so just created a 
     // condition that always evaluates to false.
     $select->where('1=0');
@@ -134,6 +146,11 @@ function tripal_field_storage_query($query) {
 
   // Perform the query and return the results.
   $entities = $select->execute();
+  
+  if ($query->count) {
+    return $entities->rowCount();
+  }
+  
   $result = array(
     'TripalEntity' => array(),
   );

+ 9 - 36
tripal/tripal_views_query.inc

@@ -145,10 +145,13 @@ class tripal_views_query extends views_plugin_query {
         $this->cquery->fieldCondition($field_name, $element_name, $value, $operator);
       }
       else {
-        // If we have a table name we need to find the field for it.
-        $field_name = $this->_get_table_field($field_name);
+        // If we have a table name then this table is in the Drupal schema and
+        // we need to add a relationship with the tripal_entity table.
+        $table = $field_name;
+        $field = $element_name;
+        $this->query->relationshipCondition($table, $field, $value, $operator);
+        $this->cquery->relationshipCondition($table, $field, $value, $operator);
       }
-
     }
   }
  
@@ -396,14 +399,6 @@ class tripal_views_query extends views_plugin_query {
    * This function copied from views_plugin_query_default::add_relationship
    */ 
   public function add_relationship($alias, $join, $base, $link_point = NULL) {
-dpm($alias);
-dpm($join);
-    if (empty($link_point)) {
-      $link_point = $this->base_table;
-    }
-    elseif (!array_key_exists($link_point, $this->relationships)) {
-      return FALSE;
-    }
   
     // Make sure $alias isn't already used; if it, start adding stuff.
     $alias_base = $alias;
@@ -411,31 +406,9 @@ dpm($join);
     while (!empty($this->relationships[$alias])) {
       $alias = $alias_base . '_' . $count++;
     }
-  
-    // Make sure this join is adjusted for our relationship.
-    if ($link_point && isset($this->relationships[$link_point])) {
-      $join = $this
-        ->adjust_join($join, $link_point);
-    }
-  
-    // Add the table directly to the queue to avoid accidentally marking it.
-    $this->table_queue[$alias] = array(
-      'table' => $join->table,
-      'num' => 1,
-      'alias' => $alias,
-      'join' => $join,
-      'relationship' => $link_point,
-    );
-    $this->relationships[$alias] = array(
-      'link' => $link_point,
-      'table' => $join->table,
-      'base' => $base,
-    );
-    $this->tables[$this->base_table][$alias] = array(
-      'count' => 1,
-      'alias' => $alias,
-    );
+      
+    $this->query->addRelationship($join->table, $alias, $join->field);
+    $this->cquery->addRelationship($join->table, $alias, $join->field);
     return $alias;
   }
-
 }