CRM_Contact_BAO_Query - Extract method `prepareOrderBy()` (via PHPStorm)
authorTim Otten <totten@civicrm.org>
Wed, 25 May 2016 01:41:32 +0000 (18:41 -0700)
committerTim Otten <totten@civicrm.org>
Wed, 25 May 2016 01:41:32 +0000 (18:41 -0700)
The fixes on `searchQuery` are making a bad function even harder to read.
In keeping with https://wiki.civicrm.org/confluence/display/CRM/Toxic+Code+Protocol , we
need to some cleanup at the same time.

This was generated using PHPStorm's automated refactoring, "Extract Method".

CRM/Contact/BAO/Query.php

index c0d45a0111b084ba9206e8e93cc88dd0fc1177d3..d863806831a69b988838769a06e25657efb8c018 100644 (file)
@@ -4491,7 +4491,7 @@ civicrm_relationship.is_permission_a_b = 0
    *   The offset for the query.
    * @param int $rowCount
    *   The number of rows to return.
-   * @param string $sort
+   * @param string|CRM_Utils_Sort $sort
    *   The order by string.
    * @param bool $count
    *   Is this a count only query ?.
@@ -4559,96 +4559,7 @@ civicrm_relationship.is_permission_a_b = 0
 
     $order = $orderBy = $limit = '';
     if (!$count) {
-      $config = CRM_Core_Config::singleton();
-      if ($config->includeOrderByClause ||
-        isset($this->_distinctComponentClause)
-      ) {
-        if ($sort) {
-          if (is_string($sort)) {
-            $orderBy = $sort;
-          }
-          else {
-            $orderBy = trim($sort->orderBy());
-          }
-          // Deliberately remove the backticks again, as they mess up the evil
-          // string munging below. This balanced by re-escaping before use.
-          $orderBy = str_replace('`', '', $orderBy);
-
-          if (!empty($orderBy)) {
-            // this is special case while searching for
-            // change log CRM-1718
-            if (preg_match('/sort_name/i', $orderBy)) {
-              $orderBy = str_replace('sort_name', 'contact_a.sort_name', $orderBy);
-            }
-
-            $order = " ORDER BY $orderBy";
-
-            if ($sortOrder) {
-              $order .= " $sortOrder";
-            }
-
-            // always add contact_a.id to the ORDER clause
-            // so the order is deterministic
-            if (strpos('contact_a.id', $order) === FALSE) {
-              $order .= ", contact_a.id";
-            }
-          }
-        }
-        elseif ($sortByChar) {
-          $order = " ORDER BY UPPER(LEFT(contact_a.sort_name, 1)) asc";
-        }
-        else {
-          $order = " ORDER BY contact_a.sort_name asc, contact_a.id";
-        }
-      }
-
-      // hack for order clause
-      if ($order) {
-        $fieldStr = trim(str_replace('ORDER BY', '', $order));
-        $fieldOrder = explode(' ', $fieldStr);
-        $field = $fieldOrder[0];
-
-        if ($field) {
-          switch ($field) {
-            case 'city':
-            case 'postal_code':
-              $this->_tables["civicrm_address"] = $this->_whereTables["civicrm_address"] = 1;
-              $order = str_replace($field, "civicrm_address.{$field}", $order);
-              break;
-
-            case 'country':
-            case 'state_province':
-              $this->_tables["civicrm_{$field}"] = $this->_whereTables["civicrm_{$field}"] = 1;
-              if (is_array($this->_returnProperties) && empty($this->_returnProperties)) {
-                $additionalFromClause .= " LEFT JOIN civicrm_{$field} ON civicrm_{$field}.id = civicrm_address.{$field}_id";
-              }
-              $order = str_replace($field, "civicrm_{$field}.name", $order);
-              break;
-
-            case 'email':
-              $this->_tables["civicrm_email"] = $this->_whereTables["civicrm_email"] = 1;
-              $order = str_replace($field, "civicrm_email.{$field}", $order);
-              break;
-
-            default:
-              //CRM-12565 add "`" around $field if it is a pseudo constant
-              foreach ($this->_pseudoConstantsSelect as $key => $value) {
-                if (!empty($value['element']) && $value['element'] == $field) {
-                  $order = str_replace($field, "`{$field}`", $order);
-                }
-              }
-          }
-          $this->_fromClause = self::fromClause($this->_tables, NULL, NULL, $this->_primaryLocation, $this->_mode);
-          $this->_simpleFromClause = self::fromClause($this->_whereTables, NULL, NULL, $this->_primaryLocation, $this->_mode);
-        }
-      }
-
-      // The above code relies on crazy brittle string manipulation of a peculiarly-encoded ORDER BY
-      // clause. But this magic helper which forgivingly reescapes ORDER BY.
-      // Note: $sortByChar implies that $order was hard-coded/trusted, so it can do funky things.
-      if ($order && !$sortByChar) {
-        $order = ' ORDER BY ' . CRM_Utils_Type::escape(preg_replace('/^\s*ORDER BY\s*/', '', $order), 'MysqlOrderBy');
-      }
+      list($order, $additionalFromClause) = $this->prepareOrderBy($sort, $sortByChar, $sortOrder, $additionalFromClause);
 
       if ($rowCount > 0 && $offset >= 0) {
         $offset = CRM_Utils_Type::escape($offset, 'Int');
@@ -6003,4 +5914,119 @@ AND   displayRelType.is_active = 1
     }
   }
 
