From 155a314b8c51f36989a654882522595a16d4511a Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Tue, 20 Jan 2015 13:18:41 +0000
Subject: [PATCH] Issue #2378729 by jibran: JoinPluginBase doesn't allow extra
 conditions on left table

---
 .../src/Plugin/views/join/JoinPluginBase.php  | 83 +++++++++++++++++--
 .../views/src/Tests/Plugin/JoinTest.php       | 31 +++++++
 core/modules/views/views.api.php              | 22 +++++
 3 files changed, 130 insertions(+), 6 deletions(-)

diff --git a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
index b9ce5a515a1c..051955e4bfb9 100644
--- a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
+++ b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
@@ -21,8 +21,8 @@
  * They must be annotated with \Drupal\views\Annotation\ViewsJoin annotation,
  * and they must be in namespace directory Plugin\views\join.
  *
- * Here is an example of how to join from table one to table two so it produces
- * the following SQL:
+ * Here are some examples of how to join from table one to table two so it
+ * produces the following SQL:
  * @code
  * INNER JOIN {two} ON one.field_a = two.field_b
  * @endcode
@@ -33,7 +33,67 @@
  *   'field' => 'field_b',
  *   'left_table' => 'one',
  *   'left_field' => 'field_a',
- *   'operator' => '='
+ *   'operator' => '=',
+ * );
+ * $join = Views::pluginManager('join')->createInstance('standard', $configuration);
+ * @endcode
+ * @code
+ * INNER JOIN {two} ON one.field_a = two.field_b AND one.field_c = 'some_val'
+ * @endcode
+ * The required php code for this kind of functionality is the following:
+ * @code
+ * $configuration = array(
+ *   'table' => 'two',
+ *   'field' => 'field_b',
+ *   'left_table' => 'one',
+ *   'left_field' => 'field_a',
+ *   'operator' => '=',
+ *   'extra' => array(
+ *     0 => array(
+ *       'left_field' => 'field_c',
+ *       'value' => 'some_val',
+ *     ),
+ *   ),
+ * );
+ * $join = Views::pluginManager('join')->createInstance('standard', $configuration);
+ * @endcode
+ * @code
+ * INNER JOIN {two} ON one.field_a = two.field_b AND two.field_d = 'other_val'
+ * @endcode
+ * The required php code for this kind of functionality is the following:
+ * @code
+ * $configuration = array(
+ *   'table' => 'two',
+ *   'field' => 'field_b',
+ *   'left_table' => 'one',
+ *   'left_field' => 'field_a',
+ *   'operator' => '=',
+ *   'extra' => array(
+ *     0 => array(
+ *       'field' => 'field_d',
+ *       'value' => 'other_val',
+ *     ),
+ *   ),
+ * );
+ * $join = Views::pluginManager('join')->createInstance('standard', $configuration);
+ * @endcode
+ * @code
+ * INNER JOIN {two} ON one.field_a = two.field_b AND one.field_c = two.field_d
+ * @endcode
+ * The required php code for this kind of functionality is the following:
+ * @code
+ * $configuration = array(
+ *   'table' => 'two',
+ *   'field' => 'field_b',
+ *   'left_table' => 'one',
+ *   'left_field' => 'field_a',
+ *   'operator' => '=',
+ *   'extra' => array(
+ *     0 => array(
+ *       'left_field' => 'field_c',
+ *       'field' => 'field_d',
+ *     ),
+ *   ),
  * );
  * $join = Views::pluginManager('join')->createInstance('standard', $configuration);
  * @endcode
