Merge pull request #15986 from civicrm/5.20
[civicrm-core.git] / CRM / Utils / Sort.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035 11
6a488035
TO
12/**
13 *
b8c71ffa 14 * Base class to provide generic sort functionality.
15 *
16 * Note that some ideas have been borrowed from the drupal tablesort.inc code.
17 *
18 * Also note that since the Pager and Sort class are similar, do match the function names
6a488035
TO
19 * if introducing additional functionality
20 *
21 * @package CRM
ca5cec67 22 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
23 */
24class CRM_Utils_Sort {
25
26 /**
100fef9d 27 * Constants to determine what direction each variable
6a488035
TO
28 * is to be sorted
29 *
30 * @var int
31 */
7da04cde 32 const ASCENDING = 1, DESCENDING = 2, DONTCARE = 4,
6a488035 33
353ffa53
TO
34 /**
35 * The name for the sort GET/POST param
36 *
37 * @var string
38 */
39 SORT_ID = 'crmSID', SORT_DIRECTION = 'crmSortDirection', SORT_ORDER = 'crmSortOrder';
6a488035
TO
40
41 /**
100fef9d 42 * Name of the sort function. Used to isolate session variables
6a488035
TO
43 * @var string
44 */
45 protected $_name;
46
47 /**
100fef9d 48 * Array of variables that influence the query
6a488035
TO
49 *
50 * @var array
51 */
52 public $_vars;
53
54 /**
100fef9d 55 * The newly formulated base url to be used as links
6a488035
TO
56 * for various table elements
57 *
58 * @var string
59 */
60 protected $_link;
61
62 /**
100fef9d 63 * What's the name of the sort variable in a REQUEST
6a488035
TO
64 *
65 * @var string
66 */
67 protected $_urlVar;
68
69 /**
70 * What variable are we currently sorting on
71 *
72 * @var string
73 */
74 protected $_currentSortID;
75
76 /**
77 * What direction are we sorting on
78 *
79 * @var string
80 */
81 protected $_currentSortDirection;
82
83 /**
84 * The output generated for the current form
85 *
86 * @var array
87 */
88 public $_response;
89
90 /**
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
94 *
77855840
TO
95 * @param mixed $vars
96 * Assoc array as described above.
97 * @param string $defaultSortOrder
98 * Order to sort.
6a488035 99 *
f4aaa82a 100 * @return \CRM_Utils_Sort
450f494d 101 */
00be9182 102 public function __construct(&$vars, $defaultSortOrder = NULL) {
be2fb01f
CW
103 $this->_vars = [];
104 $this->_response = [];
6a488035
TO
105
106 foreach ($vars as $weight => $value) {
be2fb01f 107 $this->_vars[$weight] = [
a33b83c5 108 'name' => CRM_Utils_Type::validate($value['sort'], 'MysqlColumnNameOrAlias'),
6a488035
TO
109 'direction' => CRM_Utils_Array::value('direction', $value),
110 'title' => $value['name'],
be2fb01f 111 ];
6a488035
TO
112 }
113
114 $this->_currentSortID = 1;
115 if (isset($this->_vars[$this->_currentSortID])) {
116 $this->_currentSortDirection = $this->_vars[$this->_currentSortID]['direction'];
117 }
118 $this->_urlVar = self::SORT_ID;
5340376b 119 $this->_link = CRM_Utils_System::makeURL($this->_urlVar, TRUE);
6a488035
TO
120
121 $this->initialize($defaultSortOrder);
122 }
123
124 /**
fe482240 125 * Function returns the string for the order by clause.
6a488035 126 *
a6c01b45
CW
127 * @return string
128 * the order by clause
6a488035 129 */
00be9182 130 public function orderBy() {
a7488080 131 if (empty($this->_vars[$this->_currentSortID])) {
6a488035
TO
132 return '';
133 }
134
135 if ($this->_vars[$this->_currentSortID]['direction'] == self::ASCENDING ||
136 $this->_vars[$this->_currentSortID]['direction'] == self::DONTCARE
137 ) {
138 $this->_vars[$this->_currentSortID]['name'] = str_replace(' ', '_', $this->_vars[$this->_currentSortID]['name']);
668c2561 139 return CRM_Utils_Type::escape($this->_vars[$this->_currentSortID]['name'], 'MysqlColumnNameOrAlias') . ' asc';
6a488035
TO
140 }
141 else {
142 $this->_vars[$this->_currentSortID]['name'] = str_replace(' ', '_', $this->_vars[$this->_currentSortID]['name']);
668c2561 143 return CRM_Utils_Type::escape($this->_vars[$this->_currentSortID]['name'], 'MysqlColumnNameOrAlias') . ' desc';
6a488035
TO
144 }
145 }
146
147 /**
fe482240 148 * Create the sortID string to be used in the GET param.
6a488035 149 *
77855840
TO
150 * @param int $index
151 * The field index.
152 * @param int $dir
153 * The direction of the sort.
6a488035 154 *
a6c01b45
CW
155 * @return string
156 * the string to append to the url
6a488035 157 */
00be9182 158 public static function sortIDValue($index, $dir) {
6a488035
TO
159 return ($dir == self::DESCENDING) ? $index . '_d' : $index . '_u';
160 }
161
162 /**
fe482240 163 * Init the sort ID values in the object.
6a488035 164 *
77855840
TO
165 * @param string $defaultSortOrder
166 * The sort order to use by default.
6a488035 167 */
00be9182 168 public function initSortID($defaultSortOrder) {
6a488035
TO
169 $url = CRM_Utils_Array::value(self::SORT_ID, $_GET, $defaultSortOrder);
170
171 if (empty($url)) {
172 return;
173 }
174
175 list($current, $direction) = explode('_', $url);
176
b44e3f84 177 // if current is weird and does not exist in the vars array, skip
6a488035
TO
178 if (!array_key_exists($current, $this->_vars)) {
179 return;
180 }
181
182 if ($direction == 'u') {
183 $direction = self::ASCENDING;
184 }
185 elseif ($direction == 'd') {
186 $direction = self::DESCENDING;
187 }
188 else {
189 $direction = self::DONTCARE;
190 }
191
192 $this->_currentSortID = $current;
193 $this->_currentSortDirection = $direction;
194 $this->_vars[$current]['direction'] = $direction;
195 }
196
197 /**
fe482240 198 * Init the object.
6a488035 199 *
77855840
TO
200 * @param string $defaultSortOrder
201 * The sort order to use by default.
6a488035 202 */
00be9182 203 public function initialize($defaultSortOrder) {
6a488035
TO
204 $this->initSortID($defaultSortOrder);
205
be2fb01f 206 $this->_response = [];
6a488035
TO
207
208 $current = $this->_currentSortID;
209 foreach ($this->_vars as $index => $item) {
210 $name = $item['name'];
be2fb01f 211 $this->_response[$name] = [];
6a488035
TO
212
213 $newDirection = ($item['direction'] == self::ASCENDING) ? self::DESCENDING : self::ASCENDING;
214
215 if ($current == $index) {
216 if ($item['direction'] == self::ASCENDING) {
217 $class = 'sorting_asc';
218 }
219 else {
220 $class = 'sorting_desc';
221 }
222 }
223 else {
224 $class = 'sorting';
225 }
226
227 $this->_response[$name]['link'] = '<a href="' . $this->_link . $this->sortIDValue($index, $newDirection) . '" class="' . $class . '">' . $item['title'] . '</a>';
228 }
229 }
230
231 /**
fe482240 232 * Getter for currentSortID.
6a488035 233 *
a6c01b45
CW
234 * @return int
235 * returns of the current sort id
6a488035 236 */
00be9182 237 public function getCurrentSortID() {
6a488035
TO
238 return $this->_currentSortID;
239 }
240
241 /**
fe482240 242 * Getter for currentSortDirection.
6a488035 243 *
a6c01b45
CW
244 * @return int
245 * returns of the current sort direction
6a488035 246 */
00be9182 247 public function getCurrentSortDirection() {
6a488035
TO
248 return $this->_currentSortDirection;
249 }
250
251 /**
ac51fa38 252 * Universal callback function for sorting by weight, id, title or name
6a488035 253 *
f4aaa82a
EM
254 * @param $a
255 * @param $b
256 *
a6c01b45
CW
257 * @return int
258 * (-1 or 1)
6a488035 259 */
00be9182 260 public static function cmpFunc($a, $b) {
be2fb01f 261 $cmp_order = ['weight', 'id', 'title', 'name'];
ac51fa38 262 foreach ($cmp_order as $attribute) {
263 if (isset($a[$attribute]) && isset($b[$attribute])) {
264 if ($a[$attribute] < $b[$attribute]) {
265 return -1;
0db6c3e1
TO
266 }
267 elseif ($a[$attribute] > $b[$attribute]) {
ac51fa38 268 return 1;
269 } // else: $a and $b are equal wrt to this attribute, try next...
1d45f936 270 }
e39d4fad 271 }
50bfb460 272 // if we get here, $a and $b are equal for all we know
ac51fa38 273 // however, as I understand we don't want equality here:
274 return -1;
6a488035 275 }
96025800 276
6a488035 277}