Browse Source

Chado field views integration :-).

Lacey Sanderson 9 years ago
parent
commit
5578d7dec1

+ 177 - 0
tripal_chado/includes/chado_views_handler_field.inc

@@ -0,0 +1,177 @@
+<?php
+/**
+ *
+ */
+class chado_views_handler_field extends views_handler_field {
+
+  /**
+   * Alter the views query to provide information for this field.
+   *
+   * We are going to take the same approach as the field api and simply load
+   * the entities in order to get the values of the chado fields. The hope is
+   * that a small number of cached simple queries will be more efficient than
+   * the crazy joins that occur when using chado. *fingers crossed*
+   */
+  function query ($use_groupby = FALSE) {
+
+    $this->base_table = $this->definition['entity_table'];
+    $this->base_table_alias = $this->base_table;
+    $this->ensure_my_table();
+
+    // Because we are just loading entities, we need the entity id and type only.
+    $this->entity = $entity_info = entity_get_info($this->definition['entity_type']);
+    $alias_stub = 'chado_field_' . $this->definition['chado_table'] . '_' . $this->definition['chado_field'];
+
+    $this->id_alias = $this->field_alias = $this->query->add_field($this->base_table_alias, $entity_info['entity keys']['id'], $alias_stub . '_entity_id');
+    $this->type_alias = $this->query->add_field(NULL, "'" . $this->definition['entity_type'] . "'", $alias_stub . '_entity_type');
+
+  }
+
+  /**
+   * Load the entities for all fields that are about to be displayed.
+   * 
+   * Notice that, although we load the entities for each chado field, 
+   * Drupal caches entities to ensure we don't get a performance hit per field,
+   * just per row.
+   */
+  function post_execute(&$values) {
+    if (!empty($values) AND isset($this->id_alias) AND isset($this->type_alias)) {
+
+      // Foreach row in the view we want to grab the appropriate entity_id/type.
+      $entity_ids = array();
+      $keys = array();
+      foreach ($values as $key => $object) {
+        // Only load the entity if we can access the entity_id.
+        if (isset($this->id_alias) AND isset($object->{$this->id_alias})) {
+          $entity_ids[$object->{$this->type_alias}][] = $object->{$this->id_alias};
+          $keys[$key] = $object->{$this->id_alias};
+        }
+      }
+
+      // Now load the entities.
+      foreach($entity_ids as $type => $ids) {
+        $entities[$type] = entity_load($type, $ids);
+      } 
+  
+      // Finally add the loaded entities and values back into the resultset for easy access.
+      foreach ($keys as $row_id => $entity_id) {
+
+        // First set the entities.
+        foreach($entities as $type => $objects) {
+          $values[$row_id]->_chado_field_data[$type] = array(
+            'entity_type' => $type,
+            'entity' => $objects[$entity_id],
+          );
+        }
+
+        // Then set the value of this field.
+        $values[$row_id]->{$this->field_alias} = $this->render_field($objects[$entity_id], $this->definition['field_name'], $row_id);
+      }
+    }
+  }
+
+  /**
+   * Render the field for display in the view.
+   *
+   * @param TripalEntity $entity
+   *   The entity containing the field to be rendered.
+   * @param string $field_name
+   *   The name of the field to render.
+   * @param integer $row_id
+   *   The id of the row this field will be displayed in.
+   *
+   * @return string
+   *   The rendered field.
+   */
+  function render_field($entity, $field_name, $row_id) {
+
+    $display = array(
+      'type' => $this->options['type'],
+      'settings' => (isset($this->options['settings'])) ? $this->options['settings'] : array(),
+      'label' => 'hidden',
+      // Pass the View object in the display so that fields can act on it.
+      'views_view' => $this->view,
+      'views_field' => $this,
+      'views_row_id' => $row_id,
+    );
+
+    $langcode = LANGUAGE_NONE;
+    $items = field_get_items($entity->type, $entity, $field_name);
+    if (count($items) == 1) {
+      $render_array = field_view_value($entity->type, $entity, $field_name, $items[0], $display, $langcode);
+    }
+    // @todo: handle fields with multiple values.
+    else {
+      $render_array = field_view_value($entity->type, $entity, $field_name, $items[0], $display, $langcode);
+      drupal_set_message('Tripal Chado currently only supports views integration for single value fields. The first value has been shown.', 'warning');
+    }
+
+    return drupal_render($render_array);
+  }
+
+  /**
+   * Provide options for views ui admin specific to fields.
+   */
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+
+    $field = field_info_field($this->definition['field_name']);
+    $formatters = _field_view_formatter_options($field['type']);
+    
+    $form['type'] = array(
+      '#type' => 'select',
+      '#title' => t('Formatter'),
+      '#options' => $formatters,
+      '#default_value' => $this->options['type'],
+      '#ajax' => array(
+        'path' => views_ui_build_form_url($form_state),
+      ),
+      '#submit' => array('views_ui_config_item_form_submit_temporary'),
+      '#executes_submit_callback' => TRUE,
+    );
+
+    // Get the currently selected formatter.
+    $format = $this->options['type'];
+
+    $formatter = field_info_formatter_types($format);
+    if (!isset($this->options['settings'])) $this->options['settings'] = array();
+    $settings = $this->options['settings'] + field_info_formatter_settings($format);
+
+    // Provide an instance array for hook_field_formatter_settings_form().
+    ctools_include('fields');
+    $this->instance = ctools_fields_fake_field_instance($this->definition['field_name'], '_custom', $formatter, $settings);
+
+    // Store the settings in a '_custom' view mode.
+    $this->instance['display']['_custom'] = array(
+      'type' => $format,
+      'settings' => $settings,
+    );
+
+    // Get the settings form.
+    $settings_form = array('#value' => array());
+    $function = $formatter['module'] . '_field_formatter_settings_form';
+    if (function_exists($function)) {
+      $settings_form = $function($field, $this->instance, '_custom', $form, $form_state);
+    }
+    $form['settings'] = $settings_form;
+  }
+
+  /**
+   * Define the options we are going to provide.
+   */
+  function option_definition() {
+    $options = parent::option_definition();
+
+    $field = field_info_field($this->definition['field_name']);
+    $field_type = field_info_field_types($field['type']);
+
+    $options['type'] = array(
+      'default' => $field_type['default_formatter'],
+    );
+    $options['settings'] = array(
+      'default' => array(),
+    );
+
+    return $options;
+  }
+}

