3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
14 * Base class to provide generic sort functionality.
16 * Note that some ideas have been borrowed from the drupal tablesort.inc code.
18 * Also note that since the Pager and Sort class are similar, do match the function names
19 * if introducing additional functionality
22 * @copyright CiviCRM LLC https://civicrm.org/licensing
24 class CRM_Utils_Sort
{
27 * Constants to determine what direction each variable
32 const ASCENDING
= 1, DESCENDING
= 2, DONTCARE
= 4,
35 * The name for the sort GET/POST param
39 SORT_ID
= 'crmSID', SORT_DIRECTION
= 'crmSortDirection', SORT_ORDER
= 'crmSortOrder';
42 * Name of the sort function. Used to isolate session variables
48 * Array of variables that influence the query
55 * The newly formulated base url to be used as links
56 * for various table elements
63 * What's the name of the sort variable in a REQUEST
70 * What variable are we currently sorting on
74 protected $_currentSortID;
77 * What direction are we sorting on
81 protected $_currentSortDirection;
84 * The output generated for the current form
91 * The constructor takes an assoc array
92 * key names of variable (which should be the same as the column name)
93 * value: ascending or descending
96 * Assoc array as described above.
97 * @param string $defaultSortOrder
100 * @return \CRM_Utils_Sort
102 public function __construct(&$vars, $defaultSortOrder = NULL) {
104 $this->_response
= [];
106 foreach ($vars as $weight => $value) {
107 $this->_vars
[$weight] = [
108 'name' => CRM_Utils_Type
::validate($value['sort'], 'MysqlColumnNameOrAlias'),
109 'direction' => CRM_Utils_Array
::value('direction', $value),
110 'title' => $value['name'],
114 $this->_currentSortID
= 1;
115 if (isset($this->_vars
[$this->_currentSortID
])) {
116 $this->_currentSortDirection
= $this->_vars
[$this->_currentSortID
]['direction'];
118 $this->_urlVar
= self
::SORT_ID
;
119 $this->_link
= CRM_Utils_System
::makeURL($this->_urlVar
, TRUE);
121 $this->initialize($defaultSortOrder);
125 * Function returns the string for the order by clause.
128 * the order by clause
130 public function orderBy() {
131 if (empty($this->_vars
[$this->_currentSortID
])) {
135 if ($this->_vars
[$this->_currentSortID
]['direction'] == self
::ASCENDING ||
136 $this->_vars
[$this->_currentSortID
]['direction'] == self
::DONTCARE
138 $this->_vars
[$this->_currentSortID
]['name'] = str_replace(' ', '_', $this->_vars
[$this->_currentSortID
]['name']);
139 return CRM_Utils_Type
::escape($this->_vars
[$this->_currentSortID
]['name'], 'MysqlColumnNameOrAlias') . ' asc';
142 $this->_vars
[$this->_currentSortID
]['name'] = str_replace(' ', '_', $this->_vars
[$this->_currentSortID
]['name']);
143 return CRM_Utils_Type
::escape($this->_vars
[$this->_currentSortID
]['name'], 'MysqlColumnNameOrAlias') . ' desc';
148 * Create the sortID string to be used in the GET param.
153 * The direction of the sort.
156 * the string to append to the url
158 public static function sortIDValue($index, $dir) {
159 return ($dir == self
::DESCENDING
) ?
$index . '_d' : $index . '_u';
163 * Init the sort ID values in the object.
165 * @param string $defaultSortOrder
166 * The sort order to use by default.
168 public function initSortID($defaultSortOrder) {
169 $url = CRM_Utils_Array
::value(self
::SORT_ID
, $_GET, $defaultSortOrder);
175 list($current, $direction) = explode('_', $url);
177 // if current is weird and does not exist in the vars array, skip
178 if (!array_key_exists($current, $this->_vars
)) {
182 if ($direction == 'u') {
183 $direction = self
::ASCENDING
;
185 elseif ($direction == 'd') {
186 $direction = self
::DESCENDING
;
189 $direction = self
::DONTCARE
;
192 $this->_currentSortID
= $current;
193 $this->_currentSortDirection
= $direction;
194 $this->_vars
[$current]['direction'] = $direction;
200 * @param string $defaultSortOrder
201 * The sort order to use by default.
203 public function initialize($defaultSortOrder) {
204 $this->initSortID($defaultSortOrder);
206 $this->_response
= [];
208 $current = $this->_currentSortID
;
209 foreach ($this->_vars
as $index => $item) {
210 $name = $item['name'];
211 $this->_response
[$name] = [];
213 $newDirection = ($item['direction'] == self
::ASCENDING
) ? self
::DESCENDING
: self
::ASCENDING
;
215 if ($current == $index) {
216 if ($item['direction'] == self
::ASCENDING
) {
217 $class = 'sorting_asc';
220 $class = 'sorting_desc';
227 $this->_response
[$name]['link'] = '<a href="' . $this->_link
. $this->sortIDValue($index, $newDirection) . '" class="' . $class . '">' . $item['title'] . '</a>';
232 * Getter for currentSortID.
235 * returns of the current sort id
237 public function getCurrentSortID() {
238 return $this->_currentSortID
;
242 * Getter for currentSortDirection.
245 * returns of the current sort direction
247 public function getCurrentSortDirection() {
248 return $this->_currentSortDirection
;
252 * Universal callback function for sorting by weight, id, title or name
260 public static function cmpFunc($a, $b) {
261 $cmp_order = ['weight', 'id', 'title', 'name'];
262 foreach ($cmp_order as $attribute) {
263 if (isset($a[$attribute]) && isset($b[$attribute])) {
264 if ($a[$attribute] < $b[$attribute]) {
267 elseif ($a[$attribute] > $b[$attribute]) {
269 } // else: $a and $b are equal wrt to this attribute, try next...
272 // if we get here, $a and $b are equal for all we know
273 // however, as I understand we don't want equality here: