PluginProbe ʕ •ᴥ•ʔ
Admin Columns / 7.0.19
Admin Columns v7.0.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 1 month ago QueryMetaFactory.php 1 month ago
Query.php
452 lines
1 <?php
2
3 namespace AC\Meta;
4
5 use WP_Meta_Query;
6
7 class Query
8 {
9
10 private ?WP_Meta_Query $query = null;
11
12 private string $sql;
13
14 private array $select = [];
15
16 private string $count = '';
17
18 private bool $distinct = false;
19
20 private string $join = '';
21
22 private array $join_where = [];
23
24 private array $where = [];
25
26 private string $group_by = '';
27
28 private array $order_by = [];
29
30 private int $limit = 0;
31
32 public function __construct(string $meta_type)
33 {
34 $this->set_query($meta_type);
35 }
36
37 /**
38 * Add a single field or multiple comma separated
39 *
40 * @param string $field e.g. id or id, meta_value
41 *
42 * @return $this
43 */
44 public function select(string $field): self
45 {
46 $fields = explode(',', $field);
47
48 foreach ($fields as $_field) {
49 $this->select[] = trim($_field);
50 }
51
52 return $this;
53 }
54
55 /**
56 * Add a COUNT clause AS count
57 */
58 public function count(string $field): self
59 {
60 $this->count = $field;
61
62 return $this;
63 }
64
65 /**
66 * Group by an aggregated column.
67 * Supports: count
68 *
69 * @param string $field
70 *
71 * @return $this
72 */
73 public function group_by(string $field): self
74 {
75 $this->group_by = $field;
76
77 return $this;
78 }
79
80 public function join(string $type = 'inner'): self
81 {
82 $this->join = strtoupper($type);
83
84 return $this;
85 }
86
87 /**
88 * @see get_where_clause()
89 */
90 public function join_where(string $field, ?string $operator = null, $value = null, string $boolean = 'AND'): self
91 {
92 // set default join
93 if ( ! $this->join) {
94 $this->join();
95 }
96
97 $this->join_where[] = $this->get_where_clause($field, $operator, $value, $boolean);
98
99 return $this;
100 }
101
102 public function order_by(string $order_by, string $order = 'asc'): self
103 {
104 $parts = explode(',', $order_by);
105
106 foreach ($parts as $_order_by) {
107 $this->order_by[] = [
108 'order_by' => trim($_order_by),
109 'order' => strtoupper($order),
110 ];
111 }
112
113 return $this;
114 }
115
116 public function limit(int $limit): void
117 {
118 $this->limit = absint($limit);
119 }
120
121 public function distinct(): self
122 {
123 $this->distinct = true;
124
125 return $this;
126 }
127
128 /**
129 * Set a where clause
130 *
131 * @param string|array $field
132 * @param string|null $operator
133 * @param string|int|array $value
134 * @param string $boolean
135 *
136 * @return array
137 */
138 private function get_where_clause($field, ?string $operator = null, $value = null, string $boolean = 'AND'): array
139 {
140 // allows to omit operator
141 if (null === $value) {
142 $value = $operator;
143 $operator = '=';
144 }
145
146 $where = [
147 'nested' => false,
148 'boolean' => strtoupper($boolean),
149 'field' => $field,
150 'operator' => strtoupper($operator),
151 'value' => $value,
152 ];
153
154 // set default join
155 if ($field === 'post_type' && ! $this->join) {
156 $this->join();
157 }
158
159 $nested = [];
160
161 if (is_array($field)) {
162 $count = count($field);
163 for ($i = 0; $i < $count; $i++) {
164 $nested[] = array_pop($this->where);
165 }
166 }
167
168 if ($nested) {
169 $where['nested'] = true;
170 $where['field'] = array_reverse($nested);
171 }
172
173 return $where;
174 }
175
176 /**
177 * @see get_where_clause()
178 */
179 public function remove_where($field, ?string $operator = null, $value = null, string $boolean = 'AND'): self
180 {
181 $where = $this->get_where_clause($field, $operator, $value, $boolean);
182
183 foreach ($this->where as $k => $v) {
184 if ($v == $where) {
185 unset($this->where[$k]);
186 }
187 }
188
189 return $this;
190 }
191
192 /**
193 * @see get_where_clause()
194 */
195 public function where($field, ?string $operator = null, $value = null, string $boolean = 'AND'): self
196 {
197 $this->where[] = $this->get_where_clause($field, $operator, $value, $boolean);
198
199 return $this;
200 }
201
202 /**
203 * @see get_where_clause()
204 */
205 public function or_where($field, ?string $operator = null, $value = null): self
206 {
207 return $this->where($field, $operator, $value, 'OR');
208 }
209
210 public function where_in(array $in): self
211 {
212 return $this->where('id', 'in', $in);
213 }
214
215 public function where_is_null($field): self
216 {
217 return $this->where($field, '', 'IS NULL');
218 }
219
220 public function where_post_type(string $post_type): self
221 {
222 return $this->where('post_type', '=', $post_type);
223 }
224
225 public function where_post_types(array $post_types): self
226 {
227 return $this->where('post_type', 'in', $post_types);
228 }
229
230 private function parse_field(string $field): string
231 {
232 switch ($field) {
233 case 'id':
234 $field = $this->join ? 'pt.' . $this->query->primary_id_column : 'mt.' . $this->query->meta_id_column;
235
236 break;
237 case 'meta_key':
238 case 'meta_value':
239 $field = 'mt.' . $field;
240
241 break;
242 case 'taxonomy':
243 case 'post_type':
244 $field = 'pt.' . $field;
245
246 break;
247 }
248
249 return $field;
250 }
251
252 private function parse_where(string $where, array $clauses): string
253 {
254 global $wpdb;
255
256 foreach ($clauses as $clause) {
257 if ($clause['nested']) {
258 $clause['field'][0]['boolean'] = null;
259
260 $where .= sprintf(' %s ( %s ) ', $clause['boolean'], $this->parse_where('', $clause['field']));
261 } else {
262 switch ($clause['operator']) {
263 case 'IN':
264 $clause['value'] = sprintf(' ( %s ) ', implode(', ', array_map('intval', $clause['value'])));
265
266 break;
267 default:
268 $valid_raw = ['IS NULL', 'IS NOT NULL'];
269
270 if ( ! in_array($clause['value'], $valid_raw)) {
271 $clause['value'] = $wpdb->prepare('%s', $clause['value']);
272 }
273 }
274
275 $clause['field'] = $this->parse_field($clause['field']);
276
277 $where .= implode(' ', $clause);
278 }
279 }
280
281 return $where;
282 }
283
284 public function get(): array
285 {
286 global $wpdb;
287
288 if ( ! $this->query) {
289 return [];
290 }
291
292 // parse SELECT
293 $select = 'SELECT ';
294 $select .= $this->distinct ? 'DISTINCT ' : '';
295
296 if (empty($this->select)) {
297 $this->select('id');
298 }
299
300 $fields = [];
301
302 foreach ($this->select as $field) {
303 $parsed = $this->parse_field($field);
304
305 // output 'id' in the results
306 if ('id' === $field) {
307 $parsed .= ' AS id';
308 }
309
310 $fields[] = $parsed;
311 }
312
313 if ($this->count) {
314 $fields[] = sprintf('COUNT(%s) AS count', $this->parse_field($this->count));
315 }
316
317 $select .= implode(', ', $fields);
318
319 // parse FROM
320 $from_tpl = ' FROM %s AS %s';
321
322 $from = sprintf($from_tpl, $this->query->meta_table, 'mt');
323 $join = '';
324
325 if ($this->join) {
326 $from = sprintf($from_tpl, $this->query->primary_table, 'pt');
327 $join = sprintf(
328 ' %s JOIN %s AS mt ON mt.%s = pt.%s %s',
329 $this->join,
330 $this->query->meta_table,
331 $this->query->meta_id_column,
332 $this->query->primary_id_column,
333 $this->parse_where('', $this->join_where)
334 );
335 }
336
337 // parse WHERE
338 $where = $this->parse_where(' WHERE 1=1', $this->where);
339
340 // parse GROUP BY
341 $group_by = '';
342
343 if ($this->group_by) {
344 $group_by = ' GROUP BY ' . $this->parse_field($this->group_by);
345 }
346
347 // parse ORDER BY
348 $order_by = '';
349
350 if ( ! empty($this->order_by)) {
351 $order_by_clauses = [];
352
353 foreach ($this->order_by as $order_by_clause) {
354 $order_by_clauses[] = $this->parse_field(
355 $order_by_clause['order_by']
356 ) . ' ' . $order_by_clause['order'];
357 }
358
359 $order_by = ' ORDER BY ' . implode(', ', $order_by_clauses);
360 }
361
362 $limit = '';
363
364 if ($this->limit) {
365 $limit = ' LIMIT ' . $this->limit;
366 }
367
368 // build query and store it
369 $sql = $select . $from . $join . $where . $group_by . $order_by . $limit;
370
371 $this->set_sql($sql);
372
373 $results = $wpdb->get_results($sql);
374
375 if ( ! is_array($results)) {
376 return [];
377 }
378
379 $return = $results;
380
381 if (count($fields) === 1) {
382 $return = [];
383 $field = $this->select[0];
384
385 foreach ($results as $result) {
386 $return[] = $result->$field;
387 }
388 }
389
390 return $return;
391 }
392
393 /**
394 * Return last SQL query that was queried
395 */
396 public function get_sql(): string
397 {
398 $sql = preg_replace('/ +/', ' ', $this->sql);
399 $sql = preg_replace(
400 '/(SELECT|FROM|LEFT|INNER|WHERE|(AND|OR) \(|(AND|OR) (?!\()|ORDER BY|GROUP BY|LIMIT)/',
401 "\n$1",
402 $sql
403 );
404
405 return $sql . "\n";
406 }
407
408 private function set_sql(string $sql): void
409 {
410 $this->sql = $sql;
411 }
412
413 public function get_query(): WP_Meta_Query
414 {
415 return $this->query;
416 }
417
418 private function set_query(string $type): void
419 {
420 global $wpdb;
421
422 switch ($type) {
423 case 'user':
424 $table = $wpdb->users;
425 $id = 'ID';
426
427 break;
428 case 'comment':
429 $table = $wpdb->comments;
430 $id = 'comment_ID';
431
432 break;
433 case 'post':
434 $table = $wpdb->posts;
435 $id = 'ID';
436
437 break;
438 case 'term':
439 $table = $wpdb->terms;
440 $id = 'term_id';
441
442 break;
443
444 default:
445 return;
446 }
447
448 $this->query = new WP_Meta_Query();
449 $this->query->get_sql($type, $table, $id);
450 }
451
452 }