PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 3.4.2
Tutor LMS – eLearning and online course solution v3.4.2
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
871 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 if ( 'IN' === strtoupper( $operator ) ) {
219 $value = '(' . self::prepare_in_clause( $value ) . ')';
220 }
221
222 return "{$field} {$operator} {$value}";
223 }
224
225 /**
226 * Build where clause string
227 *
228 * @param array $where assoc array with field and value.
229 * @return string
230 *
231 * @since 2.0.9
232 *
233 * @since 3.0.0
234 * Null value support added, if need to check with
235 * null: [name => 'null'] we can pass
236 */
237 public static function build_where_clause( array $where ) {
238 $arr = array();
239 foreach ( $where as $field => $value ) {
240 if ( is_array( $value ) ) {
241 $value = array( $field, 'IN', $value );
242 } else {
243 if ( 'null' == $value ) {
244 $value = array( $field, 'IS', 'NULL' );
245 } else {
246 $value = is_numeric( $value ) ? $value : "'" . $value . "'";
247 $value = array( $field, '=', $value );
248 }
249 }
250
251 $arr[] = self::make_clause( $value );
252 }
253
254 return implode( ' AND ', $arr );
255 }
256
257 /**
258 * Build like clause string with or
259 *
260 * @since 1.0.0
261 *
262 * @param array $where assoc array with field and value.
263 * @param string $relation default is OR.
264 *
265 * @return string
266 */
267 public static function build_like_clause( array $where, $relation = 'OR' ) {
268 global $wpdb;
269
270 $like_conditions = array();
271
272 foreach ( $where as $column_name => $term ) {
273 //phpcs:ignore
274 $like_conditions[] = $wpdb->prepare( "$column_name LIKE %s", '%' . $wpdb->esc_like( $term ) . '%' );
275 }
276
277 $where_clause = implode( ' OR ', $like_conditions );
278
279 return $where_clause;
280 }
281
282 /**
283 * Sanitize assoc array
284 *
285 * @param array $array an assoc array.
286 * @return array
287 *
288 * @since 2.0.9
289 */
290 private static function sanitize_assoc_array( array $array ) {
291 return array_map(
292 function ( $value ) {
293 return sanitize_text_field( $value );
294 },
295 $array
296 );
297 }
298
299 /**
300 * Delete comment with associate meta data
301 *
302 * @param array $where associative array with field and value.
303 * Example: array( 'comment_type' => 'comment', 'comment_id' => 1 ).
304 * @return bool
305 *
306 * @since 2.0.9
307 */
308 public static function delete_comment_with_meta( array $where ) {
309 if ( count( $where ) === 0 || ! tutor_utils()->is_assoc( $where ) ) {
310 return false;
311 }
312
313 $where = self::build_where_clause( self::sanitize_assoc_array( $where ) );
314
315 global $wpdb;
316 $ids = $wpdb->get_col( "SELECT comment_id FROM {$wpdb->comments} WHERE {$where}" );//phpcs:ignore
317
318 if ( is_array( $ids ) && count( $ids ) ) {
319 $ids_str = "'" . implode( "','", $ids ) . "'";
320 // delete comment metas.
321 $wpdb->query( "DELETE FROM {$wpdb->commentmeta} WHERE comment_id IN({$ids_str}) " );//phpcs:ignore
322 // delete comment.
323 $wpdb->query( "DELETE FROM {$wpdb->comments} WHERE {$where}" );//phpcs:ignore
324
325 return true;
326 }
327
328 return false;
329 }
330
331 /**
332 * Delete post with associate meta data
333 *
334 * @param array $where associative array with field and value.
335 * Example: array( 'post_type' => 'post', 'id' => 1 ).
336 * @return bool
337 *
338 * @since 2.0.9
339 */
340 public static function delete_post_with_meta( array $where ) {
341 if ( count( $where ) === 0 || ! tutor_utils()->is_assoc( $where ) ) {
342 return false;
343 }
344
345 $where = self::build_where_clause( self::sanitize_assoc_array( $where ) );
346
347 global $wpdb;
348 $ids = $wpdb->get_col( "SELECT id FROM {$wpdb->posts} WHERE {$where}" );//phpcs:ignore
349
350 if ( is_array( $ids ) && count( $ids ) ) {
351 $ids_str = "'" . implode( "','", $ids ) . "'";
352 // delete post metas.
353 $wpdb->query( "DELETE FROM {$wpdb->postmeta} WHERE post_id IN({$ids_str}) " );//phpcs:ignore
354 // delete post.
355 $wpdb->query( "DELETE FROM {$wpdb->posts} WHERE {$where}" );//phpcs:ignore
356
357 return true;
358 }
359
360 return false;
361 }
362
363 /**
364 * Get a single row from any table with where clause
365 *
366 * @param string $table table name with prefix.
367 *
368 * @param array $where assoc_array. For ex: [col_name => value ].
369 * @param string $order_by order by column name.
370 * @param string $order DESC or ASC, default is DESC.
371 * @param string $output expected output type, default is object.
372 *
373 * @return mixed based on output param, default object
374 */
375 public static function get_row( string $table, array $where, string $order_by, string $order = 'DESC', string $output = 'OBJECT' ) {
376 global $wpdb;
377
378 $where_clause = self::build_where_clause( $where );
379
380 //phpcs:disable
381 $query = $wpdb->prepare(
382 "SELECT *
383 FROM {$table}
384 WHERE {$where_clause}
385 ORDER BY {$order_by} {$order}
386 LIMIT %d
387 ",
388 1
389 );
390
391 return $wpdb->get_row(
392 $query,
393 $output
394 );
395 //phpcs:enable
396 }
397
398 /**
399 * Get all row from any table with where clause
400 *
401 * @since 2.2.1
402 * @since 3.0.0 added support for -1 value in the limit parameter.
403 *
404 * @param string $table table name with prefix.
405 *
406 * @param array $where assoc_array. For ex: [col_name => value ].
407 * @param string $order_by order by column name.
408 * @param int $limit default is 1000, -1 for no limit.
409 * @param string $order DESC or ASC, default is DESC.
410 * @param string $output expected output type, default is object.
411 *
412 * @return mixed based on output param, default object
413 */
414 public static function get_all( string $table, array $where, string $order_by, $limit = 1000, string $order = 'DESC', string $output = 'OBJECT' ) {
415 global $wpdb;
416
417 $where_clause = self::build_where_clause( $where );
418 $limit = (int) sanitize_text_field( $limit );
419 $limit_clause = ( -1 === $limit ) ? '' : 'LIMIT ' . $limit;
420
421 //phpcs:disable
422 $query = "SELECT *
423 FROM {$table}
424 WHERE {$where_clause}
425 ORDER BY {$order_by} {$order}
426 {$limit_clause}";
427
428 return $wpdb->get_results(
429 $query,
430 $output
431 );
432 //phpcs:enable
433 }
434
435 /**
436 * Update multiple rows by using where in
437 * clause
438 *
439 * @since v2.1.0
440 *
441 * @param string $table table name.
442 * @param array $data assoc_array data to update
443 * ex: [id => 2, name => 'john' ].
444 * @param string $where_in comma separated values, ex: 1,2,3.
445 * @param string $where_col default is ID but could be other.
446 *
447 * @return bool true on success, false on failure
448 */
449 public static function update_where_in( string $table, array $data, string $where_in, string $where_col = 'ID' ) {
450 global $wpdb;
451 if ( empty( $where_in ) || empty( $where_col ) ) {
452 return false;
453 }
454 $set_clause = self::prepare_set_clause( $data );
455 if ( '' === $set_clause ) {
456 return false;
457 }
458 // @codingStandardsIgnoreStart
459 $query = $wpdb->prepare(
460 "UPDATE {$table}
461 {$set_clause}
462 WHERE $where_col IN ( $where_in )
463 AND 1 = %d
464 ",
465 1
466 );
467 return $wpdb->query( $query ) ? true : false;
468 }
469
470 /**
471 * Prepare MySQL SET clause for update query
472 *
473 * @since v2.1.0
474 *
475 * @param array $data single dimension assoc_array.
476 *
477 * @return string
478 */
479 public static function prepare_set_clause( array $data ) {
480 $set = '';
481 foreach ( $data as $key => $value ) {
482 if ( $key === array_key_first ( $data ) ) {
483 $set .= "SET ";
484 }
485 // Multi dimension not allowed.
486 if ( is_array( $value ) ) {
487 continue;
488 }
489
490 if ( is_null( $value ) ) {
491 $set .= "$key = null";
492 } else {
493 $value = esc_sql( sanitize_text_field( $value ) );
494 $set .= is_numeric( $value ) ? "$key = $value" : "$key = '" . $value ."'";
495 }
496
497 $set .= ",";
498 }
499 return rtrim( $set, ',' );
500 }
501
502 /**
503 * Make sanitized SQL IN clause value from an array
504 *
505 * @param array $arr a sequentital array.
506 * @return string
507 * @since 2.1.1
508 */
509 public static function prepare_in_clause( array $arr ) {
510 $escaped = array_map(
511 function( $value ) {
512 global $wpdb;
513 $escaped_value = null;
514 if ( is_int( $value ) ) {
515 $escaped_value = $wpdb->prepare( '%d', $value );
516 } else if( is_float( $value ) ) {
517 list( $whole, $decimal ) = explode( '.', $value );
518 $expression = '%.'. strlen( $decimal ) . 'f';
519 $escaped_value = $wpdb->prepare( $expression, $value );
520 } else {
521 $escaped_value = $wpdb->prepare( '%s', $value );
522 }
523 return $escaped_value;
524 },
525 $arr
526 );
527
528 return implode( ',', $escaped );
529 }
530
531 /**
532 * Check table exist in database.
533 *
534 * @since 2.5.0
535 *
536 * @param string $table table name.
537 *
538 * @return bool
539 */
540 public static function table_exists( $table ) {
541 global $wpdb;
542 $sql = "SHOW TABLES LIKE '{$table}'";
543 return $wpdb->get_var( $sql ) === $table;
544 }
545
546 /**
547 * Check column exist in a table
548 *
549 * @since 3.0.0
550 *
551 * @param string $table table name.
552 * @param string $column column name.
553 *
554 * @return bool
555 */
556 public static function column_exist( $table, $column ) {
557 global $wpdb;
558 $sql = "SHOW COLUMNS FROM {$table} LIKE '{$column}'";
559 return $wpdb->get_var( $sql ) === $column;
560 }
561
562 /**
563 * Get data by joining multiple tables with specified join relations.
564 *
565 * Argument should be SQL escaped.
566 *
567 * @since 3.0.0
568 *
569 * @param string $primary_table The primary table name with prefix.
570 * @param array $joining_tables An array of join relations. Each relation should be an array with keys 'type', 'table', 'on'.
571 * @param array $select_columns An array of columns to select.
572 * @param array $where An associative array for the WHERE clause. For example: [col_name => value]. Without sql esc.
573 * @param array $search An associative array for the search clause. For example: [col_name => value]. Without sql esc.
574 * @param string $order_by Order by column name.
575 * @param int $limit Maximum number of rows to return.
576 * @param int $offset Offset for pagination.
577 * @param string $order DESC or ASC, default is DESC.
578 * @param string $output Expected output type, default is OBJECT.
579 *
580 * @throws \Exception If an error occurred during the query execution.
581 *
582 * @return mixed Based on output param, default OBJECT.
583 */
584 public static function get_joined_data(
585 string $primary_table,
586 array $joining_tables,
587 array $select_columns,
588 array $where = [],
589 array $search = [],
590 string $order_by = '',
591 $limit = 10,
592 $offset = 0,
593 string $order = 'DESC',
594 string $output = 'OBJECT'
595 ) {
596 global $wpdb;
597
598 $select_clause = implode(', ', $select_columns);
599
600 $from_clause = $primary_table;
601
602 $join_clauses = '';
603 foreach ($joining_tables as $relation) {
604 $join_clauses .= " {$relation['type']} JOIN {$relation['table']} ON {$relation['on']}";
605 }
606
607 $where_clause = !empty($where) ? 'WHERE ' . self::build_where_clause($where) : '';
608
609 if (!empty($search)) {
610 $search_clause = self::build_like_clause( $search );
611 // foreach ($search as $column => $value) {
612 // $search_clauses[] = $wpdb->prepare("{$column} LIKE %s", '%' . $wpdb->esc_like($value) . '%');
613 // }
614 $where_clause .= !empty($where_clause) ? ' AND (' . $search_clause . ')' : 'WHERE ' . $search_clause;
615 }
616
617 $order_by_clause = !empty($order_by) ? "ORDER BY {$order_by} {$order}" : '';
618
619 // Query to get total count.
620 $count_query = "
621 SELECT COUNT(*) as total_count
622 FROM {$from_clause}
623 {$join_clauses}
624 {$where_clause}
625 ";
626
627 $total_count = $wpdb->get_var($count_query);
628
629 if (empty($limit) && empty($offset)) {
630 $query = "SELECT
631 {$select_clause}
632 FROM {$from_clause}
633 {$join_clauses}
634 {$where_clause}
635 {$order_by_clause}";
636 } else {
637 $query = $wpdb->prepare(
638 "SELECT {$select_clause}
639 FROM {$from_clause}
640 {$join_clauses}
641 {$where_clause}
642 {$order_by_clause}
643 LIMIT %d OFFSET %d",
644 $limit,
645 $offset
646 );
647 }
648
649
650 $results = $wpdb->get_results($query, $output);
651
652 // Throw exception if error occurred.
653 if ($wpdb->last_error) {
654 throw new \Exception($wpdb->last_error);
655 }
656
657 // Prepare response array.
658 $response = array(
659 'total_count' => (int) $total_count,
660 'results' => $results,
661 );
662
663 return $response;
664 }
665
666 /**
667 * Get count var
668 *
669 * Argument should be SQL escaped.
670 *
671 * @since 1.0.0
672 *
673 * @param string $table table name with prefix.
674 * @param array $where array of where condition.
675 * @param array $search array of search conditions for LIKE operator.
676 * @param string $count_column column name to count, default id.
677 *
678 * @return int
679 */
680 public static function get_count( $table, $where = [], $search = [], $count_column = 'id' ): int {
681 global $wpdb;
682
683 $where_clause = !empty( $where ) ? 'WHERE ' . self::build_where_clause( $where ) : '';
684 $search_clause = !empty( $search ) ? self::build_like_clause( $search, 'AND' ) : '';
685
686 if ( !empty( $search_clause ) ) {
687 if ( !empty( $where_clause ) ) {
688 $where_clause .= ' AND (' . $search_clause . ')';
689 } else {
690 $where_clause = 'WHERE ' . $search_clause;
691 }
692 }
693
694 $count = $wpdb->get_var(
695 "SELECT COUNT($count_column)
696 FROM $table
697 {$where_clause}"
698 );
699
700 // If error occurred then throw new exception.
701 if ( $wpdb->last_error ) {
702 throw new \Exception( $wpdb->last_error );
703 }
704
705 return (int) $count;
706 }
707
708 /**
709 * Get count by joining multiple tables with specified join relations.
710 *
711 * Argument should be SQL escaped.
712 *
713 * @since 3.0.0
714 *
715 * @param string $primary_table The primary table name with prefix.
716 * @param array $joining_tables An array of join relations. Each relation should be an array with keys 'type', 'table', 'on'.
717 * @param array $where array of where conditions.
718 * @param array $search array of search conditions for LIKE operator.
719 * @param string $count_column column name to count, default id.
720 *
721 * @return int
722 */
723 public static function get_joined_count(string $primary_table, array $joining_tables, array $where = [], array $search = [], string $count_column = '*'): int {
724 global $wpdb;
725
726 $from_clause = $primary_table;
727
728 $join_clauses = '';
729 foreach ($joining_tables as $relation) {
730 $join_clauses .= " {$relation['type']} JOIN {$relation['table']} ON {$relation['on']}";
731 }
732
733 $where_clause = !empty($where) ? 'WHERE ' . self::build_where_clause($where) : '';
734 $search_clause = !empty($search) ? self::build_like_clause($search, 'AND') : '';
735
736 if (!empty($search_clause)) {
737 if (!empty($where_clause)) {
738 $where_clause .= ' AND (' . $search_clause . ')';
739 } else {
740 $where_clause = 'WHERE ' . $search_clause;
741 }
742 }
743
744 $count_query = "
745 SELECT COUNT($count_column) as total_count
746 FROM {$from_clause}
747 {$join_clauses}
748 {$where_clause}
749 ";
750
751 $total_count = $wpdb->get_var($count_query);
752
753 // If error occurred then throw new exception.
754 if ($wpdb->last_error) {
755 throw new \Exception($wpdb->last_error);
756 }
757
758 return (int) $total_count;
759 }
760
761 /**
762 * Get all rows from any table with where and search clauses.
763 *
764 * @since 3.0.0
765 *
766 * @param string $table Table name with prefix.
767 * @param array $where Assoc array for exact match. For example: [col_name => value]. Without sql esc.
768 * @param array $search Assoc array for LIKE match. For example: [col_name => search_term]. Without sql esc.
769 * @param string $order_by Order by column name.
770 * @param int $limit Maximum number of rows to return, default is 10.
771 * @param int $offset Offset for pagination, default is 0.
772 * @param string $order DESC or ASC, default is DESC.
773 * @param string $output Expected output type, default is OBJECT.
774 *
775 * @throws \Exception Throw exception if error occurred during query execution.
776 *
777 * @return mixed Based on output param, default OBJECT.
778 */
779 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 {
780 global $wpdb;
781
782 $where_clause = !empty($where) ? 'WHERE ' . self::build_where_clause($where) : '';
783 $search_clause = !empty($search) ? self::build_like_clause($search, 'AND') : '';
784
785 if (!empty($search_clause)) {
786 if (!empty($where_clause)) {
787 $where_clause .= ' AND (' . $search_clause . ')';
788 } else {
789 $where_clause = 'WHERE ' . $search_clause;
790 }
791 }
792
793 // Query to get total count
794 $count_query = "
795 SELECT COUNT(*)
796 FROM {$table}
797 {$where_clause}
798 ";
799 $total_count = $wpdb->get_var($count_query);
800
801 // If error occurred then throw new exception.
802 if ($wpdb->last_error) {
803 throw new \Exception($wpdb->last_error);
804 }
805
806 $query = $wpdb->prepare(
807 "SELECT *
808 FROM {$table}
809 {$where_clause}
810 ORDER BY {$order_by} {$order}
811 LIMIT %d OFFSET %d",
812 $limit,
813 $offset
814 );
815
816 $results = $wpdb->get_results($query, $output);
817
818 // If error occurred then throw new exception.
819 if ($wpdb->last_error) {
820 throw new \Exception($wpdb->last_error);
821 }
822
823 // Prepare response array.
824 $response = array(
825 'results' => $results,
826 'total_count' => (int) $total_count,
827 );
828
829 return $response;
830 }
831
832 /**
833 * Get period clause based on the provided period.
834 *
835 * @since 3.0.0
836 *
837 * @param string $column Table.column name, ex: table.created_at.
838 * @param string $period Period for filter refund data.
839 *
840 * @return string
841 */
842 public static function get_period_clause( string $column, string $period = '' ) {
843 $period_clause = '';
844 switch ( $period ) {
845 case 'today':
846 $period_clause = "AND DATE($column) = CURDATE()";
847 break;
848 case 'monthly':
849 $period_clause = "AND MONTH($column) = MONTH(CURDATE())";
850 break;
851 case 'yearly':
852 $period_clause = "AND YEAR($column) = YEAR(CURDATE())";
853 break;
854 case 'last30days':
855 $period_clause = "AND DATE($column) BETWEEN DATE_SUB(CURDATE(), INTERVAL 30 DAY) AND CURDATE()";
856 break;
857 case 'last90days':
858 $period_clause = "AND DATE($column) BETWEEN DATE_SUB(CURDATE(), INTERVAL 90 DAY) AND CURDATE()";
859 break;
860 case 'last365days':
861 $period_clause = "AND DATE($column) BETWEEN DATE_SUB(CURDATE(), INTERVAL 365 DAY) AND CURDATE()";
862 break;
863 default:
864 break;
865 }
866
867 return $period_clause;
868 }
869
870 }
871