diff --git a/classes/Instructors_List.php b/classes/Instructors_List.php index e357f15127..aa290b7f8f 100644 --- a/classes/Instructors_List.php +++ b/classes/Instructors_List.php @@ -384,16 +384,8 @@ public static function get_instructors( array $status, $offset, $per_page, $sear $query = "SELECT DISTINCT user.*, - ins_status.meta_value AS status, - ( - SELECT - COUNT(*) - FROM {$wpdb->posts} - WHERE post_author = user.ID - AND post_type = 'courses' - ) total_courses + ins_status.meta_value AS status FROM {$wpdb->users} AS user - INNER JOIN {$wpdb->usermeta} AS ins_status ON ( user.ID = ins_status.user_id ) AND ins_status.meta_key = '_tutor_instructor_status' diff --git a/classes/Quiz_Attempts_List.php b/classes/Quiz_Attempts_List.php index c1491838b1..89251b6df0 100644 --- a/classes/Quiz_Attempts_List.php +++ b/classes/Quiz_Attempts_List.php @@ -237,7 +237,6 @@ public function tabs_key_value( $user_id, $course_id, $date, $search ): array { * * @since 4.0.0 * - * @param int $quiz_attempts_count the quiz attempt count. * @param string $url the url. * @param string $result_filter the current result state. * @param string $search_filter the search filter. @@ -249,24 +248,24 @@ public function tabs_key_value( $user_id, $course_id, $date, $search ): array { * * @return array */ - public function get_quiz_attempts_nav_data( $quiz_attempts_count = 0, $url = '', $result_filter = '', $search_filter = '', $course_filter = 0, $start_date = '', $end_date = '', $order_filter = 'DESC', $all_quizzes = array() ): array { + public function get_quiz_attempts_nav_data( $url = '', $result_filter = '', $search_filter = '', $course_filter = 0, $start_date = '', $end_date = '', $order_filter = 'DESC', $all_quizzes = array() ): array { $quiz_model = new QuizModel(); if ( tutor_utils()->count( $all_quizzes ) ) { - $results = isset( $all_quizzes['results'] ) ? $all_quizzes['results'] : array(); - $all_attempts = isset( $all_quizzes['total_count'] ) ? $all_quizzes['total_count'] : 0; - $quiz_attempts_count = $all_attempts; - $passed_attempts = count( $quiz_model->get_formatted_quiz_attempt_list_by_quiz_id( $results, QuizModel::RESULT_PASS ) ); - $failed_attempts = count( $quiz_model->get_formatted_quiz_attempt_list_by_quiz_id( $results, QuizModel::RESULT_FAIL ) ); - $pending_attempts = count( $quiz_model->get_formatted_quiz_attempt_list_by_quiz_id( $results, QuizModel::RESULT_PENDING ) ); + $results = isset( $all_quizzes['results'] ) ? $all_quizzes['results'] : array(); + $all_attempts = isset( $all_quizzes['total_count'] ) ? $all_quizzes['total_count'] : 0; + $passed_attempts = count( $quiz_model->get_formatted_quiz_attempt_list_by_quiz_id( $results, QuizModel::RESULT_PASS ) ); + $failed_attempts = count( $quiz_model->get_formatted_quiz_attempt_list_by_quiz_id( $results, QuizModel::RESULT_FAIL ) ); + $pending_attempts = count( $quiz_model->get_formatted_quiz_attempt_list_by_quiz_id( $results, QuizModel::RESULT_PENDING ) ); } else { - $all_attempts = QuizModel::get_quiz_attempts( 0, 0, $search_filter, $course_filter > 0 ? $course_filter : '', $start_date, $end_date, $order_filter, '', true, true ); - $pending_attempts = QuizModel::get_quiz_attempts( 0, 0, $search_filter, $course_filter > 0 ? $course_filter : '', $start_date, $end_date, $order_filter, QuizModel::RESULT_PENDING, true, true ); - $passed_attempts = QuizModel::get_quiz_attempts( 0, 0, $search_filter, $course_filter > 0 ? $course_filter : '', $start_date, $end_date, $order_filter, QuizModel::RESULT_PASS, true, true ); - $failed_attempts = QuizModel::get_quiz_attempts( 0, 0, $search_filter, $course_filter > 0 ? $course_filter : '', $start_date, $end_date, $order_filter, QuizModel::RESULT_FAIL, true, true ); + $all_attempts = (int) QuizModel::get_quiz_attempts( 0, 0, $search_filter, $course_filter > 0 ? $course_filter : '', $start_date, $end_date, $order_filter, '', true, true ); + $pending_attempts = (int) QuizModel::get_quiz_attempts( 0, 0, $search_filter, $course_filter > 0 ? $course_filter : '', $start_date, $end_date, $order_filter, QuizModel::RESULT_PENDING, true, true ); + $passed_attempts = (int) QuizModel::get_quiz_attempts( 0, 0, $search_filter, $course_filter > 0 ? $course_filter : '', $start_date, $end_date, $order_filter, QuizModel::RESULT_PASS, true, true ); + $failed_attempts = (int) QuizModel::get_quiz_attempts( 0, 0, $search_filter, $course_filter > 0 ? $course_filter : '', $start_date, $end_date, $order_filter, QuizModel::RESULT_FAIL, true, true ); } - $filter_url = remove_query_arg( 'current_page', $url ); + $quiz_attempts_count = array_sum( array( $passed_attempts, $failed_attempts, $pending_attempts ) ); + $filter_url = remove_query_arg( 'current_page', $url ); $nav_links = array( 'type' => 'dropdown', diff --git a/classes/Utils.php b/classes/Utils.php index c3b45acae6..b3a7f5c082 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -1470,9 +1470,13 @@ public function get_course_first_lesson( $course_id = 0, $post_type = null ) { $course_id = $this->get_post_id( $course_id ); $user_id = get_current_user_id(); - $lessons = $wpdb->get_results( - $wpdb->prepare( - "SELECT items.ID + $course_first_lesson_cache_key = 'tutor_course_first_lesson_' . $course_id . '_' . $post_type; + $cache = TutorCache::get( $course_first_lesson_cache_key ); + + if ( false === $cache ) { + $lessons = $wpdb->get_results( + $wpdb->prepare( + "SELECT items.ID FROM {$wpdb->posts} topic INNER JOIN {$wpdb->posts} items ON topic.ID = items.post_parent @@ -1482,10 +1486,15 @@ public function get_course_first_lesson( $course_id = 0, $post_type = null ) { ORDER BY topic.menu_order ASC, items.menu_order ASC; ', - $course_id, - 'publish' - ) - ); + $course_id, + 'publish' + ) + ); + + TutorCache::set( $course_first_lesson_cache_key, $lessons ); + } else { + $lessons = $cache; + } $first_lesson = false; @@ -2108,6 +2117,12 @@ public function get_completed_courses_ids_by_user( $user_id = 0 ) { $user_id = $this->get_user_id( $user_id ); + $cache_key = 'tutor_completed_courses_ids_by_user_' . $user_id; + $cache = TutorCache::get( $cache_key ); + if ( false !== $cache ) { + return $cache; + } + $course_ids = (array) $wpdb->get_col( $wpdb->prepare( "SELECT comment_post_ID AS course_id @@ -2127,6 +2142,8 @@ public function get_completed_courses_ids_by_user( $user_id = 0 ) { ) ); + TutorCache::set( $cache_key, $course_ids ); + return $course_ids; } @@ -2158,6 +2175,12 @@ public function get_enrolled_courses_ids_by_user( $user_id = 0, $with_bundle_enr ); } + $cache_key = 'tutor_enrolled_courses_ids_by_user_' . $user_id . '_bundle_' . $with_bundle_enrolled_courses; + $cached = TutorCache::get( $cache_key ); + if ( false !== $cached ) { + return $cached; + } + $course_ids = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT e.post_parent, e.post_date @@ -2174,6 +2197,8 @@ public function get_enrolled_courses_ids_by_user( $user_id = 0, $with_bundle_enr ) ); + TutorCache::set( $cache_key, (array) $course_ids ); + return $course_ids; } @@ -3255,15 +3280,23 @@ public function get_total_students_by_instructor( $instructor_id, $args = array( $course_post_type = tutor()->course_post_type; $enrollment_date_clause = ''; + $cache_key = __FUNCTION__ . "_{$instructor_id}"; if ( ! empty( $args['from'] ) && ! empty( $args['to'] ) ) { $from = Input::sanitize( $args['from'] ); $to = Input::sanitize( $args['to'] ); + $cache_key .= "_{$from}_{$to}"; + $where['enrollment.post_date'] = array( 'BETWEEN', array( $from, $to ) ); $enrollment_date_clause = ' AND ' . QueryHelper::prepare_where_clause( $where ); } + $cached = TutorCache::get( $cache_key ); + if ( false !== $cached ) { + return $cached; + } + $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT(enrollment.post_author)) @@ -3284,6 +3317,8 @@ public function get_total_students_by_instructor( $instructor_id, $args = array( ) ); + TutorCache::set( $cache_key, $count ); + return (int) $count; } @@ -3484,9 +3519,16 @@ public function get_courses_by_student_instructor_id( int $student_id, int $inst */ public function get_completed_assignment( int $course_id, int $student_id ): int { global $wpdb; + $course_id = sanitize_text_field( $course_id ); $student_id = sanitize_text_field( $student_id ); - $count = $wpdb->get_var( + $cache_key = __FUNCTION__ . "_{$course_id}_{$student_id}"; + $cached = TutorCache::get( $cache_key ); + if ( false !== $cached ) { + return $cached; + } + + $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( DISTINCT ID ) FROM {$wpdb->posts} INNER JOIN {$wpdb->comments} c ON c.comment_post_ID = ID AND c.user_id = %d AND c.comment_approved = %s @@ -3503,6 +3545,8 @@ public function get_completed_assignment( int $course_id, int $student_id ): int 'publish' ) ); + + TutorCache::set( $cache_key, (int) $count ); return (int) $count; } @@ -3520,7 +3564,13 @@ public function get_completed_quiz( int $course_id, int $student_id ): int { global $wpdb; $course_id = sanitize_text_field( $course_id ); $student_id = sanitize_text_field( $student_id ); - $count = $wpdb->get_var( + $cache_key = __FUNCTION__ . "_{$course_id}_{$student_id}"; + $cached = TutorCache::get( $cache_key ); + if ( false !== $cached ) { + return $cached; + } + + $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT quiz_id) AS total FROM {$wpdb->prefix}tutor_quiz_attempts @@ -3533,6 +3583,8 @@ public function get_completed_quiz( int $course_id, int $student_id ): int { 'attempt_ended' ) ); + + TutorCache::set( $cache_key, (int) $count ); return (int) $count; } @@ -7265,6 +7317,11 @@ public function get_student_emails_by_course_id( $course_id = 0 ) { */ public function get_single_comment_user_post_id( $post_id, $user_id ) { global $wpdb; + $cache_key = __FUNCTION__ . "_{$post_id}_{$user_id}"; + $cached = TutorCache::get( $cache_key ); + if ( false !== $cached ) { + return $cached; + } $table = $wpdb->prefix . 'comments'; $query = $wpdb->get_row( $wpdb->prepare( @@ -7278,6 +7335,8 @@ public function get_single_comment_user_post_id( $post_id, $user_id ) { $user_id ) ); + + TutorCache::set( $cache_key, $query ); return $query ? $query : false; } @@ -8465,17 +8524,26 @@ public function has_attempted_quiz( $user_id, $quiz_id, $row = false ) { // Sanitize data. $user_id = sanitize_text_field( $user_id ); $quiz_id = sanitize_text_field( $quiz_id ); + $cache_key = 'tutor_has_attempted_quiz_' . $user_id . '_' . $quiz_id; + $cache = TutorCache::get( $cache_key ); + + if ( false !== $cache ) { + return $cache ? true : false; + } + $attempted = $wpdb->get_row( $wpdb->prepare( "SELECT quiz_id FROM {$wpdb->tutor_quiz_attempts} WHERE user_id = %d - AND quiz_id = %d - ", + AND quiz_id = %d", $user_id, $quiz_id ) ); + + TutorCache::set( $cache_key, $attempted ); + return $attempted ? true : false; } @@ -9257,7 +9325,7 @@ public function hsl_to_hex( $h, $s, $l ) { if ( 0 === $s ) { $r = $g = $b = $l; //phpcs:ignore } else { - $hue2rgb = function( $p, $q, $t ) { + $hue2rgb = function ( $p, $q, $t ) { if ( $t < 0 ) { ++$t; } @@ -9286,9 +9354,9 @@ public function hsl_to_hex( $h, $s, $l ) { /** * Get brand color - * + * * @since 4.0.0 - * + * * @return string */ public function get_brand_color() { @@ -9298,9 +9366,9 @@ public function get_brand_color() { /** * Get default brand color - * + * * @since 4.0.0 - * + * * @return string */ public function get_default_brand_color() { diff --git a/ecommerce/CheckoutController.php b/ecommerce/CheckoutController.php index 7172c2b4d9..935138c9e3 100644 --- a/ecommerce/CheckoutController.php +++ b/ecommerce/CheckoutController.php @@ -463,7 +463,7 @@ public function prepare_checkout_items( $item_ids, $order_type = OrderModel::TYP $should_calculate_tax = Tax::should_calculate_tax(); $tax_included = Tax::is_tax_included_in_price(); - $tax_rate = Tax::get_user_tax_rate(); + $tax_rate = $should_calculate_tax ? Tax::get_user_tax_rate() : 0; // Keep calculated price for each item. foreach ( $items as $item ) { diff --git a/models/CourseModel.php b/models/CourseModel.php index b866ced236..6b9ce4465d 100644 --- a/models/CourseModel.php +++ b/models/CourseModel.php @@ -16,6 +16,7 @@ use TUTOR\Lesson; use Tutor\Ecommerce\Tax; use InvalidArgumentException; +use Tutor\Cache\TutorCache; use Tutor\Helpers\QueryHelper; use TUTOR\User; use TUTOR_ASSIGNMENTS\Assignments; @@ -235,16 +236,38 @@ public static function get_courses_by_args( array $args = array() ) { * Get course count by instructor * * @since 1.0.0 + * @since 4.0.0 param $post_type,$post_status and $search added. * - * @param int $instructor_id instructor ID. + * @param int $instructor_id instructor ID. + * @param array $post_type the post types. + * @param array $post_status the post statuses. + * @param string $search the search term. * - * @return null|string + * @return int */ - public static function get_course_count_by_instructor( $instructor_id ) { + public static function get_course_count_by_instructor( $instructor_id, $post_type = array( self::POST_TYPE ), $post_status = array( self::STATUS_PUBLISH ), $search = '' ) { global $wpdb; - $course_post_type = tutor()->course_post_type; + $cache_key = 'tutor_course_count_by_instructor_' . $instructor_id . '_' . implode( '_', $post_type ) . '_' . implode( '_', $post_status ) . '_' . $search; + + $cache = TutorCache::get( $cache_key ); + if ( false !== $cache ) { + return (int) $cache; + } + + $course_post_type = QueryHelper::prepare_in_clause( $post_type ); + $course_post_status = QueryHelper::prepare_in_clause( $post_status ); + $search_sql = ''; + if ( ! empty( $search ) ) { + $like = '%' . $wpdb->esc_like( $search ) . '%'; + $search_sql = $wpdb->prepare( + " AND ( {$wpdb->posts}.post_title LIKE %s OR {$wpdb->posts}.post_excerpt LIKE %s ) ", + $like, + $like + ); + } + // phpcs:disable $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) @@ -253,15 +276,17 @@ public static function get_course_count_by_instructor( $instructor_id ) { ON user_id = %d AND meta_key = %s AND meta_value = ID - WHERE post_status = %s - AND post_type = %s; + WHERE post_status IN ({$course_post_status}) + AND post_type IN ({$course_post_type}) + {$search_sql} ", $instructor_id, '_tutor_instructor_course_id', - 'publish', - $course_post_type ) ); + // phpcs:enable + + TutorCache::set( $cache_key, $count ); return $count; } @@ -359,7 +384,7 @@ public static function get_courses_by_instructor( $query = $wpdb->prepare( "SELECT $select_col FROM $wpdb->posts - LEFT JOIN {$wpdb->usermeta} + INNER JOIN {$wpdb->usermeta} ON $wpdb->usermeta.user_id = %d AND $wpdb->usermeta.meta_key = %s AND $wpdb->usermeta.meta_value = $wpdb->posts.ID @@ -1424,10 +1449,18 @@ public static function get_course_count_by_date( $start_date, $end_date, $user_i $user_id = tutor_utils()->get_user_id( $user_id ); if ( empty( $start_date ) && empty( $end_date ) ) { - return (int) self::get_courses_by_instructor( $user_id, array( 'publish', 'private' ), 0, PHP_INT_MAX, true ); + return (int) self::get_course_count_by_instructor( $user_id, array( tutor()->course_post_type ), array( 'publish', 'private' ) ); } - $courses = self::get_courses_by_instructor( $user_id, array( 'publish', 'private' ), 0, PHP_INT_MAX ); + $cache_key = 'tutor_course_count_by_date_' . $user_id; + + $courses = TutorCache::get( $cache_key ); + + if ( ! $courses ) { + // Don't need to call this twice. + $courses = self::get_courses_by_instructor( $user_id, array( 'publish', 'private' ), 0, PHP_INT_MAX ); + TutorCache::set( $cache_key, $courses ); + } if ( empty( $courses ) ) { return 0; diff --git a/models/OrderModel.php b/models/OrderModel.php index c7c95a4c8e..44e0db419b 100644 --- a/models/OrderModel.php +++ b/models/OrderModel.php @@ -2229,9 +2229,12 @@ public static function get_order_history_titles( $order ) { $titles = array(); $items = ( new OrderModel() )->get_order_items_by_id( $order->id ); foreach ( $items as $item ) { - if ( self::TYPE_SINGLE_ORDER === $order->order_type ) { - $titles[] = get_the_title( $item->id ); + if ( empty( $item->title ) ) { + continue; + } + + $titles[] = $item->title; continue; } diff --git a/models/QuizModel.php b/models/QuizModel.php index 70d3830775..bf15c1caf7 100644 --- a/models/QuizModel.php +++ b/models/QuizModel.php @@ -971,17 +971,27 @@ public static function get_quiz_answers_by_attempt_id( $attempt_id, $add_index = return array(); } - //phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared - $results = $wpdb->get_results( - "SELECT answers.*, + $cache_key = __FUNCTION__ . implode( '_', $ids ); + $cached = TutorCache::get( $cache_key ); + $results = array(); + + if ( false === $cached ) { + //phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared + $results = $wpdb->get_results( + "SELECT answers.*, question.* FROM {$wpdb->prefix}tutor_quiz_attempt_answers answers LEFT JOIN {$wpdb->prefix}tutor_quiz_questions question ON answers.question_id = question.question_id WHERE answers.quiz_attempt_id IN ({$ids_in}) ORDER BY attempt_answer_id ASC;" - ); - //phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared + ); + //phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared + + TutorCache::set( $cache_key, $results ); + } else { + $results = $cached; + } if ( $add_index ) { $new_array = array(); diff --git a/templates/dashboard.php b/templates/dashboard.php index 6d8b0691c7..03fec1cb77 100644 --- a/templates/dashboard.php +++ b/templates/dashboard.php @@ -97,13 +97,15 @@ 'icon_class' => 'ttr tutor-icon-hamburger-o tutor-dashboard-menu-toggler', ), ); + +$enrolled_student_count = (int) tutor_utils()->get_total_students_by_instructor( $user_id ); ?>