@@ -98,6 +158,8 @@ class JoinPluginBase extends PluginBase implements JoinPluginInterface {
    *     existing alias. If you use realtime joins, it works better.
    *   - field(optional): Field or formula. In formulas we can reference the
    *     right table by using %alias.
+   *   - left_field(optional): Field or formula. In formulas we can reference
+   *     the left table by using %alias.
    *   - operator(optional): The operator used, Defaults to "=".
    *   - value: Must be set. If an array, operator will be defaulted to IN.
    *   - numeric: If true, the value will not be surrounded in quotes.
@@ -257,18 +319,27 @@ public function buildJoin($select_query, $table, $view_query) {
           else {
             // With a single value, the '=' operator is implicit.
             $operator = !empty($info['operator']) ? $info['operator'] : '=';
+            $placeholder = ':views_join_condition_' . $select_query->nextPlaceholder();
+          }
+          // Set 'field' as join table field if available or set 'left field' as
+          // join table field is not set.
+          if (isset($info['field'])) {
+            $join_table_field = "$join_table$info[field]";
             // Allow the value to be set either with the 'value' element or
             // with 'left_field'.
             if (isset($info['left_field'])) {
               $placeholder = "$left[alias].$info[left_field]";
             }
             else {
-              $placeholder = ':views_join_condition_' . $select_query->nextPlaceholder();
               $arguments[$placeholder] = $info['value'];
             }
           }
-
-          $extras[] = "$join_table$info[field] $operator $placeholder";
+          // Set 'left field' as join table field is not set.
+          else {
+            $join_table_field = "$left[alias].$info[left_field]";
+            $arguments[$placeholder] = $info['value'];
+          }
+          $extras[] = "$join_table_field $operator $placeholder";
         }
 
         if ($extras) {
diff --git a/core/modules/views/src/Tests/Plugin/JoinTest.php b/core/modules/views/src/Tests/Plugin/JoinTest.php
index c557a9400fd3..28dbc4c64166 100644
--- a/core/modules/views/src/Tests/Plugin/JoinTest.php
+++ b/core/modules/views/src/Tests/Plugin/JoinTest.php
@@ -149,6 +149,7 @@ public function testBasePlugin() {
 
     $tables = $query->getTables();
     $join_info = $tables['users3'];
+    $this->assertTrue(strpos($join_info['condition'], "views_test_data.uid = users3.uid") !== FALSE, 'Make sure the join condition appears in the query.');
     $this->assertTrue(strpos($join_info['condition'], "users3.name = :views_join_condition_0") !== FALSE, 'Make sure the first extra join condition appears in the query and uses the first placeholder.');
     $this->assertTrue(strpos($join_info['condition'], "users3.name <> :views_join_condition_1") !== FALSE, 'Make sure the second extra join condition appears in the query and uses the second placeholder.');
     $this->assertEqual(array_values($join_info['arguments']), array($random_name_1, $random_name_2), 'Make sure the arguments are in the right order');
@@ -174,7 +175,37 @@ public function testBasePlugin() {
 
     $tables = $query->getTables();
     $join_info = $tables['users4'];
+    $this->assertTrue(strpos($join_info['condition'], "views_test_data.uid = users4.uid") !== FALSE, 'Make sure the join condition appears in the query.');
+    $this->assertTrue(strpos($join_info['condition'], "users4.name = :views_join_condition_2") !== FALSE, 'Make sure the first extra join condition appears in the query.');
     $this->assertTrue(strpos($join_info['condition'], "users4.name IN ( :views_join_condition_3, :views_join_condition_4, :views_join_condition_5 )") !== FALSE, 'The IN condition for the join is properly formed.');
+
+    // Test that all the conditions are properly built.
+    $configuration['extra'] = array(
+      array(
+        'field' => 'langcode',
+        'value' => 'en'
+      ),
+      array(
+        'left_field' => 'status',
+        'value' => 0,
+        'numeric' => TRUE,
+      ),
+      array(
+        'field' => 'name',
+        'left_field' => 'name'
+      ),
+    );
+    $join = $this->manager->createInstance('standard', $configuration);
+    $table = array('alias' => 'users5');
+    $join->buildJoin($query, $table, $view->query);
+
+    $tables = $query->getTables();
+    $join_info = $tables['users5'];
+    $this->assertTrue(strpos($join_info['condition'], "views_test_data.uid = users5.uid") !== FALSE, 'Make sure the join condition appears in the query.');
+    $this->assertTrue(strpos($join_info['condition'], "users5.langcode = :views_join_condition_6") !== FALSE, 'Make sure the first extra join condition appears in the query.');
+    $this->assertTrue(strpos($join_info['condition'], "views_test_data.status = :views_join_condition_7") !== FALSE, 'Make sure the second extra join condition appears in the query.');
+    $this->assertTrue(strpos($join_info['condition'], "users5.name = views_test_data.name") !== FALSE, 'Make sure the third extra join condition appears in the query.');
+    $this->assertEqual(array_values($join_info['arguments']), array('en', 0), 'Make sure the arguments are in the right order');
   }
 
 }
diff --git a/core/modules/views/views.api.php b/core/modules/views/views.api.php
index 120bed0725fd..9f2813b395bb 100644
--- a/core/modules/views/views.api.php
+++ b/core/modules/views/views.api.php
@@ -188,6 +188,28 @@ function hook_views_data() {
       'left_field' => 'nid',
       // Foreign key field in example_table to use in the join.
       'field' => 'nid',
+      // An array of extra conditions on the join.
+      'extra' => array(
+        0 => array(
+          // Adds AND node.published = TRUE to the join.
+          'field' => 'published',
+          'value' => TRUE,
+        ),
+        1 => array(
+          // Adds AND example_table.numeric_field = 1 to the join.
+          'left_field' => 'numeric_field',
+          'value' => 1,
+          // If true, the value will not be surrounded in quotes.
+          'numeric' => TRUE,
+        ),
+        2 => array(
+          // Adds AND example_table.boolean_field <> node.published to the join.
+          'field' => 'published',
+          'left_field' => 'boolean_field',
+          // The operator used, Defaults to "=".
+          'operator' => '!=',
+        ),
+      ),
     ),
   );
 
-- 
GitLab