Browse Source

Issue #2343599: Multiple relationships between same tables were only available in one direction, now available from both directions.

Lacey Sanderson 10 years ago
parent
commit
9bcd54fcdf

+ 129 - 77
tripal_views/api/tripal_views.api.inc

@@ -150,10 +150,21 @@ function mymodule_admin_landing_page() {
               'filter' => array( ... ),
               ...
             ),
-            'join' => array( //describe a table that joins to this one via this field
-              'table' => 'featureprop', //table to join to
-              'field' => 'feature_id', //field in above table (featureprop)
-              'handler' => 'views_handler_join_chado_aggregator', //handler to use
+            // Describe any joins involving this field.
+            // Note: you can include both foreign keys (feature.type_id => cvterm.cvterm_id)
+            // and referring tables (ie: feature.feature_id <= feature_relationship.subject_id)
+            'joins' => array(
+              'feature_relationship' => array( //table to join to.
+                'subject_id' => array( //field in above table (feature_relationship)
+                  'table' => 'featureprop', //table to join to
+                  'field' => 'feature_id', //field in above table (feature_relationship)
+                  'handler' => 'views_join', //handler to use for joining
+                  'relationship_handler' => 'views_handler_relationship', //handler to use when a relationship is added.
+                  'relationship_only' => FALSE, //whether to join automatically (FALSE) or not (TRUE)
+                ),
+                ...
+              ),
+              ...
             ),
           )
         ),
@@ -591,10 +602,21 @@ function tripal_rebuild_views_integrations($delete_first = FALSE) {
           'filter' => array( ... ),
           ...
         ),
-        'join' => array( //describe a table that joins to this one via this field
-          'table' => 'featureprop', //table to join to
-          'field' => 'feature_id', //field in above table (featureprop)
-          'handler' => 'views_handler_join_chado_aggregator', //handler to use
+        // Describe any joins involving this field.
+        // Note: you can include both foreign keys (feature.type_id => cvterm.cvterm_id)
+        // and referring tables (ie: feature.feature_id <= feature_relationship.subject_id)
+        'joins' => array(
+          'feature_relationship' => array( //table to join to.
+            'subject_id' => array( //field in above table (feature_relationship)
+              'table' => 'featureprop', //table to join to
+              'field' => 'feature_id', //field in above table (feature_relationship)
+              'handler' => 'views_join', //handler to use for joining
+              'relationship_handler' => 'views_handler_relationship', //handler to use when a relationship is added.
+              'relationship_only' => FALSE, //whether to join automatically (FALSE) or not (TRUE)
+            ),
+            ...
+          ),
+          ...
         ),
       )
     ),
@@ -733,42 +755,59 @@ function tripal_add_views_integration($defn_array, $setup_id = FALSE) {
         }
 
         // Insert Joins
+        // Note: The new defn_array structure accounts for 1+ joins to the same
+        // table (ie: feature_relationship => feature : subject_id & object_id)
+        // by making $field['joins'] an array of left_field keys.
         if (!is_array($field['joins'])) {
           $field['joins'] = array();
         }
