PluginProbe ʕ •ᴥ•ʔ
Admin Columns / 4.7.19
Admin Columns v4.7.19
7.0.19 2.3.5 2.4 2.4.1 2.4.10 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.4.8 2.4.9 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.6.1 2.5.6.2 2.5.6.3 2.5.6.4 3.0 3.0.1 3.0.2 3.0.3 3.0.5 3.0.7 3.1 3.1.1 3.1.10 3.1.2 3.1.3 3.1.5 3.2.3 3.2.7 3.3.1 3.4.1 3.4.6 3.4.8 4.0.1 4.0.3 4.1.6 4.2.2 4.2.5 4.3 4.3.2 4.4.1 4.4.4 4.4.5 4.5.5 4.6.1 4.7.18 4.7.19 4.7.20 4.7.7 7.0.13 7.0.14 7.0.16 trunk 1.0 1.1 1.1.3 1.2 1.2.1 1.3 1.3.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.5.1 1.4.6 1.4.6.1 1.4.6.2 1.4.6.3 1.4.6.4 1.4.7 1.4.8 1.4.9 2.0.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.2 2.2.1 2.2.1.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.5.1 2.2.6 2.2.6.1 2.2.6.2 2.2.6.3 2.2.6.4 2.2.7 2.2.8 2.2.8.1 2.2.9 2.3.1 2.3.2 2.3.3
codepress-admin-columns / classes / Meta / Query.php
codepress-admin-columns / classes / Meta Last commit date
Query.php 6 months ago QueryMetaFactory.php 6 months ago
Query.php
522 lines
1 <?php
2
3 namespace AC\Meta;
4
5 use WP_Meta_Query;
6
7 class Query
8 {
9
10 /**
11 * @var WP_Meta_Query
12 */
13 private $query;
14
15 /**
16 * @var string
17 */
18 private $sql;
19
20 /**
21 * @var array
22 */
23 private $select = [];
24
25 /**
26 * @var string|false
27 */
28 private $count = false;
29
30 /**
31 * @var bool
32 */
33 private $distinct = false;
34
35 /**
36 * @var bool
37 */
38 private $join = false;
39
40 /**
41 * @var array
42 */
43 private $join_where = [];
44
45 /**
46 * @var array
47 */
48 private $where = [];
49
50 /**
51 * @var array
52 */
53 private $group_by = [];
54
55 /**
56 * @var array
57 */
58 private $order_by = [];
59
60 /**
61 * @var int|false
62 */
63 private $limit = false;
64
65 public function __construct(string $meta_type)
66 {
67 $this->set_query($meta_type);
68 }
69
70 /**
71 * Add a single field or multiple comma separated
72 *
73 * @param string $field e.g. id or id, meta_value
74 *
75 * @return $this
76 */
77 public function select($field)
78 {
79 $fields = explode(',', $field);
80
81 foreach ($fields as $_field) {
82 $this->select[] = trim($_field);
83 }
84
85 return $this;
86 }
87
88 /**
89 * Add a COUNT clause AS count
90 *
91 * @param string $field
92 *
93 * @return $this
94 */
95 public function count($field)
96 {
97 $this->count = $field;
98
99 return $this;
100 }
101
102 /**
103 * Group by an aggregated column.
104 * Supports: count
105 *
106 * @param string $field
107 *
108 * @return $this
109 */
110 public function group_by($field)
111 {
112 $this->group_by = $field;
113
114 return $this;
115 }
116
117 public function join($type = 'inner')
118 {
119 $this->join = strtoupper($type);
120
121 return $this;
122 }
123
124 public function left_join()
125 {
126 return $this->join('left');
127 }
128
129 /**
130 * @param string $field
131 * @param string $operator
132 * @param string|int|array $value
133 * @param string $boolean
134 *
135 * @return $this
136 * @see get_where_clause()
137 */
138 public function join_where($field, $operator = null, $value = null, $boolean = 'AND'): self
139 {
140 // set default join
141 if ( ! $this->join) {
142 $this->join();
143 }
144
145 $this->join_where[] = $this->get_where_clause($field, $operator, $value, $boolean);
146
147 return $this;
148 }
149
150 public function order_by($order_by, $order = 'asc')
151 {
152 $parts = explode(',', $order_by);
153
154 foreach ($parts as $_order_by) {
155 $this->order_by[] = [
156 'order_by' => trim($_order_by),
157 'order' => strtoupper($order),
158 ];
159 }
160
161 return $this;
162 }
163
164 public function limit($limit)
165 {
166 $this->limit = absint($limit);
167 }
168
169 public function distinct()
170 {
171 $this->distinct = true;
172
173 return $this;
174 }
175
176 /**
177 * Set a where clause
178 *
179 * @param string|array $field
180 * @param string|null $operator
181 * @param string|int|array $value
182 * @param string $boolean
183 *
184 * @return array
185 */
186 private function get_where_clause($field, string $operator = null, $value = null, string $boolean = 'AND')
187 {
188 // allows to omit operator
189 if (null === $value) {
190 $value = $operator;
191 $operator = '=';
192 }
193
194 $where = [
195 'nested' => false,
196 'boolean' => strtoupper($boolean),
197 'field' => $field,
198 'operator' => strtoupper($operator),
199 'value' => $value,
200 ];
201
202 // set default join
203 if ($field === 'post_type' && ! $this->join) {
204 $this->join();
205 }
206
207 $nested = [];
208
209 if (is_array($field)) {
210 $count = count($field);
211 for ($i = 0; $i < $count; $i++) {
212 $nested[] = array_pop($this->where);
213 }
214 }
215
216 if ($nested) {
217 $where['nested'] = true;
218 $where['field'] = array_reverse($nested);
219 }
220
221 return $where;
222 }
223
224 /**
225 * @param $field
226 * @param null $operator
227 * @param null $value
228 * @param string $boolean
229 *
230 * @return $this
231 * @see get_where_clause()
232 */
233 public function remove_where($field, $operator = null, $value = null, $boolean = 'AND')
234 {
235 $where = $this->get_where_clause($field, $operator, $value, $boolean);
236
237 foreach ($this->where as $k => $v) {
238 if ($v == $where) {
239 unset($this->where[$k]);
240 }
241 }
242
243 return $this;
244 }
245
246 /**
247 * @param $field
248 * @param null $operator
249 * @param null $value
250 * @param string $boolean
251 *
252 * @return $this
253 * @see get_where_clause()
254 */
255 public function where($field, $operator = null, $value = null, $boolean = 'AND')
256 {
257 $this->where[] = $this->get_where_clause($field, $operator, $value, $boolean);
258
259 return $this;
260 }
261
262 /**
263 * @param $field
264 * @param null $operator
265 * @param null $value
266 *
267 * @return $this
268 * @see get_where_clause()
269 */
270 public function or_where($field, $operator = null, $value = null)
271 {
272 return $this->where($field, $operator, $value, 'OR');
273 }
274
275 /**
276 * @param array $in
277 *
278 * @return $this
279 */
280 public function where_in(array $in)
281 {
282 return $this->where('id', 'in', $in);
283 }
284
285 public function where_is_null($field)
286 {
287 return $this->where($field, '', 'IS NULL');
288 }
289
290 public function where_post_type($post_type): self
291 {
292 return $this->where('post_type', '=', $post_type);
293 }
294
295 public function where_post_types(array $post_types): self
296 {
297 return $this->where('post_type', 'in', $post_types);
298 }
299
300 private function parse_field($field)
301 {
302 switch ($field) {
303 case 'id':
304 $field = $this->join ? 'pt.' . $this->query->primary_id_column : 'mt' . $this->query->meta_id_column;
305
306 break;
307 case 'meta_key':
308 case 'meta_value':
309 $field = 'mt.' . $field;
310
311 break;
312 case 'taxonomy':
313 case 'post_type':
314 $field = 'pt.' . $field;
315
316 break;
317 }
318
319 return $field;
320 }
321
322 private function parse_where(string $where, array $clauses): string
323 {
324 global $wpdb;
325
326 foreach ($clauses as $clause) {
327 if ($clause['nested']) {
328 $clause['field'][0]['boolean'] = null;
329
330 $where .= sprintf(' %s ( %s ) ', $clause['boolean'], $this->parse_where('', $clause['field']));
331 } else {
332 switch ($clause['operator']) {
333 case 'IN':
334 $clause['value'] = sprintf(' ( %s ) ', implode(', ', array_map('intval', $clause['value'])));
335
336 break;
337 default:
338 $valid_raw = ['IS NULL', 'IS NOT NULL'];
339
340 if ( ! in_array($clause['value'], $valid_raw)) {
341 $clause['value'] = $wpdb->prepare('%s', $clause['value']);
342 }
343 }
344
345 $clause['field'] = $this->parse_field($clause['field']);
346
347 $where .= implode(' ', $clause);
348 }
349 }
350
351 return $where;
352 }
353
354 public function get(): array
355 {
356 global $wpdb;
357
358 if ( ! $this->query) {
359 return [];
360 }
361
362 // parse SELECT
363 $select = 'SELECT ';
364 $select .= $this->distinct ? 'DISTINCT ' : '';
365
366 if (empty($this->select)) {
367 $this->select('id');
368 }
369
370 $fields = [];
371
372 foreach ($this->select as $field) {
373 $parsed = $this->parse_field($field);
374
375 // output 'id' in the results
376 if ('id' === $field) {
377 $parsed .= ' AS id';
378 }
379
380 $fields[] = $parsed;
381 }
382
383 if ($this->count) {
384 $fields[] = sprintf('COUNT(%s) AS count', $this->parse_field($this->count));
385 }
386
387 $select .= implode(', ', $fields);
388
389 // parse FROM
390 $from_tpl = ' FROM %s AS %s';
391
392 $from = sprintf($from_tpl, $this->query->meta_table, 'mt');
393 $join = '';
394
395 if ($this->join) {
396 $from = sprintf($from_tpl, $this->query->primary_table, 'pt');
397 $join = sprintf(
398 ' %s JOIN %s AS mt ON mt.%s = pt.%s %s',
399 $this->join,
400 $this->query->meta_table,
401 $this->query->meta_id_column,
402 $this->query->primary_id_column,
403 $this->parse_where('', $this->join_where)
404 );
405 }
406
407 // parse WHERE
408 $where = $this->parse_where(' WHERE 1=1', $this->where);
409
410 // parse GROUP BY
411 $group_by = '';
412
413 if ($this->group_by) {
414 $group_by = ' GROUP BY ' . $this->parse_field($this->group_by);
415 }
416
417 // parse ORDER BY
418 $order_by = '';
419
420 if ( ! empty($this->order_by)) {
421 $order_by_clauses = [];
422
423 foreach ($this->order_by as $order_by_clause) {
424 $order_by_clauses[] = $this->parse_field(
425 $order_by_clause['order_by']
426 ) . ' ' . $order_by_clause['order'];
427 }
428
429 $order_by = ' ORDER BY ' . implode(', ', $order_by_clauses);
430 }
431
432 $limit = '';
433
434 if ($this->limit) {
435 $limit = ' LIMIT ' . $this->limit;
436 }
437
438 // build query and store it
439 $sql = $select . $from . $join . $where . $group_by . $order_by . $limit;
440
441 $this->set_sql($sql);
442
443 $results = $wpdb->get_results($sql);
444
445 if ( ! is_array($results)) {
446 return [];
447 }
448
449 $return = $results;
450
451 if (count($fields) === 1) {
452 $return = [];
453 $field = $this->select[0];
454
455 foreach ($results as $result) {
456 $return[] = $result->$field;
457 }
458 }
459
460 return $return;
461 }
462
463 /**
464 * Return last sql that was queried
465 */
466 public function get_sql(): string
467 {
468 $sql = preg_replace('/ +/', ' ', $this->sql);
469 $sql = preg_replace(
470 '/(SELECT|FROM|LEFT|INNER|WHERE|(AND|OR) \(|(AND|OR) (?!\()|ORDER BY|GROUP BY|LIMIT)/',
471 "\n$1",
472 $sql
473 );
474
475 return $sql . "\n";
476 }
477
478 private function set_sql(string $sql): void
479 {
480 $this->sql = $sql;
481 }
482
483 public function get_query(): WP_Meta_Query
484 {
485 return $this->query;
486 }
487
488 private function set_query(string $type)
489 {
490 global $wpdb;
491
492 switch ($type) {
493 case 'user':
494 $table = $wpdb->users;
495 $id = 'ID';
496
497 break;
498 case 'comment':
499 $table = $wpdb->comments;
500 $id = 'comment_ID';
501
502 break;
503 case 'post':
504 $table = $wpdb->posts;
505 $id = 'ID';
506
507 break;
508 case 'term':
509 $table = $wpdb->terms;
510 $id = 'term_id';
511
512 break;
513
514 default:
515 return;
516 }
517
518 $this->query = new WP_Meta_Query();
519 $this->query->get_sql($type, $table, $id);
520 }
521
522 }