PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.1.7.2
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.1.7.2
4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 All 139 releases
learnpress / inc / databases / class-lp-course-db.php

class-lp-course-db.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.1.7.2, at inc/databases/class-lp-course-db.php

685 lines 18.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class LP_Course_DB
4 *
5 * @author tungnx
6 * @since 3.2.7.5
7 */
8
9 defined( 'ABSPATH' ) || exit();
10
11 class LP_Course_DB extends LP_Database {
12 private static $_instance;
13
14 protected function __construct() {
15 parent::__construct();
16 }
17
18 public static function getInstance() {
19 if ( is_null( self::$_instance ) ) {
20 self::$_instance = new self();
21 }
22
23 return self::$_instance;
24 }
25
26 /**
27 * Get course_id of item
28 *
29 * item type lp_lesson, lp_quiz
30 *
31 * @param int $item_id
32 *
33 * @return int
34 * @throws Exception
35 */
36 public function get_course_by_item_id( $item_id = 0 ): int {
37 // Get cache
38 $lp_course_cache = LP_Course_Cache::instance();
39 $key_cache = "$item_id/course_id_of_item_id";
40 $course_id = $lp_course_cache->get_cache( $key_cache );
41
42 if ( ! $course_id ) {
43 $query = $this->wpdb->prepare(
44 "
45 SELECT section_course_id
46 FROM {$this->tb_lp_sections} AS s
47 INNER JOIN {$this->tb_lp_section_items} AS si
48 ON si.section_id = s.section_id
49 WHERE si.item_id = %d",
50 $item_id
51 );
52
53 $course_id = (int) $this->wpdb->get_var( $query );
54
55 $this->check_execute_has_error();
56
57 // Set cache
58 $lp_course_cache->set_cache( $key_cache, $course_id );
59 }
60
61 return $course_id;
62 }
63
64 /**
65 * Get all item ids' course
66 *
67 * @param int $course_id
68 *
69 * @return array|object|stdClass[]|null
70 * @throws Exception
71 * @since 4.1.6.9
72 * @version 1.0.0
73 */
74 public function get_full_sections_and_items_course( int $course_id = 0 ) {
75 $method_called_to = debug_backtrace()[1]['function'];
76
77 // Check accept call from function 'get_sections_and_items_course_from_db_and_sort'
78 if ( 'get_sections_and_items_course_from_db_and_sort' !== $method_called_to ) {
79 error_log( 'You can not call direct this function' );
80 return [];
81 }
82
83 $query = $this->wpdb->prepare(
84 "SELECT *
85 FROM {$this->tb_lp_section_items} AS si
86 INNER JOIN {$this->tb_lp_sections} AS s
87 ON si.section_id = s.section_id
88 WHERE section_course_id = %d
89 ORDER BY s.section_order",
90 $course_id
91 );
92
93 $sections_items = $this->wpdb->get_results( $query );
94
95 $this->check_execute_has_error();
96
97 return $sections_items;
98 }
99
100 /**
101 * Get all sections' course
102 *
103 * @param int $course_id
104 *
105 * @return array|object|stdClass[]|null
106 * @throws Exception
107 * @since 4.1.6.9
108 * @version 1.0.0
109 */
110 public function get_sections( int $course_id = 0 ) {
111 $method_called_to = debug_backtrace()[1]['function'];
112
113 // Check accept call from function 'get_sections_and_items_course_from_db_and_sort'
114 if ( 'get_sections_and_items_course_from_db_and_sort' !== $method_called_to ) {
115 error_log( 'You can not call direct this function' );
116 return [];
117 }
118
119 $query = $this->wpdb->prepare(
120 "SELECT * FROM {$this->tb_lp_sections}
121 WHERE section_course_id = %d
122 ORDER BY section_order",
123 $course_id
124 );
125
126 $sections_items = $this->wpdb->get_results( $query, OBJECT_K );
127
128 $this->check_execute_has_error();
129
130 return $sections_items;
131 }
132
133 /**
134 * Get user_item_id by order_id, course_id, user_id
135 *
136 * @param int $order_id
137 * @param int $course_id
138 * @param int $user_id
139 *
140 * @return int
141 */
142 public function get_user_item_id( $order_id = 0, $course_id = 0, $user_id = 0 ): int {
143 $query = $this->wpdb->prepare(
144 "
145 SELECT user_item_id
146 FROM {$this->tb_lp_user_items}
147 WHERE ref_type = %s
148 AND ref_id = %d
149 AND item_type = %s
150 AND item_id = %d
151 AND user_id = %d
152 ",
153 LP_ORDER_CPT,
154 $order_id,
155 LP_COURSE_CPT,
156 $course_id,
157 $user_id
158 );
159
160 return (int) $this->wpdb->get_var( $query );
161 }
162
163 /**
164 * Get first item id of course
165 *
166 * @param int $course_id .
167 *
168 * @return int
169 * @throws Exception
170 * @since 4.0.0
171 * @version 1.0.2
172 * @modify 4.1.3
173 * @author tungnx
174 */
175 public function get_first_item_id( int $course_id = 0 ): int {
176 // Get cache
177 $lp_course_cache = LP_Course_Cache::instance();
178 $key_cache = "$course_id/first_item_id";
179 $first_item_id = $lp_course_cache->get_cache( $key_cache );
180
181 if ( ! $first_item_id ) {
182 $query = $this->wpdb->prepare(
183 "
184 SELECT item_id FROM $this->tb_lp_section_items AS items
185 INNER JOIN $this->tb_lp_sections AS sections
186 ON items.section_id = sections.section_id
187 AND sections.section_course_id = %d
188 ORDER BY items.item_order ASC, sections.section_order ASC
189 LIMIT %d
190 ",
191 $course_id,
192 1
193 );
194
195 $first_item_id = (int) $this->wpdb->get_var( $query );
196
197 $this->check_execute_has_error();
198
199 // Set cache
200 $lp_course_cache->set_cache( $key_cache, $first_item_id );
201 }
202
203 return $first_item_id;
204 }
205
206 /**
207 * Get popular courses.
208 *
209 * @param LP_Course_Filter $filter
210 *
211 * @return array
212 * @author tungnx
213 * @version 1.0.0
214 * @depecated 4.1.6.4
215 */
216 /*public function get_popular_courses( LP_Course_Filter $filter ): array {
217 $offset = ( absint( $filter->page ) - 1 ) * $filter->limit;
218 $sql_limit = $this->wpdb->prepare( 'LIMIT %d, %d', $offset, $filter->limit );
219
220 $query = apply_filters(
221 'learn-press/course-curd/query-popular-courses',
222 $this->wpdb->prepare(
223 "SELECT DISTINCT(item_id), COUNT(item_id) as total
224 FROM $this->tb_lp_user_items
225 WHERE item_type = %s
226 AND ( status = %s OR status = %s OR status = %s )
227 GROUP BY item_id
228 ORDER BY total DESC
229 {$sql_limit}
230 ",
231 LP_COURSE_CPT,
232 LP_COURSE_ENROLLED,
233 LP_COURSE_FINISHED,
234 LP_COURSE_PURCHASED
235 )
236 );
237
238 return $this->wpdb->get_col( $query );
239 }*/
240
241 public function get_recent_courses( LP_Course_Filter $filter ) : array {
242 global $wpdb;
243
244 $limit = $filter->limit ?? - 1;
245 $order = ! empty( $filter->order ) ? $filter->order : 'DESC';
246
247 if ( $limit <= 0 ) {
248 $limit = 0;
249 }
250
251 $query = apply_filters(
252 'learnpress/databases/widgets/recent_courses',
253 $wpdb->prepare(
254 "SELECT DISTINCT p.ID
255 FROM $wpdb->posts AS p
256 WHERE p.post_type = %s
257 AND p.post_status = %s
258 ORDER BY p.post_date {$order}
259 LIMIT %d",
260 LP_COURSE_CPT,
261 'publish',
262 $limit
263 )
264 );
265
266 return $wpdb->get_col( $query );
267 }
268
269 public function get_featured_courses( LP_Course_Filter $filter ) : array {
270 global $wpdb;
271
272 $limit = ! empty( $filter->limit ) ? $filter->limit : -1;
273 $order_by = ! empty( $filter->order_by ) ? $filter->order_by : 'post_date';
274 $order = ! empty( $filter->order ) ? $filter->order : 'DESC';
275
276 if ( $limit <= 0 ) {
277 $limit = 0;
278 }
279
280 $query = apply_filters(
281 'learnpress/databases/widgets/featured_courses',
282 $wpdb->prepare(
283 "SELECT DISTINCT p.ID
284 FROM {$wpdb->posts} p
285 LEFT JOIN {$wpdb->postmeta} as pmeta ON p.ID=pmeta.post_id AND pmeta.meta_key = %s
286 WHERE p.post_type = %s
287 AND p.post_status = %s
288 AND pmeta.meta_value = %s
289 ORDER BY p.{$order_by} {$order}
290 LIMIT %d",
291 '_lp_featured',
292 LP_COURSE_CPT,
293 'publish',
294 'yes',
295 $limit
296 )
297 );
298
299 return $wpdb->get_col( $query );
300 }
301
302 /**
303 * Get list user ids enrolled by course
304 *
305 * @return array|object|null
306 * @throws Exception
307 * @version 1.0.0
308 * @author tungnx
309 * @since 4.1.3.1
310 */
311 public function get_user_ids_enrolled( int $course_id ) {
312 $query = $this->wpdb->prepare(
313 "
314 SELECT DISTINCT user_id FROM {$this->tb_lp_user_items}
315 WHERE item_id = %d
316 AND item_type = %s
317 AND (status = %s OR status = %s )
318 ",
319 $course_id,
320 LP_COURSE_CPT,
321 'enrolled',
322 'finished'
323 );
324
325 $result = $this->wpdb->get_results( $query, OBJECT_K );
326
327 $this->check_execute_has_error();
328
329 return $result;
330 }
331
332 /**
333 * Count total user enrolled by course
334 *
335 * @param int $course_id
336 *
337 * @return int
338 * @version 1.0.0
339 * @author tungnx
340 * @since 4.1.4
341 */
342 public function get_total_user_enrolled( int $course_id ): int {
343 $query = $this->wpdb->prepare(
344 "
345 SELECT COUNT(DISTINCT user_id) AS total FROM {$this->tb_lp_user_items}
346 WHERE item_id = %d
347 AND item_type = %s
348 AND (status = %s OR status = %s )
349 ",
350 $course_id,
351 LP_COURSE_CPT,
352 LP_COURSE_ENROLLED,
353 LP_COURSE_FINISHED
354 );
355
356 return (int) $this->wpdb->get_var( $query );
357 }
358
359 /**
360 * Count total user enrolled or purchase by course
361 *
362 * @param int $course_id
363 *
364 * @return int
365 * @version 1.0.0
366 * @author tungnx
367 * @since 4.1.4
368 */
369 public function get_total_user_enrolled_or_purchased( int $course_id ): int {
370 $query = $this->wpdb->prepare(
371 "
372 SELECT COUNT(DISTINCT user_id) AS total FROM {$this->tb_lp_user_items}
373 WHERE item_id = %d
374 AND item_type = %s
375 AND (status = %s OR status = %s OR status = %s )
376 ",
377 $course_id,
378 LP_COURSE_CPT,
379 LP_COURSE_ENROLLED,
380 LP_COURSE_FINISHED,
381 LP_COURSE_PURCHASED
382 );
383
384 return (int) $this->wpdb->get_var( $query );
385 }
386
387 /**
388 * Get total items of course
389 *
390 * @param int $course_id
391 * @author tungnx
392 * @since 4.1.4.1
393 * @version 1.0.0
394 * @return null|object
395 */
396 public function get_total_items( int $course_id = 0 ) {
397 // Get cache
398 $lp_course_cache = LP_Course_Cache::instance();
399 $key_cache = "$course_id/total_items";
400 $total_items = $lp_course_cache->get_cache( $key_cache );
401
402 if ( ! $total_items ) {
403 $item_types = learn_press_get_course_item_types();
404 $count_item_types = count( $item_types );
405 $i = 0;
406
407 $query_count = $this->wpdb->prepare( 'SUM(s.section_course_id = %d) AS count_items,', $course_id );
408
409 foreach ( $item_types as $item_type ) {
410 $i++;
411 if ( $i == $count_item_types ) {
412 $query_count .= $this->wpdb->prepare( 'SUM(s.section_course_id = %d AND si.item_type = %s) AS %s', $course_id, $item_type, $item_type );
413 } else {
414 $query_count .= $this->wpdb->prepare( 'SUM(s.section_course_id = %d AND si.item_type = %s) AS %s,', $course_id, $item_type, $item_type );
415 }
416 }
417
418 $query = "
419 SELECT $query_count
420 FROM $this->tb_lp_section_items si
421 INNER JOIN $this->tb_lp_sections s ON s.section_id = si.section_id
422 ";
423
424 $total_items = $this->wpdb->get_row( $query );
425
426 // Set cache
427 $lp_course_cache->set_cache( $key_cache, $total_items );
428 }
429
430 return $total_items;
431 }
432
433 /**
434 * Count all item are unassigned to any courses.
435 *
436 * @param string $item_type (type item Lesson, Quiz, Assignment, H5P ...)
437 *
438 * @return int
439 * @throws Exception
440 * @author tungnx
441 * @version 1.0.1
442 * @since 4.1.4.1
443 */
444 public function get_total_item_unassigned( string $item_type ): int {
445 $filter = new LP_Post_Type_Filter();
446 $filter->post_type = $item_type;
447 $filter->query_count = true;
448 $filter->post_status = array();
449 $filter->field_count = 'p.ID';
450
451 return $this->get_item_ids_unassigned( $filter );
452 }
453
454 /**
455 * list id item are unassigned to any courses.
456 *
457 * @param LP_Post_Type_Filter $filter
458 *
459 * @return array|int|string|null
460 * @throws Exception
461 * @author tungnx
462 * @version 1.0.0
463 * @since 4.1.6
464 */
465 public function get_item_ids_unassigned( LP_Post_Type_Filter $filter = null ) {
466 if ( is_null( $filter ) ) {
467 $filter = new LP_Post_Type_Filter();
468 }
469
470 $filter_section_items = new LP_Section_Items_Filter();
471 $filter_section_items->return_string_query = true;
472 $filter_section_items->only_fields = array( 'si.item_id' );
473 $filter_section_items->where[] = $this->wpdb->prepare( 'AND si.item_type = %s', $filter->post_type );
474 $query_item_ids_assigned = LP_Section_Items_DB::getInstance()->get_section_items( $filter_section_items );
475
476 $filter->only_fields = array( 'p.ID' );
477 $filter->collection = $this->tb_posts;
478 $filter->collection_alias = 'p';
479 $filter->where[] = $this->wpdb->prepare( 'AND p.post_type = %s', $filter->post_type );
480 $filter->where[] = 'AND ID NOT IN(' . $query_item_ids_assigned . ')';
481 $filter->where[] = $this->wpdb->prepare( 'AND p.post_status not IN(%s, %s)', 'trash', 'auto-draft' );
482
483 return $this->execute( $filter );
484 }
485
486 /**
487 * Get Courses
488 *
489 * @param LP_Course_Filter $filter
490 * @param int $total_rows return total_rows
491 *
492 * @return array|null|int|string
493 * @throws Exception
494 * @author tungnx
495 * @version 1.0.1
496 * @since 4.1.5
497 */
498 public function get_courses( LP_Course_Filter $filter, int &$total_rows = 0 ) {
499 $default_fields = $this->get_cols_of_table( $this->tb_posts );
500 $filter->fields = array_merge( $default_fields, $filter->fields );
501
502 if ( empty( $filter->collection ) ) {
503 $filter->collection = $this->tb_posts;
504 }
505
506 if ( empty( $filter->collection_alias ) ) {
507 $filter->collection_alias = 'p';
508 }
509
510 // Where
511 $filter->where[] = $this->wpdb->prepare( 'AND p.post_type = %s', $filter->post_type );
512
513 // Status
514 $filter->post_status = (array) $filter->post_status;
515 if ( ! empty( $filter->post_status ) ) {
516 $post_status_format = LP_Helper::db_format_array( $filter->post_status, '%s' );
517 $filter->where[] = $this->wpdb->prepare( 'AND p.post_status IN (' . $post_status_format . ')', $filter->post_status );
518 }
519
520 // Term ids
521 if ( ! empty( $filter->term_ids ) ) {
522 $filter->join[] = "INNER JOIN $this->tb_term_relationships AS r_term ON p.ID = r_term.object_id";
523
524 $term_ids_format = LP_Helper::db_format_array( $filter->term_ids, '%d' );
525 $filter->where[] = $this->wpdb->prepare( 'AND r_term.term_taxonomy_id IN (' . $term_ids_format . ')', $filter->term_ids );
526 }
527
528 // course ids
529 if ( ! empty( $filter->post_ids ) ) {
530 $list_ids_format = LP_Helper::db_format_array( $filter->post_ids, '%d' );
531 $filter->where[] = $this->wpdb->prepare( 'AND p.ID IN (' . $list_ids_format . ')', $filter->post_ids );
532 }
533
534 // Title
535 if ( $filter->post_title ) {
536 $filter->where[] = $this->wpdb->prepare( 'AND p.post_title LIKE %s', '%' . $filter->post_title . '%' );
537 }
538
539 // Author
540 if ( $filter->post_author ) {
541 $filter->where[] = $this->wpdb->prepare( 'AND p.post_author = %d', $filter->post_author );
542 }
543 // Authors
544 if ( ! empty( $filter->post_authors ) ) {
545 $post_authors_format = LP_Helper::db_format_array( $filter->post_authors, '%d' );
546 $filter->where[] = $this->wpdb->prepare( 'AND p.post_author IN (' . $post_authors_format . ')', $filter->post_authors );
547 }
548
549 $filter = apply_filters( 'lp/course/query/filter', $filter );
550
551 return $this->execute( $filter, $total_rows );
552 }
553
554 /**
555 * Get list courses sort by price
556 *
557 * @param LP_Course_Filter $filter
558 *
559 * @return LP_Course_Filter
560 * @since 4.1.5
561 * @author tungnx
562 * @version 1.0.0
563 */
564 public function get_courses_order_by_price( LP_Course_Filter $filter ): LP_Course_Filter {
565 $filter->join[] = "INNER JOIN $this->tb_postmeta AS pm ON p.ID = pm.post_id";
566 $filter->where[] = $this->wpdb->prepare( 'AND pm.meta_key = %s', '_lp_price' );
567 $filter->order_by = 'CAST( pm.meta_value AS UNSIGNED )';
568
569 return $filter;
570 }
571
572 /**
573 * Get list courses is on sale
574 *
575 * @param LP_Course_Filter $filter
576 *
577 * @return LP_Course_Filter
578 * @since 4.1.5
579 * @author tungnx
580 * @version 1.0.0
581 */
582 public function get_courses_sort_by_sale( LP_Course_Filter $filter ): LP_Course_Filter {
583 $filter->join[] = "INNER JOIN $this->tb_postmeta AS pm ON p.ID = pm.post_id";
584 $filter->where[] = $this->wpdb->prepare( 'AND pm.meta_key = %s', '_lp_course_is_sale' );
585
586 return $filter;
587 }
588
589 /**
590 * Get list courses is on feature
591 *
592 * @param LP_Course_Filter $filter
593 *
594 * @return LP_Course_Filter
595 * @author tungnx
596 * @version 1.0.0
597 * @since 4.1.5
598 */
599 public function get_courses_sort_by_feature( LP_Course_Filter $filter ): LP_Course_Filter {
600 $filter->join[] = "INNER JOIN $this->tb_postmeta AS pm ON p.ID = pm.post_id";
601 $filter->where[] = $this->wpdb->prepare( 'AND pm.meta_key = %s', '_lp_featured' );
602 $filter->where[] = $this->wpdb->prepare( 'AND pm.meta_value = %s', 'yes' );
603
604 return $filter;
605 }
606
607 /**
608 * Get list courses is on popular
609 * Use "UNION" to merge 2 query
610 * @param LP_Course_Filter $filter
611 *
612 * @return LP_Course_Filter
613 * @throws Exception
614 * @version 1.0.0
615 * @since 4.1.6
616 * @author minhpd
617 */
618 public function get_courses_order_by_popular( LP_Course_Filter &$filter ): LP_Course_Filter {
619 // Set list name columns get
620 $columns_table_posts = $this->get_cols_of_table( $this->tb_posts );
621 $filter->fields = array_merge( $columns_table_posts, $filter->fields );
622
623 $filter_user_course = clone $filter;
624 $filter_course_not_attend = clone $filter;
625
626 // Query get users total attend courses
627 $filter_user_course->fields = array( 'ID', 'COUNT(ID) AS total' );
628 $filter_user_course->only_fields = [];
629 $filter_user_course->join[] = "INNER JOIN {$this->tb_lp_user_items} AS ui ON p.ID = ui.item_id";
630 $filter_user_course->where[] = $this->wpdb->prepare( 'AND ui.item_type = %s', LP_COURSE_CPT );
631 $filter_user_course->where[] = $this->wpdb->prepare(
632 'AND (status = %s OR status = %s OR status = %s)',
633 LP_COURSE_ENROLLED,
634 LP_COURSE_PURCHASED,
635 LP_COURSE_FINISHED
636 );
637 $filter_user_course->group_by = 'p.ID';
638 $filter_user_course->return_string_query = true;
639 $query_user_course = LP_Course_DB::getInstance()->get_courses( $filter_user_course );
640
641 // Query get courses not attend
642 $filter_user_course_cl = clone $filter_user_course;
643 $filter_user_course_cl->only_fields = array( 'ID' );
644 $query_user_course_for_not_in = LP_Course_DB::getInstance()->get_courses( $filter_user_course_cl );
645
646 $filter_course_not_attend->fields = [ 'ID', '0 AS total' ];
647 $filter_course_not_attend->only_fields = [];
648 $filter_course_not_attend->where[] = 'AND p.ID NOT IN(' . $query_user_course_for_not_in . ')';
649
650 $filter_course_not_attend->return_string_query = true;
651 $query_course_not_attend = LP_Course_DB::getInstance()->get_courses( $filter_course_not_attend );
652
653 $filter->union[] = $query_user_course;
654 $filter->union[] = $query_course_not_attend;
655 $filter->order_by = 'total';
656 $filter->order = 'DESC';
657
658 return $filter;
659 }
660
661 /**
662 * Get total courses of Author
663 *
664 * @param int $author_id
665 *
666 * @return LP_Course_Filter
667 * @since 4.1.6
668 * @version 1.0.0
669 * @throws Exception
670 */
671 public function count_courses_publish_of_author( int $author_id ): LP_Course_Filter {
672 $filter_course = new LP_Course_Filter();
673 $filter_course->only_fields = array( 'ID' );
674 $filter_course->post_author = $author_id;
675 $filter_course->post_status = 'publish';
676 $filter_course->field_count = 'ID';
677 $filter_course->query_count = true;
678
679 return apply_filters( 'lp/user/course/query/filter/count-users-attend-courses-of-author', $filter_course );
680 }
681 }
682
683 LP_Course_DB::getInstance();
684
685