فهرست منبع

Views with filters is now functional. Display of field values uses the field formatter

Stephen Ficklin 7 سال پیش
والد
کامیت
18ed477f65

+ 6 - 0
tripal/includes/TripalFieldQuery.inc

@@ -34,6 +34,7 @@ class TripalFieldQuery extends EntityFieldQuery {
       foreach ($this->field_storage as $storage_type => $storage_module) {
         // Execute the query using the correct callback.
         $callback = $this->queryStorageCallback($storage_module);
+
         $st_results = call_user_func($callback, $this);
         // If this is the first storage type to be queries then save these
         // as the current results list.
@@ -60,7 +61,11 @@ class TripalFieldQuery extends EntityFieldQuery {
     else {
       $results = call_user_func($this->queryCallback(), $this);
     }
+
     if ($results and $this->count) {
+      if (!is_numeric($results)) {
+        throw new Exception('Query callback function did not provide a numeric value: ' . $this->queryCallback());
+      }
       return $results;
     }
     else {
@@ -95,6 +100,7 @@ class TripalFieldQuery extends EntityFieldQuery {
     if (empty($this->fields)) {
       return array($this, 'propertyQuery');
     }
+
     if ($storage) {
       // Use hook_field_storage_query() from the field storage.
       return $storage . '_field_storage_query';

+ 34 - 10
tripal/includes/TripalFields/TripalField.inc

@@ -79,6 +79,28 @@ class TripalField {
   // the user but otherwise provides no data.
   public static $no_data = 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',
+    ),
+  );
+
 
   // --------------------------------------------------------------------------
   //              PROTECTED CLASS MEMBERS -- DO NOT OVERRIDE
@@ -281,6 +303,8 @@ class TripalField {
   public function viewsData() {
     $data = array();
 
+    $class = get_called_class();
+
     $bundle_name = $this->instance['bundle'];
     $field_name = $this->field['field_name'];
 
@@ -290,20 +314,20 @@ class TripalField {
     $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
     $table = $term->vocab->vocabulary . '__' . $term->accession;
 
+    $handlers = $class::$default_view_handlers;
     $data[$table][$field_name] = array(
       'title' => $this->instance['label'],
       'help' => $this->instance['description'],
-      'field' => array(
-        'handler' => 'tripal_views_handler_field',
-        'click sortable' => TRUE,
-      ),
-      'filter' => array(
-        'handler' => 'tripal_views_handler_filter_string',
-      ),
-      'sort' => array(
-        'handler' => 'views_handler_sort',
-      ),
     );
+    if (array_key_exists('field', $handlers)) {
+      $data[$table][$field_name]['field'] = $handlers['field'];
+    }
+    if (array_key_exists('sort', $handlers)) {
+      $data[$table][$field_name]['sort'] = $handlers['sort'];
+    }
+    if (array_key_exists('filter', $handlers)) {
+      $data[$table][$field_name]['filter'] = $handlers['filter'];
+    }
 
     return $data;
   }

+ 1 - 1
tripal/tripal.info

@@ -18,7 +18,7 @@ files[] = views_handlers/tripal_views_handler_field_image.inc
 files[] = views_handlers/tripal_views_handler_field_boolean.inc
 files[] = views_handlers/tripal_views_handler_filter.inc
 files[] = views_handlers/tripal_views_handler_filter_string.inc
-files[] = views_handlers/tripal_views_handler_filter_boolean.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_entity_string.inc

+ 1 - 1
tripal/tripal.views.inc

@@ -165,7 +165,7 @@ function tripal_views_data_tripal_entity(&$data) {
         ),
       ),
       'filter' => array(
-        'handler' => 'views_handler_filter_boolean_operator',
+        'handler' => 'tripal_views_handler_filter_boolean_operator',
         'label' => t('Published'),
         'type' => 'yes-no',
         'use equal' => TRUE, // Use status = 1 instead of status <> 0 in WHERE statment

+ 134 - 64
tripal/tripal_views_query.inc

@@ -38,10 +38,8 @@ class tripal_views_query extends views_plugin_query {
   /**
    *
    */
-  public function init($base_table = 'tripal_entity', $base_field = 'id', $options) {
-
+  public function init($base_table = 'tripal_entity', $base_field = 'id', $options) {;
     parent::init($base_table, $base_field, $options);
-
     $this->fields = array();
     $this->where = array();
 
@@ -91,91 +89,163 @@ class tripal_views_query extends views_plugin_query {
    *   here.
    */
   public function add_where($group, $field_name, $value = NULL, $operator = NULL) {
-    // Remove the preceeding period from the $field_name
-    $field_name = preg_replace('/^\./', '', $field_name);
-
     $this->filters[] = array(
       'group' => $group,
       'field_name' => $field_name,
       'value' => $value,
       'op' => $operator
     );
+
     if ($value) {
-      $this->query->fieldCondition($field_name, $value, $value, $op);
+      // Handle the bundle properties separate from real fields.
+      if ($field_name == 'entity_id' or $field_name == 'status') {
+        $this->query->propertyCondition($field_name, $value, $operator);
+      }
+      else if ($field_name == 'link' or $field_name == 'edit_link' or $field_name == 'delete_link') {
+        // TODO: not sure how to handle these just yet.
+      }
+      else {
+        // Unforutnately, we don't know the column for this field, so leave it
+        // as NULL and the let the field_storage implementation handle it.
+        $this->query->fieldCondition($field_name, $field_name, $value, $operator);
+      }
     }
   }
+  /**
+   *
+   */
+  function build(&$view) {
+    // Make the query distinct if the option was set.
+    if (!empty($this->options['distinct'])) {
+      $this->set_distinct(TRUE, !empty($this->options['pure_distinct']));
+    }
+
+    // Store the view in the object to be able to use it later.
+    $this->view = $view;
+
+    $view->init_pager();
+
+    // 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();
+  }
   /**
    *
    * @param  $view
    */
   function execute(&$view) {
-    $query = $this->query;
-
-    $start = microtime(TRUE);
-
-    // Execute the count query
-    // TODO: support paging.
-    $cquery = clone $query;
-    $cquery->count();
-    //$views->total_rows = $cquery->execute();
-
-    // Get the IDs
-    $results = $query->execute();
-    $entity_ids = array_keys($results['TripalEntity']);
-
-
-    // Get the fields to attach to the entity
-    $fields = array();
-    $field_ids = array();
-    foreach ($this->fields as $details) {
-      $field_name = $details['field_name'];
-      $field = field_info_field($field_name);
-      if ($field) {
-        $fields[$field_name] = $field;
-        $field_ids[] = $field['id'];
-      }
-    }
-
-    $entities = tripal_load_entity('TripalEntity', $entity_ids, FALSE, $field_ids);
-    $i = 0;
-    foreach ($entities as $entity_id => $entity) {
-      $view->result[$i] = new stdClass();
-      foreach ($this->fields as $details) {
-        $field_name = $details['field_name'];
-        // The entity_id and link fields are not true fields. They are
-        // added by the tripal_views_data_tripal_entity() function to provide
-        // useful fields to reference entities. If we see these
-        // we need to support them here by giving them values.
-        if ($field_name == 'entity_id') {
-          $view->result[$i]->$field_name = $entity;
-          continue;
+    $query = $view->build_info['query'];
+    $count_query = $view->build_info['count_query'];
+
+    if ($query) {
+      $start = microtime(TRUE);
+
+      try {
+
+        if ($this->pager->use_count_query() || !empty($view->get_total_rows)) {
+          // TODO: The code below was taken from the
+          // views_plugin_pager::execute_count_query($count_query) which would
+          // be called here, but that function expects the query is a
+          // database query rather than a TripalEntityField query.  We
+          // 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();
+          if (!empty($this->pager->options['offset'])) {
+            $this->pager->total_items -= $this->pager->options['offset'];
+          };
+          $this->pager->update_page_info();
         }
-        if ($field_name == 'link') {
-          $view->result[$i]->$field_name = $entity;
-          continue;
+
+        // TODO: we need to implement a new views_plugin_pager class to
+        // override the pre_execute to set the range, instead we'll just do
+        // it manully here until we have the class.
+        $this->pager->pre_execute($query);
+        $num_items_per_page = $this->pager->get_items_per_page();
+        $offset = $this->pager->get_current_page() * $num_items_per_page;
+        $query->range($offset, $num_items_per_page);
+
+        // 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();
         }
-        if ($field_name == 'edit_link') {
-          $view->result[$i]->$field_name = $entity;
-          continue;
+
+        // Get the fields to attach to the entity
+        $fields = array();
+        $field_ids = array();
+        foreach ($this->fields as $details) {
+          $field_name = $details['field_name'];
+          $field = field_info_field($field_name);
+          if ($field) {
+            $fields[$field_name] = $field;
+            $field_ids[] = $field['id'];
+          }
         }
-        if ($field_name == 'delete_link') {
-          $view->result[$i]->$field_name = $entity;
-          continue;
+
+        $entities = tripal_load_entity('TripalEntity', $entity_ids, FALSE, $field_ids);
+        $i = 0;
+        foreach ($entities as $entity_id => $entity) {
+          $view->result[$i] = new stdClass();
+          foreach ($this->fields as $details) {
+            $field_name = $details['field_name'];
+            // The entity_id and link fields are not true fields. They are
+            // added by the tripal_views_data_tripal_entity() function to provide
+            // useful fields to reference entities. If we see these
+            // we need to support them here by giving them values.
+            if ($field_name == 'entity_id') {
+              $view->result[$i]->$field_name = $entity;
+              continue;
+            }
+            if ($field_name == 'link') {
+              $view->result[$i]->$field_name = $entity;
+              continue;
+            }
+            if ($field_name == 'edit_link') {
+              $view->result[$i]->$field_name = $entity;
+              continue;
+            }
+            if ($field_name == 'delete_link') {
+              $view->result[$i]->$field_name = $entity;
+              continue;
+            }
+            if ($field_name == 'status') {
+              $view->result[$i]->$field_name = $entity->status;
+              continue;
+            }
+            if (array_key_exists($field_name, $fields)) {
+              $items = field_get_items('TripalEntity', $entity, $field_name);
+              $view->result[$i]->$field_name = $items;
+            }
+          }
+          // Always add the entity to the results so that handlers
+          // can take advantage of it.
+          $view->result[$i]->entity = $entity;
+          $i++;
         }
-        if ($field_name == 'status') {
-          $view->result[$i]->$field_name = $entity->status;
-          continue;
+      }
+      catch (Exception $e) {
+        $view->result = array();
+        if (!empty($view->live_preview)) {
+          drupal_set_message($e->getMessage(), 'error');
         }
-        if (array_key_exists($field_name, $fields)) {
-          $items = field_get_items('TripalEntity', $entity, $field_name);
-          $view->result[$i]->$field_name = $items;
+        else {
+          vpr('Exception in @human_name[@view_name]: @message', array('@human_name' => $view->human_name, '@view_name' => $view->name, '@message' => $e->getMessage()));
         }
       }
-      $i++;
+    }
+    else {
+      $start = microtime(TRUE);
     }
     $view->execute_time = microtime(TRUE) - $start;
-    $view->current_page = 0;
   }
 
 }

+ 13 - 3
tripal/views_handlers/tripal_views_handler_field.inc

@@ -59,10 +59,20 @@ class tripal_views_handler_field extends views_handler_field {
    */
   function render($values) {
 
-    $value = $this->get_value($values);
+    list ($vocabulary, $accession) = explode('__', $this->table_alias);
+    $term = tripal_load_term_entity(array('vocabulary' => $vocabulary, 'accession' => $accession));
+    $bundle = tripal_load_bundle_entity(array('term_id' => $term->id));
+    $field_name = $this->field_alias;
+    $field = field_info_field($field_name);
+    $instance = field_info_instance('TripalEntity', $field_name, $bundle->name);
+    $entity = $values->entity;
+
+    $items = $this->get_value($values);
+
     // Handle single value fields:
-    if (count($value == 1)) {
-      $value = $this->sanitize_value($value[0]['value'], 'xss');
+    if (count($items == 1)) {
+      $function = $field['module'] . '_field_formatter_view';
+      $value = $function('TripalEntity', $entity, $field, $instance, 'und', $items, $instance['display']['default']);
       return $value;
     }
     return '';

+ 6 - 6
tripal/views_handlers/tripal_views_handler_filter.inc

@@ -4,14 +4,14 @@ class tripal_views_handler_filter extends views_handler_filter {
 
   /**
    * Add this filter to the query.
-   *
-   * Due to the nature of fapi, the value and the operator have an unintended
-   * level of indirection. You will find them in $this->operator
-   * and $this->value respectively.
    */
   function query() {
     $this->ensure_my_table();
-    $this->query->add_where($this->options['group'], $this->real_field, $this->value, $this->operator);
+    $this->query->add_where(
+      $this->options['group'],
+      $this->real_field,
+      $this->value,
+      $this->operator
+    );
   }
-
 }

+ 0 - 10
tripal/views_handlers/tripal_views_handler_filter_boolean.inc → tripal/views_handlers/tripal_views_handler_filter_boolean_operator.inc

@@ -129,14 +129,4 @@ class tripal_views_handler_filter_boolean_operator extends tripal_views_handler_
     $this->options['expose']['label'] = $this->value_value;
     $this->options['expose']['required'] = TRUE;
   }
-
-  function query() {
-//     $this->ensure_my_table();
-//     $field = $this->real_field;
-
-//     $info = $this->operators();
-//     if (!empty($info[$this->operator]['method'])) {
-//       $this->{$info[$this->operator]['method']}($field);
-//     }
-  }
 }

+ 1 - 1
tripal/views_handlers/tripal_views_handler_filter_string.inc

@@ -238,7 +238,7 @@ class tripal_views_handler_filter_string extends tripal_views_handler_filter {
   }
 
   function op_equal($field) {
-    $this->query->add_where($this->options['group'], $field, $this->value, $this->operator());
+    $this->query->add_where($this->options['group'], $field, $this->value, $this->operator);
   }
 
   function op_contains($field) {

+ 15 - 9
tripal_chado/api/tripal_chado.semweb.api.inc

@@ -199,7 +199,7 @@ function tripal_get_chado_semweb_term($chado_table, $chado_column, $options = ar
  *   The name of the Chado table.
  * @param $term
  *   The term. This can be a term name or a unique identifer of the form
- *   {db}:{accession}
+ *   {db}:{accession} or of the form {db}__{term_name}.
  *
  * @return
  *   The name of the Chado column that matches the given term or FALSE if the
@@ -212,23 +212,29 @@ function tripal_get_chado_semweb_column($chado_table, $term) {
     ->execute();
   while($column = $columns->fetchObject()) {
     $cvterm_id = $column->cvterm_id;
+
     if ($cvterm_id) {
+
       $cvterm = chado_generate_var('cvterm', array('cvterm_id' => $cvterm_id));
+
+      $full_accession = strtolower($cvterm->dbxref_id->db_id->name . ':' . $cvterm->dbxref_id->accession);
+      $full_accession = preg_replace('/ /', '_', $full_accession);
+      $full_name_uscore = strtolower($cvterm->dbxref_id->db_id->name . '__' . $cvterm->name);
+      $full_name_uscore = preg_replace('/ /', '_', $full_name_uscore);
+
+      $term = preg_replace('/ /', '_', $term);
+      $term = strtolower(preg_replace('/ /', '_', $term));
+
       // Does the term match identically?
       if ($term == $cvterm->name) {
         return $column->chado_column;
       }
       // Is the term a concatenation of the vocab and the accession?
-      else if ($term == $cvterm->dbxref_id->db_id->name . ':' . $cvterm->dbxref_id->accession) {
-        return $column->chado_column;
-      }
-      // Does the term have spaces that are renamed to underscores?
-      else if ($term == preg_replace('/ /', '_', $cvterm->name)) {
+      else if ($term == $full_accession) {
         return $column->chado_column;
       }
-      // Does the term have spaces that are renamed to underscores and
-      // all lowercase.
-      else if (strtolower($term) == strtolower(preg_replace('/ /', '_', $cvterm->name))) {
+      // Is the term a concatenation of the vocab and the accession?
+      else if ($term == $full_name_uscore) {
         return $column->chado_column;
       }
     }

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

@@ -44,6 +44,16 @@ class ChadoField extends TripalField {
     'base_table' => '',
   );
 
+  // By default our Chado fields are complicated and we only want to support
+  // viewing them, and not filtering by them.  Each field cand override as
+  // needed.
+  public static $default_view_handlers = array(
+    'field' => array(
+      'handler' => 'tripal_views_handler_field',
+      'click sortable' => TRUE,
+    ),
+  );
+
   // The module that manages this field.
   public static $module = 'tripal_chado';
 

+ 21 - 0
tripal_chado/includes/TripalFields/data__accession/data__accession.inc

@@ -37,6 +37,27 @@ class data__accession extends ChadoField {
     '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';
 

+ 28 - 0
tripal_chado/includes/TripalFields/obi__organism/obi__organism.inc

@@ -30,6 +30,28 @@ class obi__organism extends ChadoField {
     'field_display_string' => '<i>[organism.genus] [organism.species]</i>',
   );
 
+  // 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 = 'OBI__organism_widget';
 
@@ -196,6 +218,12 @@ class obi__organism extends ChadoField {
 
     $query->join('organism', $alias, "base.organism_id = $alias.organism_id");
 
+    // If the column is the field name.
+    if ($condition['column'] == 'obi__organism') {
+      $query->where("CONCAT($alias.genus, ' ', $alias.species) $operator :full_name",  array(':full_name' => $condition['value']));
+    }
+
+    // If the column is a subfield.
     if ($condition['column'] == 'organism.species') {
       $query->condition("$alias.species", $condition['value'], $operator);
     }

+ 0 - 5
tripal_chado/includes/TripalFields/obi__organism/obi__organism_formatter.inc

@@ -14,11 +14,6 @@ class obi__organism_formatter extends ChadoFieldFormatter {
   public function view(&$element, $entity_type, $entity, $langcode, $items, $display) {
     if (count($items) > 0) {
       $content = $items[0]['value']['rdfs:label'];
-      $entity = isset($item['value']['entity']) ? $item['value']['entity'] : '';
-      if ($entity) {
-        list($entity_type, $entity_id) = explode(':', $entity);
-        $content = l(content, 'bio_data/' . $entity_id);
-      }
       if (array_key_exists('entity', $items[0]['value'])) {
         list($entity_type, $entity_id) = explode(':', $items[0]['value']['entity']);
         $content = l(strip_tags($items[0]['value']['rdfs:label']), 'bio_data/' . $entity_id);

+ 8 - 3
tripal_chado/includes/tripal_chado.field_storage.inc

@@ -477,7 +477,6 @@ function tripal_chado_field_storage_query($query) {
   $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));
@@ -620,11 +619,17 @@ function tripal_chado_field_storage_query($query) {
     } // end if ($sort['type'] == 'field') {
   } // end foreach ($query->order as $index => $sort) {
 
-  //print_r($cquery->__toString());
-  //print_r($cquery->getArguments());
+  //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);