PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 3.5.0
Tutor LMS – eLearning and online course solution v3.5.0
3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / helpers / QueryHelper.php
tutor / helpers Last commit date
DateTimeHelper.php 1 year ago HttpHelper.php 1 year ago PluginInstaller.php 1 year ago QueryHelper.php 1 year ago SessionHelper.php 3 years ago ValidationHelper.php 1 year ago
QueryHelper.php
947 lines
1 <?php
2 /**
3 * Query helper class contains static helper methods to perform basic
4 * operations
5 *
6 * @package Tutor\Helper
7 * @since v2.0.7
8 */
9
10 namespace Tutor\Helpers;
11
12 /**
13 * Do the common db operations through helper
14 * methods
15 */
16 class QueryHelper {
17
18 /**
19 * Insert data in the table
20 *
21 * @since 2.0.7
22 * @since 3.2.0 sanitize_mapping param added to override sanitize function to specific keys.
23 *
24 * @param string $table table name.
25 * @param array $data | data to insert in the table.
26 * @param array $sanitize_mapping sanitize mapping.
27 *
28 * @return int inserted id.
29 *
30 * @throws \Exception Database error if occur.
31 */
32 public static function insert( string $table, array $data, array $sanitize_mapping = array() ): int {
33 global $wpdb;
34
35 $data = \TUTOR\Input::sanitize_array( $data, $sanitize_mapping );
36
37 $insert = $wpdb->insert(
38 $table,
39 $data
40 );
41
42 if ( $wpdb->last_error ) {
43 throw new \Exception( $wpdb->last_error );
44 }
45
46 return $insert ? $wpdb->insert_id : 0;
47 }
48
49 /**
50 * Update data
51 *
52 * @since 2.0.7
53 * @since 3.2.0 IN clause support added.
54 *
55 * @param string $table table name.
56 * @param array $data | data to update in the table.
57 * @param array $where | condition array.
58 *
59 * @return bool true on success false on failure
60 */
61 public static function update( string $table, array $data, array $where ): bool {
62 global $wpdb;
63
64 $set_clause = self::prepare_set_clause( $data );
65 $where_clause = self::build_where_clause( $where );
66
67 // phpcs:ignore
68 $query = $wpdb->prepare( "UPDATE {$table} {$set_clause} WHERE {$where_clause} AND 1 = %d", 1 );
69
70 // phpcs:ignore
71 $wpdb->query( $query );
72
73 if ( $wpdb->last_error ) {
74 error_log( $wpdb->last_error );
75 return false;
76 }
77
78 return true;
79 }
80
81 /**
82 * Delete rows from table
83 *
84 * @param string $table table name.
85 * @param array $where key value pairs.Where key is the name of
86 * column & value is the value to match.
87 * For ex: [ 'id' => 1 ].
88 *
89 * @since v2.0.7
90 */
91 public static function delete( string $table, array $where ): bool {
92 global $wpdb;
93 $delete = $wpdb->delete(
94 $table,
95 $where
96 );
97 return $delete ? true : false;
98 }
99
100 /**
101 * Delete rows from table
102 *
103 * @since 3.0.0
104 *
105 * @param string $table table name.
106 * @param array $ids array of ids.
107 *
108 * @see prepare_in_clause
109 *
110 * @throws \Exception Throw database error if occurred.
111 *
112 * @return true on success
113 */
114 public static function bulk_delete_by_ids( string $table, array $ids ): bool {
115 global $wpdb;
116
117 $ids = self::prepare_in_clause( $ids );
118 //phpcs:ignore --ids already sanitized.
119 $wpdb->query( "DELETE FROM {$table} WHERE id IN ( $ids )");
120
121 if ( $wpdb->last_error ) {
122 throw new \Exception( $wpdb->last_error );
123 }
124
125 return true;
126 }
127
128 /**
129 * Clean everything from table
130 *
131 * @since v2.0.7
132 *
133 * @param string $table table name.
134 *
135 * @return bool
136 */
137 public static function table_clean( string $table ): bool {
138 global $wpdb;
139 $delete = $wpdb->query(
140 //phpcs:ignore
141 $wpdb->prepare( "DELETE FROM {$table} WHERE 1 = %d", 1 )
142 );
143 return $delete ? true : false;
144 }
145
146 /**
147 * Insert multiple rows without knowing key value
148 *
149 * @since v2.0.7
150 *
151 * @param string $table table name.
152 * @param array $request two dimensional array
153 * for ex: [ [id => 1], [id => 2] ].
154 *
155 * @return mixed wpdb response true or int on success, false on failure.
156 * @throws \Exception If error occur.
157 */
158 public static function insert_multiple_rows( $table, $request ) {
159 global $wpdb;
160 $column_keys = '';
161 $column_values = '';
162 $sql = '';
163 $last_key = array_key_last( $request );
164 $first_key = array_key_first( $request );
165 foreach ( $request as $k => $value ) {
166 $keys = array_keys( $value );
167
168 // Prepare column keys & values.
169 foreach ( $keys as $v ) {
170 $column_keys .= sanitize_key( $v ) . ',';
171 $sanitize_value = is_null( $value[ $v ] ) ? $value[ $v ] : sanitize_text_field( $value[ $v ] );
172 $column_values .= is_numeric( $sanitize_value ) ? $sanitize_value . ',' : "'$sanitize_value'" . ',';
173 }
174 // Trim trailing comma.
175 $column_keys = rtrim( $column_keys, ',' );
176 $column_values = rtrim( $column_values, ',' );
177 if ( $first_key === $k ) {
178 $sql .= "INSERT INTO {$table} ($column_keys) VALUES ($column_values)";
179 if ( count( $request ) > 1 ) {
180 $sql .= ',';
181 }
182 } elseif ( $last_key == $k ) {
183 $sql .= "($column_values)";
184 } else {
185 $sql .= "($column_values),";
186 }
187
188 // Reset keys & values to avoid duplication.
189 $column_keys = '';
190 $column_values = '';
191 }
192
193 $wpdb->query( $sql );//phpcs:ignore
194
195 // If error occurred then throw new exception.
196 if ( $wpdb->last_error ) {
197 throw new \Exception( $wpdb->last_error );
198 }
199
200 return true;
201 }
202
203 /**
204 * Make tge where clause base on its column operator and values.
205 *
206 * If the operator is IN then make the clause like `WHERE column_name IN (value1, value2, ...)`
207 * Otherwise the clause would be `WHERE column_name = 'value'`
208 *
209 * @since 3.0.0
210 *
211 * @param array $where The where clause array. e.g. array( 'id', 'IN', array(1, 2, 3) ) or array( 'id', '=', 1 ).
212 *
213 * @return string
214 */
215 public static function make_clause( array $where ) {
216 list ( $field, $operator, $value ) = $where;
217
218 $upper_operator = strtoupper( $operator );
219 if ( in_array( $upper_operator, array( 'IN', 'NOT IN' ), true ) ) {
220 $value = '(' . self::prepare_in_clause( $value ) . ')';
221 }
222
223 return "{$field} {$upper_operator} {$value}";
224 }
225
226 /**
227 * Check operator is supported.
228 *
229 * @since 3.5.0
230 *
231 * @param string $operator operator like =, !=, > , < etc.
232 *
233 * @return boolean
234 */
235 public static function is_support_operator( $operator ) {
236 $operator = strtoupper( $operator );
237
238 return in_array(
239 $operator,
240 array(
241 '=',
242 '!=',
243 '<>',
244 '>',
245 '<',
246 '>=',
247 '<=',
248 'LIKE',
249 'NOT LIKE',
250 'IN',
251 'NOT IN',
252 'IS',
253 'IS NOT',
254 'BETWEEN',
255 'NOT BETWEEN',
256 ),
257 true
258 );
259 }
260
261 /**
262 * Build where clause string
263 *
264 * @since 2.0.9
265 * @since 3.0.0 Null value support added, if need to check with null: [name => 'null']
266 * @since 3.5.0 All common SQL comparison operators support added.
267 * $where = array(
268 * 'id' => ['BETWEEN', [10, 20]],
269 * 'status' => ['!=', 'draft'],
270 * 'email' => ['LIKE', '%@gmail.com'],
271 * 'type' => ['NOT IN', ['test', 'sample']],
272 * 'age' => ['>=', 18],
273 * 'active' => true,
274 * 'deleted_at' => 'null',
275 * 'role' => 'editor',
276 * )
277 *
278 * @param array $where assoc array with field and value.
279 *
280 * @return string
281 */
282 public static function build_where_clause( array $where ) {
283 $arr = array();
284 foreach ( $where as $field => $value ) {
285 if ( is_array( $value ) && isset( $value[0] ) && is_string( $value[0] ) && self::is_support_operator( $value[0] ) ) {
286 $operator = strtoupper( $value[0] );
287 $val = $value[1];
288 switch ( $operator ) {
289 case 'IN':
290 case 'NOT IN':
291 if ( is_array( $val ) ) {
292 $clause = array( $field, $operator, $val );
293 }
294 break;
295
296 case 'BETWEEN':
297 case 'NOT BETWEEN':
298 if ( is_array( $val ) && count( $val ) === 2 ) {
299 $val1 = is_numeric( $val[0] ) ? $val[0] : "'" . $val[0] . "'";
300 $val2 = is_numeric( $val[1] ) ? $val[1] : "'" . $val[1] . "'";
301 $clause = array( $field, $operator, "{$val1} AND {$val2}" );
302 }
303 break;
304
305 case 'IS':
306 case 'IS NOT':
307 $val = strtoupper( $val ) === 'NULL' ? 'NULL' : "'" . $val . "'";
308 $clause = array( $field, $operator, $val );
309 break;
310
311 default: // =, !=, <, >, <=, >=, LIKE, NOT LIKE, <>
312 $val = is_numeric( $val ) ? $val : "'" . $val . "'";
313 $clause = array( $field, $operator, $val );
314 break;
315 }
316 } elseif ( is_array( $value ) ) {
317 $clause = array( $field, 'IN', $value );
318 } else {
319 if ( 'null' === strtolower( $value ) ) {
320 $clause = array( $field, 'IS', 'NULL' );
321 } else {
322 $value = is_numeric( $value ) ? $value : "'" . $value . "'";
323 $clause = array( $field, '=', $value );
324 }
325 }
326
327 $arr[] = self::make_clause( $clause );
328 }
329
330 return implode( ' AND ', $arr );
331 }
332
333 /**
334 * Build like clause string with or
335 *
336 * @since 1.0.0
337 *
338 * @param array $where assoc array with field and value.
339 * @param string $relation default is OR.
340 *
341 * @return string
342 */
343 public static function build_like_clause( array $where, $relation = 'OR' ) {
344 global $wpdb;
345
346 $like_conditions = array();
347
348 foreach ( $where as $column_name => $term ) {
349 //phpcs:ignore
350 $like_conditions[] = $wpdb->prepare( "$column_name LIKE %s", '%' . $wpdb->esc_like( $term ) . '%' );
351 }
352
353 $where_clause = implode( ' OR ', $like_conditions );
354
355 return $where_clause;
356 }
357
358 /**
359 * Sanitize assoc array
360 *
361 * @param array $array an assoc array.
362 * @return array
363 *
364 * @since 2.0.9
365 */
366 private static function sanitize_assoc_array( array $array ) {
367 return array_map(
368 function ( $value ) {
369 return sanitize_text_field( $value );
370 },
371 $array
372 );
373 }
374
375 /**
376 * Delete comment with associate meta data
377 *
378 * @param array $where associative array with field and value.
379 * Example: array( 'comment_type' => 'comment', 'comment_id' => 1 ).
380 * @return bool
381 *
382 * @since 2.0.9
383 */
384 public static function delete_comment_with_meta( array $where ) {
385 if ( count( $where ) === 0 || ! tutor_utils()->is_assoc( $where ) ) {
386 return false;
387 }
388
389 $where = self::build_where_clause( self::sanitize_assoc_array( $where ) );
390
391 global $wpdb;
392 $ids = $wpdb->get_col( "SELECT comment_id FROM {$wpdb->comments} WHERE {$where}" );//phpcs:ignore
393
394 if ( is_array( $ids ) && count( $ids ) ) {
395 $ids_str = "'" . implode( "','", $ids ) . "'";
396 // delete comment metas.
397 $wpdb->query( "DELETE FROM {$wpdb->commentmeta} WHERE comment_id IN({$ids_str}) " );//phpcs:ignore
398 // delete comment.
399 $wpdb->query( "DELETE FROM {$wpdb->comments} WHERE {$where}" );//phpcs:ignore
400
401 return true;
402 }
403
404 return false;
405 }
406
407 /**
408 * Delete post with associate meta data
409 *
410 * @param array $where associative array with field and value.
411 * Example: array( 'post_type' => 'post', 'id' => 1 ).
412 * @return bool
413 *
414 * @since 2.0.9
415 */
416 public static function delete_post_with_meta( array $where ) {
417 if ( count( $where ) === 0 || ! tutor_utils()->is_assoc( $where ) ) {
418 return false;
419 }
420
421 $where = self::build_where_clause( self::sanitize_assoc_array( $where ) );
422
423 global $wpdb;
424 $ids = $wpdb->get_col( "SELECT id FROM {$wpdb->posts} WHERE {$where}" );//phpcs:ignore
425
426 if ( is_array( $ids ) && count( $ids ) ) {
427 $ids_str = "'" . implode( "','", $ids ) . "'";
428 // delete post metas.
429 $wpdb->query( "DELETE FROM {$wpdb->postmeta} WHERE post_id IN({$ids_str}) " );//phpcs:ignore
430 // delete post.
431 $wpdb->query( "DELETE FROM {$wpdb->posts} WHERE {$where}" );//phpcs:ignore
432
433 return true;
434 }
435
436 return false;
437 }
438
439 /**
440 * Get a single row from any table with where clause
441 *
442 * @param string $table table name with prefix.
443 *
444 * @param array $where assoc_array. For ex: [col_name => value ].
445 * @param string $order_by order by column name.
446 * @param string $order DESC or ASC, default is DESC.
447 * @param string $output expected output type, default is object.
448 *
449 * @return mixed based on output param, default object
450 */
451 public static function get_row( string $table, array $where, string $order_by, string $order = 'DESC', string $output = 'OBJECT' ) {
452 global $wpdb;
453
454 $where_clause = self::build_where_clause( $where );
455
456 //phpcs:disable
457 $query = $wpdb->prepare(
458 "SELECT *
459 FROM {$table}
460 WHERE {$where_clause}
461 ORDER BY {$order_by} {$order}
462 LIMIT %d
463 ",
464 1
465 );
466
467 return $wpdb->get_row(
468 $query,
469 $output
470 );
471 //phpcs:enable
472 }
473
474 /**
475 * Get all row from any table with where clause
476 *
477 * @since 2.2.1
478 * @since 3.0.0 added support for -1 value in the limit parameter.
479 *
480 * @param string $table table name with prefix.
481 *
482 * @param array $where assoc_array. For ex: [col_name => value ].
483 * @param string $order_by order by column name.
484 * @param int $limit default is 1000, -1 for no limit.
485 * @param string $order DESC or ASC, default is DESC.
486 * @param string $output expected output type, default is object.
487 *
488 * @return mixed based on output param, default object
489 */
490 public static function get_all( string $table, array $where, string $order_by, $limit = 1000, string $order = 'DESC', string $output = 'OBJECT' ) {
491 global $wpdb;
492
493 $where_clause = self::build_where_clause( $where );
494 $limit = (int) sanitize_text_field( $limit );
495 $limit_clause = ( -1 === $limit ) ? '' : 'LIMIT ' . $limit;
496
497 //phpcs:disable
498 $query = "SELECT *
499 FROM {$table}
500 WHERE {$where_clause}
501 ORDER BY {$order_by} {$order}
502 {$limit_clause}";
503
504 return $wpdb->get_results(
505 $query,
506 $output
507 );
508 //phpcs:enable
509 }
510
511 /**
512 * Update multiple rows by using where in
513 * clause
514 *
515 * @since v2.1.0
516 *
517 * @param string $table table name.
518 * @param array $data assoc_array data to update
519 * ex: [id => 2, name => 'john' ].
520 * @param string $where_in comma separated values, ex: 1,2,3.
521 * @param string $where_col default is ID but could be other.
522 *
523 * @return bool true on success, false on failure
524 */
525 public static function update_where_in( string $table, array $data, string $where_in, string $where_col = 'ID' ) {
526 global $wpdb;
527 if ( empty( $where_in ) || empty( $where_col ) ) {
528 return false;
529 }
530 $set_clause = self::prepare_set_clause( $data );
531 if ( '' === $set_clause ) {
532 return false;
533 }
534 // @codingStandardsIgnoreStart
535 $query = $wpdb->prepare(
536 "UPDATE {$table}
537 {$set_clause}
538 WHERE $where_col IN ( $where_in )
539 AND 1 = %d
540 ",
541 1
542 );
543 return $wpdb->query( $query ) ? true : false;
544 }
545
546 /**
547 * Prepare MySQL SET clause for update query
548 *
549 * @since v2.1.0
550 *
551 * @param array $data single dimension assoc_array.
552 *
553 * @return string
554 */
555 public static function prepare_set_clause( array $data ) {
556 $set = '';
557 foreach ( $data as $key => $value ) {
558 if ( $key === array_key_first ( $data ) ) {
559 $set .= "SET ";
560 }
561 // Multi dimension not allowed.
562 if ( is_array( $value ) ) {
563 continue;
564 }
565
566 if ( is_null( $value ) ) {
567 $set .= "$key = null";
568 } else {
569 $value = esc_sql( sanitize_text_field( $value ) );
570 $set .= is_numeric( $value ) ? "$key = $value" : "$key = '" . $value ."'";
571 }
572
573 $set .= ",";
574 }
575 return rtrim( $set, ',' );
576 }
577
578 /**
579 * Make sanitized SQL IN clause value from an array
580 *
581 * @param array $arr a sequentital array.
582 * @return string
583 * @since 2.1.1
584 */
585 public static function prepare_in_clause( array $arr ) {
586 $escaped = array_map(
587 function( $value ) {
588 global $wpdb;
589 $escaped_value = null;
590 if ( is_int( $value ) ) {
591 $escaped_value = $wpdb->prepare( '%d', $value );
592 } else if( is_float( $value ) ) {
593 list( $whole, $decimal ) = explode( '.', $value );
594 $expression = '%.'. strlen( $decimal ) . 'f';
595 $escaped_value = $wpdb->prepare( $expression, $value );
596 } else {
597 $escaped_value = $wpdb->prepare( '%s', $value );
598 }
599 return $escaped_value;
600 },
601 $arr
602 );
603
604 return implode( ',', $escaped );
605 }
606
607 /**
608 * Check table exist in database.
609 *
610 * @since 2.5.0
611 *
612 * @param string $table table name.
613 *
614 * @return bool
615 */
616 public static function table_exists( $table ) {
617 global $wpdb;
618 $sql = "SHOW TABLES LIKE '{$table}'";
619 return $wpdb->get_var( $sql ) === $table;
620 }
621
622 /**
623 * Check column exist in a table
624 *
625 * @since 3.0.0
626 *
627 * @param string $table table name.
628 * @param string $column column name.
629 *
630 * @return bool
631 */
632 public static function column_exist( $table, $column ) {
633 global $wpdb;
634 $sql = "SHOW COLUMNS FROM {$table} LIKE '{$column}'";
635 return $wpdb->get_var( $sql ) === $column;
636 }
637
638 /**
639 * Get data by joining multiple tables with specified join relations.
640 *
641 * Argument should be SQL escaped.
642 *
643 * @since 3.0.0
644 *
645 * @param string $primary_table The primary table name with prefix.
646 * @param array $joining_tables An array of join relations. Each relation should be an array with keys 'type', 'table', 'on'.
647 * @param array $select_columns An array of columns to select.
648 * @param array $where An associative array for the WHERE clause. For example: [col_name => value]. Without sql esc.
649 * @param array $search An associative array for the search clause. For example: [col_name => value]. Without sql esc.
650 * @param string $order_by Order by column name.
651 * @param int $limit Maximum number of rows to return.
652 * @param int $offset Offset for pagination.
653 * @param string $order DESC or ASC, default is DESC.
654 * @param string $output Expected output type, default is OBJECT.
655 *
656 * @throws \Exception If an error occurred during the query execution.
657 *
658 * @return mixed Based on output param, default OBJECT.
659 */
660 public static function get_joined_data(
661 string $primary_table,
662 array $joining_tables,
663 array $select_columns,
664 array $where = [],
665 array $search = [],
666 string $order_by = '',
667 $limit = 10,
668 $offset = 0,
669 string $order = 'DESC',
670 string $output = 'OBJECT'
671 ) {
672 global $wpdb;
673
674 $select_clause = implode(', ', $select_columns);
675
676 $from_clause = $primary_table;
677
678 $join_clauses = '';
679 foreach ($joining_tables as $relation) {
680 $join_clauses .= " {$relation['type']} JOIN {$relation['table']} ON {$relation['on']}";
681 }
682
683 $where_clause = !empty($where) ? 'WHERE ' . self::build_where_clause($where) : '';
684
685 if (!empty($search)) {
686 $search_clause = self::build_like_clause( $search );
687 // foreach ($search as $column => $value) {
688 // $search_clauses[] = $wpdb->prepare("{$column} LIKE %s", '%' . $wpdb->esc_like($value) . '%');
689 // }
690 $where_clause .= !empty($where_clause) ? ' AND (' . $search_clause . ')' : 'WHERE ' . $search_clause;
691 }
692
693 $order_by_clause = !empty($order_by) ? "ORDER BY {$order_by} {$order}" : '';
694
695 // Query to get total count.
696 $count_query = "
697 SELECT COUNT(*) as total_count
698 FROM {$from_clause}
699 {$join_clauses}
700 {$where_clause}
701 ";
702
703 $total_count = $wpdb->get_var($count_query);
704
705 if (empty($limit) && empty($offset)) {
706 $query = "SELECT
707 {$select_clause}
708 FROM {$from_clause}
709 {$join_clauses}
710 {$where_clause}
711 {$order_by_clause}";
712 } else {
713 $query = $wpdb->prepare(
714 "SELECT {$select_clause}
715 FROM {$from_clause}
716 {$join_clauses}
717 {$where_clause}
718 {$order_by_clause}
719 LIMIT %d OFFSET %d",
720 $limit,
721 $offset
722 );
723 }
724
725
726 $results = $wpdb->get_results($query, $output);
727
728 // Throw exception if error occurred.
729 if ($wpdb->last_error) {
730 throw new \Exception($wpdb->last_error);
731 }
732
733 // Prepare response array.
734 $response = array(
735 'total_count' => (int) $total_count,
736 'results' => $results,
737 );
738
739 return $response;
740 }
741
742 /**
743 * Get count var
744 *
745 * Argument should be SQL escaped.
746 *
747 * @since 1.0.0
748 *
749 * @param string $table table name with prefix.
750 * @param array $where array of where condition.
751 * @param array $search array of search conditions for LIKE operator.
752 * @param string $count_column column name to count, default id.
753 *
754 * @return int
755 */
756 public static function get_count( $table, $where = [], $search = [], $count_column = 'id' ): int {
757 global $wpdb;
758
759 $where_clause = !empty( $where ) ? 'WHERE ' . self::build_where_clause( $where ) : '';
760 $search_clause = !empty( $search ) ? self::build_like_clause( $search, 'AND' ) : '';
761
762 if ( !empty( $search_clause ) ) {
763 if ( !empty( $where_clause ) ) {
764 $where_clause .= ' AND (' . $search_clause . ')';
765 } else {
766 $where_clause = 'WHERE ' . $search_clause;
767 }
768 }
769
770 $count = $wpdb->get_var(
771 "SELECT COUNT($count_column)
772 FROM $table
773 {$where_clause}"
774 );
775
776 // If error occurred then throw new exception.
777 if ( $wpdb->last_error ) {
778 throw new \Exception( $wpdb->last_error );
779 }
780
781 return (int) $count;
782 }
783
784 /**
785 * Get count by joining multiple tables with specified join relations.
786 *
787 * Argument should be SQL escaped.
788 *
789 * @since 3.0.0
790 *
791 * @param string $primary_table The primary table name with prefix.
792 * @param array $joining_tables An array of join relations. Each relation should be an array with keys 'type', 'table', 'on'.
793 * @param array $where array of where conditions.
794 * @param array $search array of search conditions for LIKE operator.
795 * @param string $count_column column name to count, default id.
796 *
797 * @return int
798 */
799 public static function get_joined_count(string $primary_table, array $joining_tables, array $where = [], array $search = [], string $count_column = '*'): int {
800 global $wpdb;
801
802 $from_clause = $primary_table;
803
804 $join_clauses = '';
805 foreach ($joining_tables as $relation) {
806 $join_clauses .= " {$relation['type']} JOIN {$relation['table']} ON {$relation['on']}";
807 }
808
809 $where_clause = !empty($where) ? 'WHERE ' . self::build_where_clause($where) : '';
810 $search_clause = !empty($search) ? self::build_like_clause($search, 'AND') : '';
811
812 if (!empty($search_clause)) {
813 if (!empty($where_clause)) {
814 $where_clause .= ' AND (' . $search_clause . ')';
815 } else {
816 $where_clause = 'WHERE ' . $search_clause;
817 }
818 }
819
820 $count_query = "
821 SELECT COUNT($count_column) as total_count
822 FROM {$from_clause}
823 {$join_clauses}
824 {$where_clause}
825 ";
826
827 $total_count = $wpdb->get_var($count_query);
828
829 // If error occurred then throw new exception.
830 if ($wpdb->last_error) {
831 throw new \Exception($wpdb->last_error);
832 }
833
834 return (int) $total_count;
835 }
836
837 /**
838 * Get all rows from any table with where and search clauses.
839 *
840 * @since 3.0.0
841 *
842 * @param string $table Table name with prefix.
843 * @param array $where Assoc array for exact match. For example: [col_name => value]. Without sql esc.
844 * @param array $search Assoc array for LIKE match. For example: [col_name => search_term]. Without sql esc.
845 * @param string $order_by Order by column name.
846 * @param int $limit Maximum number of rows to return, default is 10.
847 * @param int $offset Offset for pagination, default is 0.
848 * @param string $order DESC or ASC, default is DESC.
849 * @param string $output Expected output type, default is OBJECT.
850 *
851 * @throws \Exception Throw exception if error occurred during query execution.
852 *
853 * @return mixed Based on output param, default OBJECT.
854 */
855 public static function get_all_with_search(string $table, array $where, array $search, string $order_by, $limit = 10, $offset = 0, string $order = 'DESC', string $output = 'OBJECT'): array {
856 global $wpdb;
857
858 $where_clause = !empty($where) ? 'WHERE ' . self::build_where_clause($where) : '';
859 $search_clause = !empty($search) ? self::build_like_clause($search, 'AND') : '';
860
861 if (!empty($search_clause)) {
862 if (!empty($where_clause)) {
863 $where_clause .= ' AND (' . $search_clause . ')';
864 } else {
865 $where_clause = 'WHERE ' . $search_clause;
866 }
867 }
868
869 // Query to get total count
870 $count_query = "
871 SELECT COUNT(*)
872 FROM {$table}
873 {$where_clause}
874 ";
875 $total_count = $wpdb->get_var($count_query);
876
877 // If error occurred then throw new exception.
878 if ($wpdb->last_error) {
879 throw new \Exception($wpdb->last_error);
880 }
881
882 $query = $wpdb->prepare(
883 "SELECT *
884 FROM {$table}
885 {$where_clause}
886 ORDER BY {$order_by} {$order}
887 LIMIT %d OFFSET %d",
888 $limit,
889 $offset
890 );
891
892 $results = $wpdb->get_results($query, $output);
893
894 // If error occurred then throw new exception.
895 if ($wpdb->last_error) {
896 throw new \Exception($wpdb->last_error);
897 }
898
899 // Prepare response array.
900 $response = array(
901 'results' => $results,
902 'total_count' => (int) $total_count,
903 );
904
905 return $response;
906 }
907
908 /**
909 * Get period clause based on the provided period.
910 *
911 * @since 3.0.0
912 *
913 * @param string $column Table.column name, ex: table.created_at.
914 * @param string $period Period for filter refund data.
915 *
916 * @return string
917 */
918 public static function get_period_clause( string $column, string $period = '' ) {
919 $period_clause = '';
920 switch ( $period ) {
921 case 'today':
922 $period_clause = "AND DATE($column) = CURDATE()";
923 break;
924 case 'monthly':
925 $period_clause = "AND MONTH($column) = MONTH(CURDATE())";
926 break;
927 case 'yearly':
928 $period_clause = "AND YEAR($column) = YEAR(CURDATE())";
929 break;
930 case 'last30days':
931 $period_clause = "AND DATE($column) BETWEEN DATE_SUB(CURDATE(), INTERVAL 30 DAY) AND CURDATE()";
932 break;
933 case 'last90days':
934 $period_clause = "AND DATE($column) BETWEEN DATE_SUB(CURDATE(), INTERVAL 90 DAY) AND CURDATE()";
935 break;
936 case 'last365days':
937 $period_clause = "AND DATE($column) BETWEEN DATE_SUB(CURDATE(), INTERVAL 365 DAY) AND CURDATE()";
938 break;
939 default:
940 break;
941 }
942
943 return $period_clause;
944 }
945
946 }
947