+  /**
+   * Parse and assimilate the various sort options.
+   *
+   * Side-effect: if sorting on a common column from a related table (`city`, `postal_code`,
+   * `email`), the related table may be joined automatically.
+   *
+   * At time of writing, this code is deeply flawed and should be rewritten. For the moment,
+   * it's been extracted to a standalone function.
+   *
+   * @param string|CRM_Utils_Sort $sort
+   *   The order by string.
+   * @param bool $sortByChar
+   *   If true returns the distinct array of first characters for search results.
+   * @param null $sortOrder
+   *   Who knows? Hu knows. He who knows Hu knows who.
+   * @param string $additionalFromClause
+   *   Should be clause with proper joins, effective to reduce where clause load.
+   * @return array
+   *   list(string $orderByClause, string $additionalFromClause).
+   */
+  protected function prepareOrderBy($sort, $sortByChar, $sortOrder, $additionalFromClause) {
+    $config = CRM_Core_Config::singleton();
+    if ($config->includeOrderByClause ||
+      isset($this->_distinctComponentClause)
+    ) {
+      if ($sort) {
+        if (is_string($sort)) {
+          $orderBy = $sort;
+        }
+        else {
+          $orderBy = trim($sort->orderBy());
+        }
+        // Deliberately remove the backticks again, as they mess up the evil
+        // string munging below. This balanced by re-escaping before use.
+        $orderBy = str_replace('`', '', $orderBy);
+
+        if (!empty($orderBy)) {
+          // this is special case while searching for
+          // change log CRM-1718
+          if (preg_match('/sort_name/i', $orderBy)) {
+            $orderBy = str_replace('sort_name', 'contact_a.sort_name', $orderBy);
+          }
+
+          $order = " ORDER BY $orderBy";
+
+          if ($sortOrder) {
+            $order .= " $sortOrder";
+          }
+
+          // always add contact_a.id to the ORDER clause
+          // so the order is deterministic
+          if (strpos('contact_a.id', $order) === FALSE) {
+            $order .= ", contact_a.id";
+          }
+        }
+      }
+      elseif ($sortByChar) {
+        $order = " ORDER BY UPPER(LEFT(contact_a.sort_name, 1)) asc";
+      }
+      else {
+        $order = " ORDER BY contact_a.sort_name asc, contact_a.id";
+      }
+    }
+
+    // hack for order clause
+    if ($order) {
+      $fieldStr = trim(str_replace('ORDER BY', '', $order));
+      $fieldOrder = explode(' ', $fieldStr);
+      $field = $fieldOrder[0];
+
+      if ($field) {
+        switch ($field) {
+          case 'city':
+          case 'postal_code':
+            $this->_tables["civicrm_address"] = $this->_whereTables["civicrm_address"] = 1;
+            $order = str_replace($field, "civicrm_address.{$field}", $order);
+            break;
+
+          case 'country':
+          case 'state_province':
+            $this->_tables["civicrm_{$field}"] = $this->_whereTables["civicrm_{$field}"] = 1;
+            if (is_array($this->_returnProperties) && empty($this->_returnProperties)) {
+              $additionalFromClause .= " LEFT JOIN civicrm_{$field} ON civicrm_{$field}.id = civicrm_address.{$field}_id";
+            }
+            $order = str_replace($field, "civicrm_{$field}.name", $order);
+            break;
+
+          case 'email':
+            $this->_tables["civicrm_email"] = $this->_whereTables["civicrm_email"] = 1;
+            $order = str_replace($field, "civicrm_email.{$field}", $order);
+            break;
+
+          default:
+            //CRM-12565 add "`" around $field if it is a pseudo constant
+            foreach ($this->_pseudoConstantsSelect as $key => $value) {
+              if (!empty($value['element']) && $value['element'] == $field) {
+                $order = str_replace($field, "`{$field}`", $order);
+              }
+            }
+        }
+        $this->_fromClause = self::fromClause($this->_tables, NULL, NULL, $this->_primaryLocation, $this->_mode);
+        $this->_simpleFromClause = self::fromClause($this->_whereTables, NULL, NULL, $this->_primaryLocation, $this->_mode);
+      }
+    }
+
+    // The above code relies on crazy brittle string manipulation of a peculiarly-encoded ORDER BY
+    // clause. But this magic helper which forgivingly reescapes ORDER BY.
+    // Note: $sortByChar implies that $order was hard-coded/trusted, so it can do funky things.
+    if ($order && !$sortByChar) {
+      $order = ' ORDER BY ' . CRM_Utils_Type::escape(preg_replace('/^\s*ORDER BY\s*/', '', $order), 'MysqlOrderBy');
+      return array($order, $additionalFromClause);
+    }
+    return array($order, $additionalFromClause);
+  }
+
 }