From: Tim Otten Date: Wed, 25 May 2016 01:41:32 +0000 (-0700) Subject: CRM_Contact_BAO_Query - Extract method `prepareOrderBy()` (via PHPStorm) X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=db54ba068f60415927408e592db8d3e8f9ca209d;p=civicrm-core.git CRM_Contact_BAO_Query - Extract method `prepareOrderBy()` (via PHPStorm) 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". --- diff --git a/CRM/Contact/BAO/Query.php b/CRM/Contact/BAO/Query.php index c0d45a0111..d863806831 100644 --- a/CRM/Contact/BAO/Query.php +++ b/CRM/Contact/BAO/Query.php @@ -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); + } + }