From 744050b03bb40310b3e6108c33a4376adb4f01b3 Mon Sep 17 00:00:00 2001
From: Jitendra Purohit <jitendra@fuzion.co.nz>
Date: Fri, 16 Feb 2018 12:43:45 +0530
Subject: [PATCH] CRM-21776: Ensure from clause adds custom table when search
 query is ordered by a custom field

---
 CRM/Contact/BAO/Query.php                  |  9 ++++
 tests/phpunit/CRM/Contact/SelectorTest.php | 53 ++++++++++++++++++++++
 2 files changed, 62 insertions(+)

diff --git a/CRM/Contact/BAO/Query.php b/CRM/Contact/BAO/Query.php
index 5435aa3c2fb6..bcc3ee00a58e 100644
--- a/CRM/Contact/BAO/Query.php
+++ b/CRM/Contact/BAO/Query.php
@@ -6418,6 +6418,15 @@ protected function prepareOrderBy($sort, $sortByChar, $sortOrder, $additionalFro
           break;
 
         default:
+          $cfID = CRM_Core_BAO_CustomField::getKeyID($field);
+          // add to cfIDs array if not present
+          if (!empty($cfID) && !array_key_exists($cfID, $this->_cfIDs)) {
+            $this->_cfIDs[$cfID] = array();
+            $this->_customQuery = new CRM_Core_BAO_CustomQuery($this->_cfIDs, TRUE, $this->_locationSpecificCustomFields);
+            $this->_customQuery->query();
+            $this->_select = array_merge($this->_select, $this->_customQuery->_select);
+            $this->_tables = array_merge($this->_tables, $this->_customQuery->_tables);
+          }
           foreach ($this->_pseudoConstantsSelect as $key => $pseudoConstantMetadata) {
             // By replacing the join to the option value table with the mysql construct
             // ORDER BY field('contribution_status_id', 2,1,4)
diff --git a/tests/phpunit/CRM/Contact/SelectorTest.php b/tests/phpunit/CRM/Contact/SelectorTest.php
index 8aa95640b09c..58e41583aee1 100644
--- a/tests/phpunit/CRM/Contact/SelectorTest.php
+++ b/tests/phpunit/CRM/Contact/SelectorTest.php
@@ -173,6 +173,59 @@ public function testContactIDQuery() {
     $searchOBJ->contactIDQuery($params, '1_u');
   }
 
+  /**
+   * Test if custom table is added in from clause when
+   * search results are ordered by a custom field.
+   */
+  public function testSelectorQueryOrderByCustomField() {
+    //Search for any params.
+    $params = [[
+      0 => 'country-1',
+      1 => '=',
+      2 => '1228',
+      3 => 1,
+      4 => 0,
+    ]];
+
+    //Create a test custom group and field.
+    $customGroup = $this->callAPISuccess('CustomGroup', 'create', array(
+      'title' => "test custom group",
+      'extends' => "Individual",
+    ));
+    $cgTableName = $customGroup['values'][$customGroup['id']]['table_name'];
+    $customField = $this->callAPISuccess('CustomField', 'create', array(
+      'custom_group_id' => $customGroup['id'],
+      'label' => "test field",
+      'html_type' => "Text",
+    ));
+    $customFieldId = $customField['id'];
+
+    //Sort by the custom field created above.
+    $sortParams = array(
+      1 => array(
+        'name' => 'test field',
+        'sort' => "custom_{$customFieldId}",
+      ),
+    );
+    $sort = new CRM_Utils_Sort($sortParams, '1_d');
+
+    //Form a query to order by a custom field.
+    $query = new CRM_Contact_BAO_Query($params,
+      CRM_Contact_BAO_Query::NO_RETURN_PROPERTIES,
+      NULL, FALSE, FALSE, 1,
+      FALSE, TRUE, TRUE, NULL,
+      'AND'
+    );
+    $query->searchQuery(0, 0, $sort,
+      FALSE, FALSE, FALSE,
+      FALSE, FALSE
+    );
+    //Check if custom table is included in $query->_tables.
+    $this->assertTrue(in_array($cgTableName, array_keys($query->_tables)));
+    //Assert if from clause joins the custom table.
+    $this->assertTrue(strpos($query->_fromClause, $cgTableName) !== FALSE);
+  }
+
   /**
    * Get the default select string since this is generally consistent.
    */