-        foreach ($field['joins'] as $join) {
-          $join_record = array(
-            'setup_id' => $view_record['setup_id'],
-            'base_table' => $defn_array['table'],
-            'base_field' => $field['name'],
-            'left_table' => $join['table'],
-            'left_field' => $join['field'],
-          );
-
-          $join_record['handler'] = (!empty($join['handler'])) ? $join['handler'] : 'views_join';
-          $join_record['relationship_handler'] = (!empty($join['relationship_handler'])) ? $join['relationship_handler'] : 'views_handler_relationship';
-          $join_record['relationship_only'] = (!empty($join['relationship_only'])) ? $join['relationship_only'] : 0;
-
-          if ($view_record['setup_id'] && $defn_array['table'] && $field['name'] && $join['table'] && $join['field']) {
-            $status = drupal_write_record('tripal_views_join', $join_record);
-          }
-          else {
-            $status = FALSE;
+        foreach ($field['joins'] as $joins) {
+          // To keep backwards compatibility with the old defn_array which just
+          // jumped right into the table definition allowing only a single join,
+          // we need to check for old defn_arrays and transform them into the
+          // new format.
+          if (isset($joins['table'])) {
+            $left_field = $joins['field'];
+            $joins = array(
+              $left_field => $joins
+            );
           }
-          if (!$status) {
-            drupal_set_message(
-              t(
-                'Unable to join %left_table.%left_field with %table.%field',
-                array(
-                  '%left_table' => $join['table'],
-                  '%left_field' => $join['field'],
-                  '%table' => $defn_array['table'],
-                  '%field' => $field['name']
-                )
-              ),
-              'error'
+
+          // Loop on left fields
+          foreach ($joins as $left_field => $join) {
+            $join_record = array(
+              'setup_id' => $view_record['setup_id'],
+              'base_table' => $defn_array['table'],
+              'base_field' => $field['name'],
+              'left_table' => $join['table'],
+              'left_field' => $left_field,
             );
-            $no_errors = FALSE;
+
+            $join_record['handler'] = (!empty($join['handler'])) ? $join['handler'] : 'views_join';
+            $join_record['relationship_handler'] = (!empty($join['relationship_handler'])) ? $join['relationship_handler'] : 'views_handler_relationship';
+            $join_record['relationship_only'] = (!empty($join['relationship_only'])) ? $join['relationship_only'] : 0;
+
+            if ($view_record['setup_id'] && $defn_array['table'] && $field['name'] && $join['table'] && $left_field) {
+              $status = drupal_write_record('tripal_views_join', $join_record);
+            }
+            else {
+              $status = FALSE;
+            }
+            if (!$status) {
+              drupal_set_message(
+                t(
+                  'Unable to join %left_table.%left_field with %table.%field',
+                  array(
+                    '%left_table' => $join['table'],
+                    '%left_field' => $left_field,
+                    '%table' => $defn_array['table'],
+                    '%field' => $field['name']
+                  )
+                ),
+                'error'
+              );
+              $no_errors = FALSE;
+            }
           }
         }
 
@@ -821,7 +860,6 @@ function tripal_export_views_integration($setup_id) {
   );
 
   // Add fields
-  // D7 TODO: Check DBTNG changes work
   $resource = db_query("SELECT * FROM {tripal_views_field} WHERE setup_id=:setup", array(':setup' => $setup_id));
   foreach ($resource as $r) {
     $defn_array['fields'][ $r->column_name ] = array(
@@ -835,7 +873,6 @@ function tripal_export_views_integration($setup_id) {
   }
 
   // Add handlers
-  // D7 TODO: Check DBTNG changes work
   $resource = db_query("SELECT * FROM {tripal_views_handlers} WHERE setup_id=:setup", array(':setup' => $setup_id));
   foreach ($resource as $r) {
     $defn_array['fields'][ $r->column_name ]['handlers'][ $r->handler_type ] = array(
@@ -844,10 +881,9 @@ function tripal_export_views_integration($setup_id) {
   }
 
   // Add joins
-  // D7 TODO: Check DBTNG changes work
   $resource = db_query("SELECT * FROM {tripal_views_join} WHERE setup_id=:setup", array(':setup' => $setup_id));
   foreach ($resource as $r) {
-    $defn_array['fields'][ $r->base_field ]['joins'][ $r->left_table ] = array(
+    $defn_array['fields'][ $r->base_field ]['joins'][ $r->left_table ][ $r->left_field ] = array(
       'table' => $r->left_table,
       'field' => $r->left_field,
       'handler' => $r->handler,
@@ -970,7 +1006,6 @@ function tripal_clone_views_integration($table_name, $new_priority = NULL, $temp
     $defn_array['priority'] = $defn_array['priority'] - 1;
   }
 
-  // D7 TODO: Check DBTNG changes work
   tripal_add_views_integration($defn_array);
   $setup_id = db_query(
     "SELECT setup_id FROM {tripal_views} WHERE table_name=:table AND priority=:priority",
@@ -1103,45 +1138,62 @@ function tripal_add_field_to_views_integration($table_name, $priority, $field) {
     }
 
     // Insert Joins
+    // Note: The new defn_array structure accounts for 1+ joins to the same
+    // table (ie: feature_relationship => feature : subject_id & object_id)
+    // by making $field['joins'] an array of left_field keys.
     if (!is_array($field['joins'])) {
       $field['joins'] = array();
     }
-    foreach ($field['joins'] as $join) {
-      $join_record = array(
-        'setup_id' => $setup_id,
-        'base_table' => $defn_array['table'],
-        'base_field' => $field['name'],
-        'left_table' => $join['table'],
-        'left_field' => $join['field'],
-      );
-
-      if (!empty($join['handler'])) {
-        $join_record['handler'] = $join['handler'];
-      }
-      else {
-        $join_record['handler'] = 'views_join';
+    foreach ($field['joins'] as $joins) {
+      // To keep backwards compatibility with the old defn_array which just
+      // jumped right into the table definition allowing only a single join,
+      // we need to check for old defn_arrays and transform them into the
+      // new format.
+      if (isset($joins['table'])) {
+        $left_field = $joins['field'];
+        $joins = array(
+          $left_field => $joins
+        );
       }
 
-      if ($setup_id && $defn_array['table'] && $field['name'] && $join['table'] && $join['field']) {
-        $status = drupal_write_record('tripal_views_join', $join_record);
-      }
-      else {
-        $status = FALSE;
-      }
-      if (!$status) {
-        drupal_set_message(
-          t(
-            'Unable to join %left_table.%left_field with %table.%field',
-            array(
-              '%left_table' => $join['table'],
-              '%left_field' => $join['field'],
-              '%table' => $defn_array['table'],
-              '%field' => $field['name']
-            )
-          ),
-          'error'
+      // Loop on left fields
+      foreach ($joins as $left_field => $join) {
+        $join_record = array(
+          'setup_id' => $setup_id,
+          'base_table' => $defn_array['table'],
+          'base_field' => $field['name'],
+          'left_table' => $join['table'],
+          'left_field' => $join['field'],
         );
-        $no_errors = FALSE;
+
+        if (!empty($join['handler'])) {
+          $join_record['handler'] = $join['handler'];
+        }
+        else {
+          $join_record['handler'] = 'views_join';
+        }
+
+        if ($setup_id && $defn_array['table'] && $field['name'] && $join['table'] && $join['field']) {
+          $status = drupal_write_record('tripal_views_join', $join_record);
+        }
+        else {
+          $status = FALSE;
+        }
+        if (!$status) {
+          drupal_set_message(
+            t(
+              'Unable to join %left_table.%left_field with %table.%field',
+              array(
+                '%left_table' => $join['table'],
+                '%left_field' => $join['field'],
+                '%table' => $defn_array['table'],
+                '%field' => $field['name']
+              )
+            ),
+            'error'
+          );
+          $no_errors = FALSE;
+        }
       }
     }
 

+ 21 - 4
tripal_views/includes/tripal_views_integration.inc

@@ -111,6 +111,7 @@ function tripal_views_get_integration_array_for_chado_table($table_name, $base_t
           'field' => array('name' => 'views_handler_field_numeric'),
           'filter' => array('name' => 'views_handler_filter_numeric'),
           'sort' => array('name' => 'views_handler_sort'),
+          'argument' => array('name' => 'views_handler_argument_numeric'),
         );
       }
       elseif (preg_match('/^serial/', $field_schema['type'])) {
@@ -123,6 +124,7 @@ function tripal_views_get_integration_array_for_chado_table($table_name, $base_t
           'field' => array('name' => 'views_handler_field_numeric'),
           'filter' => array('name' => 'views_handler_filter_numeric'),
           'sort' => array('name' => 'views_handler_sort'),
+          'argument' => array('name' => 'views_handler_argument_numeric'),
         );
         $defn_array['fields'][$field_name]['type'] = 'int';
       }
@@ -136,6 +138,7 @@ function tripal_views_get_integration_array_for_chado_table($table_name, $base_t
           'field' => array('name' => 'views_handler_field'),
           'filter' => array('name' => 'views_handler_filter_string'),
           'sort' => array('name' => 'views_handler_sort'),
+          'argument' => array('name' => 'views_handler_argument_string'),
         );
       }
       elseif (preg_match('/^text/', $field_schema['type'])) {
@@ -148,6 +151,7 @@ function tripal_views_get_integration_array_for_chado_table($table_name, $base_t
           'field' => array('name' => 'views_handler_field'),
           'filter' => array('name' => 'views_handler_filter_string'),
           'sort' => array('name' => 'views_handler_sort'),
+          'argument' => array('name' => 'views_handler_argument_string'),
         );
       }
       elseif (preg_match('/^boolean/', $field_schema['type'])) {
@@ -172,6 +176,7 @@ function tripal_views_get_integration_array_for_chado_table($table_name, $base_t
           'field' => array('name' => 'views_handler_field_date'),
           'filter' => array('name' => 'views_handler_filter_date'),
           'sort' => array('name' => 'views_handler_sort_date'),
+          'argument' => array('name' => 'views_handler_argument_date'),
         );
       }
       else {
@@ -184,6 +189,7 @@ function tripal_views_get_integration_array_for_chado_table($table_name, $base_t
           'field' => array('name' => 'views_handler_field'),
           'filter' => array('name' => 'views_handler_filter_string'),
           'sort' => array('name' => 'views_handler_sort'),
+          'argument' => array('name' => 'views_handler_argument_string'),
         );
       }
 
@@ -203,11 +209,13 @@ function tripal_views_get_integration_array_for_chado_table($table_name, $base_t
   }
   foreach ($schema['foreign keys'] as $foreign_key_schema) {
     foreach ($foreign_key_schema['columns'] as $left_field => $right_field) {
-      // Join
-      $defn_array['fields'][$left_field]['joins'][ $foreign_key_schema['table'] ] = array(
+
+      // Note: Even though there can only be a single join  for a foreign key
+      // we make the joins an array keyed by left_field to ensure that both
+      // foeign key and referring_tables (see below) can be processed the same.
+      $defn_array['fields'][$left_field]['joins'][ $foreign_key_schema['table'] ][ $left_field ] = array(
         'table' => $foreign_key_schema['table'],
         'field' => $right_field,
-        /**D6 'handler' => 'views_handler_join_chado_aggregator'*/
         'handler' => 'views_handler_join',
         'relationship_handler' => 'views_handler_relationship',
       );
@@ -215,6 +223,10 @@ function tripal_views_get_integration_array_for_chado_table($table_name, $base_t
   }
 
   // Add in reverse relationships
+  // Note: The array structure is set up with the left_field keyed array to
+  // handle more than one join between the same set of tables on different
+  // fields. For example, the reverse relationship between feature &
+  // feature_relationship needs to join on both the subject_id and object_id.
   if (isset($schema['referring_tables'])) {
     foreach ($schema['referring_tables'] as $referring_table) {
 
@@ -222,12 +234,17 @@ function tripal_views_get_integration_array_for_chado_table($table_name, $base_t
       $referring_schema = chado_get_schema($referring_table);
       $referring_schema_fk_columns = $referring_schema['foreign keys'][$table_name]['columns'];
       foreach ($referring_schema_fk_columns as $left_field => $right_field) {
-        $defn_array['fields'][$right_field]['joins'][ $referring_table ] = array(
+
+        // Also notice that it doesn't matter whether this is the first or second
+        // reverse join on this table ($referring_table) to be added since
+        // having $left_field as the key keeps them separate.
+        $defn_array['fields'][$right_field]['joins'][ $referring_table ][ $left_field ] = array(
           'table' => $referring_table,
           'field' => $left_field,
           'relationship_handler' => 'views_handler_relationship',
           'relationship_only' => 1
         );
+
       }
     }
   }

+ 33 - 12
tripal_views/tripal_views.views.inc

@@ -340,7 +340,7 @@ function tripal_views_views_data() {
         if (!$join->relationship_only) {
           $data[$left_table]['table']['join'][$base_table] = array(
             'left_field' => $base_field,
-            'field' => $left_table . '_id',
+            'field' => $left_field,
           );
           if ($handler) {
             $data[$left_table]['table']['join'][$base_table]['handler'] = $handler;
@@ -356,15 +356,36 @@ function tripal_views_views_data() {
             'DEPRECATED: Currently using tripal_views_handlers to store relationship for %base => %left when you should be using tripal_views_joins.',
             array('%base' => $base_table, '%left' => $left_table));
         }
-        // add relationship entry
+
+        // Add relationship entry.
+        // NOTE: we use a fake field name to allow us to have multiple
+        // relationships for the same field (ie: feature.feature_id has many
+        // Many relationships but views only supports a single one).
         $fake_field = $base_field . '_to_' . $left_table;
+
+        // Bug Fix: The old $fake_field used above doesn't take into account
+        // multiple relationships to the same left table. To keep backwards
+        // compatibility, this old fake_field needs to continue to be used for
+        // the LAST recorded relationship. However, for all previously set
+        // relationships we can use an improved fake name which takes into
+        // account the left field and ensures all relationships for a single
+        // left table are not condensed into a single relationship.
+        if (array_key_exists($fake_field, $data[$base_table])) {
+          $improved_fake_field = $base_field . '_to_' . $left_table . "." . $left_field;
+          // Again, note that we can't just change the fake_name after finding
+          // there is more than one relationship because then the FIRST
+          // relationship would keep the old fake_name rather than the LAST
+          // which keeps backwards compatiblity since the old naming caused all
+          // previous relationships be be overridden by the next one.
+          $data[$base_table][$improved_fake_field] = $data[$base_table][$fake_field];
+        }
         $data[$base_table][$fake_field] = array(
-          'title' => "$base_title.$base_field => $left_title",
-          'help' => "Joins $base_title.$base_field to $left_title",
+          'title' => "$base_title.$base_field => $left_title.$left_field",
+          'help' => t("Joins @base to @left", array('@base' => "$base_title.$base_field", '@left' => "$left_title.$left_field")),
           'relationship' => array(
             'handler' => $join->relationship_handler,
-            'title' => t("$base_field => $left_title"),
-            'label' => t("$base_field => $left_title"),
+            'title' => t("$base_field => $left_title ($left_field)"),
+            'label' => t("$base_field => $left_title ($left_field)"),
             'real field' => $base_field,
             'base' => $left_table,
             'base field' => $left_field
@@ -582,11 +603,11 @@ function tripal_views_views_data_alter(&$data) {
           $data[$linker_table][$base_field] = array(
             'group' => $base_title,
             'title' => $base_title . 'Node',
-            'help' => "Links $base_title to it's node.",
+            'help' => t("Links @base_title to it's node.", array('@base_title' => $base_title)),
             'relationship' => array(
               'handler' => 'views_handler_relationship',
-              'title' => t("$base_title => Node"),
-              'label' => t("$base_title => Node"),
+              'title' => t("@base_title => Node", array('@base_title' => $base_title)),
+              'label' => t("@base_title => Node", array('@base_title' => $base_title)),
               'real field' => 'nid',
               'base' => 'node',
               'base field' => 'nid'
@@ -599,11 +620,11 @@ function tripal_views_views_data_alter(&$data) {
           $data['node'][$base_field] = array(
             'group' => $base_title,
             'title' => $base_title,
-            'help' => "Links node to chado $base_title.",
+            'help' => t("Links node to chado @base_title.", array('@base_title' => $base_title)),
             'relationship' => array(
               'handler' => 'views_handler_relationship',
-              'title' => t("Node => $base_title"),
-              'label' => t("Node => $base_title"),
+              'title' => t("Node => @base_title", array('@base_title' => $base_title)),
+              'label' => t("Node => @base_title", array('@base_title' => $base_title)),
               'real field' => 'nid',
               'base' => $linker_table,
               'base field' => 'nid'