+ 1 - 0
tripal_chado/tripal_chado.info

@@ -5,6 +5,7 @@ project = tripal
 package = Tripal
 version = 7.x-2.0
 
+files[] = includes/chado_views_handler_field.inc
 stylesheets[all][] = theme/css/tripal_chado.css
 
 dependencies[] = tripal

+ 1 - 1
tripal_chado/tripal_chado.module

@@ -25,6 +25,7 @@ require_once "includes/tripal_chado.entity.inc";
 require_once "includes/tripal_chado.schema.inc";
 require_once "includes/tripal_chado.term_storage.inc";
 require_once "includes/tripal_chado.field_storage.inc";
+require_once "includes/chado_views_handler_field.inc";
 
 tripal_chado_set_globals();
 
@@ -822,7 +823,6 @@ function tripal_chado_set_field_form_values($field_name, &$form_state, $newvalue
   return TRUE;
 }
 
-
 /**
  * Implements hook_theme().
  */

+ 62 - 0
tripal_chado/tripal_chado.views.inc

@@ -22,6 +22,68 @@ function tripal_chado_views_data() {
   return $data;
 }
 
+/**
+ * Implements hook_views_data_alter().
+ */
+function tripal_chado_views_data_alter(&$data) {
+
+  // Adds integration for chado-based fields.
+  tripal_chado_add_field_views_data($data);
+
+  return $data;
+}
+
+/** 
+ * Adds integration for chado-based fields.
+ *
+ * We can't use hook_field_view_data since this only works when the
+ * storage engine is of type 'field_sql_storage' and of course,
+ * ours is not. Thus we create our own implementation of field_views_data()
+ * for our storage engine.
+ */
+function tripal_chado_add_field_views_data(&$data) {
+
+  foreach (field_info_fields() as $field) {
+    if ($field['storage']['type'] != 'field_chado_storage') {
+      continue;
+    }
+
+    // Currently, we only handle integration of chado fields with TripalEntity.
+    // @todo: extend this to work with other entities in the future.
+    if (isset($field['bundles']['TripalEntity']) AND isset($field['settings']['chado_column'])) {
+
+      // We currently don't support prop tables for views integration due
+      // in part to the multiple values but also b/c we can't indicate which
+      // type of property to show. Thus, instead of warning the user,
+      // we just won't integrate it at this time.
+      // @todo: Handle property fields.
+      if (preg_match('/prop$/', $field['settings']['chado_table'])) {
+        continue;
+      }
+
+      // Unfortunatly we can't use the field label since that is set at the 
+      // instance level and fields are integrated at the field level (independant of bundle).
+      // Thus we will simply make the most readable and informative field name we can.
+      $data['tripal_entity'][ $field['field_name'] ]['title'] = ucfirst(str_replace('_',' ',$field['settings']['chado_table']))
+        . ' ' .ucfirst(str_replace('_',' ',$field['settings']['chado_column']));
+
+      // The help should be 'Appears in: TripalEntity: bio-data_1, bio-data_2'
+      // so that users know where they can use it.
+      $data['tripal_entity'][ $field['field_name'] ]['help'] = 'Appears in: TripalEntity:' . implode(',', $field['bundles']['TripalEntity']);
+
+      // Finally we define the field.
+      $data['tripal_entity'][ $field['field_name'] ]['field']['chado_field'] = $field['settings']['chado_column'];
+      $data['tripal_entity'][ $field['field_name'] ]['field']['chado_table'] = $field['settings']['chado_table'];
+      $data['tripal_entity'][ $field['field_name'] ]['field']['field_name'] = $field['field_name'];
+      $data['tripal_entity'][ $field['field_name'] ]['field']['entity_table'] = 'tripal_entity';
+      $data['tripal_entity'][ $field['field_name'] ]['field']['entity_type'] = 'TripalEntity';
+      $data['tripal_entity'][ $field['field_name'] ]['field']['bundles'] = $field['bundles']['TripalEntity'];
+      $data['tripal_entity'][ $field['field_name'] ]['field']['handler'] = 'chado_views_handler_field';
+      $data['tripal_entity'][ $field['field_name'] ]['field']['click sortable'] = FALSE;
+    }    
+  }
+}
+
 /**
  * Provides the data array for the tripal custom tables management
  *