From f712f4222c0e3108de4783d27692d2a9490ca2c6 Mon Sep 17 00:00:00 2001 From: Anindra Das Bivas Date: Fri, 12 Jun 2026 16:12:25 +0600 Subject: [PATCH 01/12] Query Optimization --- models/CourseModel.php | 45 ++++++++++++++++++----- templates/dashboard/components/header.php | 3 +- templates/dashboard/my-courses.php | 8 ++-- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/models/CourseModel.php b/models/CourseModel.php index b866ced236..85301b8acd 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_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,7 +1449,7 @@ 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 ); diff --git a/templates/dashboard/components/header.php b/templates/dashboard/components/header.php index 621b27aca2..13b005ee0a 100644 --- a/templates/dashboard/components/header.php +++ b/templates/dashboard/components/header.php @@ -50,7 +50,8 @@ course_post_type ) ); + $active_course_count = (int) CourseModel::get_course_count_by_instructor( $user_id, $course_post_type ); $enrolled_student_count = (int) tutor_utils()->get_total_students_by_instructor( $user_id ); ?>
diff --git a/templates/dashboard/my-courses.php b/templates/dashboard/my-courses.php index d60020c68a..29f478dfd8 100644 --- a/templates/dashboard/my-courses.php +++ b/templates/dashboard/my-courses.php @@ -46,10 +46,10 @@ // Get counts for course tabs. $count_map = array( - 'publish' => CourseModel::get_courses_by_instructor( $current_user_id, CourseModel::STATUS_PUBLISH, 0, 0, true, $course_post_type, $search_term ), - 'pending' => CourseModel::get_courses_by_instructor( $current_user_id, CourseModel::STATUS_PENDING, 0, 0, true, $course_post_type, $search_term ), - 'draft' => CourseModel::get_courses_by_instructor( $current_user_id, CourseModel::STATUS_DRAFT, 0, 0, true, $course_post_type, $search_term ), - 'future' => CourseModel::get_courses_by_instructor( $current_user_id, CourseModel::STATUS_FUTURE, 0, 0, true, $course_post_type, $search_term ), + 'publish' => CourseModel::get_course_count_by_instructor( $current_user_id, $course_post_type, array( CourseModel::STATUS_PUBLISH ), $search_term ), + 'pending' => CourseModel::get_course_count_by_instructor( $current_user_id, $course_post_type, array( CourseModel::STATUS_PENDING ), $search_term ), + 'draft' => CourseModel::get_course_count_by_instructor( $current_user_id, $course_post_type, array( CourseModel::STATUS_DRAFT ), $search_term ), + 'future' => CourseModel::get_course_count_by_instructor( $current_user_id, $course_post_type, array( CourseModel::STATUS_FUTURE ), $search_term ), ); $item_per_page = tutor_utils()->get_option( 'pagination_per_page', 10 ); From 867794aa9d866cb3ffc036d41e1c5100b2139b69 Mon Sep 17 00:00:00 2001 From: Anindra Das Bivas Date: Mon, 15 Jun 2026 13:34:27 +0600 Subject: [PATCH 02/12] remove student count duplicate queries --- templates/dashboard.php | 8 +++++--- templates/dashboard/components/header.php | 7 +++---- templates/dashboard/dashboard.php | 2 +- templates/dashboard/instructor/home.php | 8 +++++--- 4 files changed, 14 insertions(+), 11 deletions(-) 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 ); ?>
- + $enrolled_student_count ) ); ?>
$enrolled_student_count ) ); } else { // Load template from other location full abspath. include_once $from_other_location; @@ -129,7 +131,7 @@ do_action( 'tutor_load_dashboard_template_after', $dashboard_page_name ); } elseif ( User::is_instructor_view() ) { - tutor_load_template( 'dashboard.dashboard' ); + tutor_load_template( 'dashboard.dashboard', array( 'total_students' => $enrolled_student_count ) ); } else { tutor_load_template( 'dashboard.student.dashboard' ); } diff --git a/templates/dashboard/components/header.php b/templates/dashboard/components/header.php index a89da56db8..8850440ed4 100644 --- a/templates/dashboard/components/header.php +++ b/templates/dashboard/components/header.php @@ -50,9 +50,8 @@
course_post_type ) ); - $active_course_count = (int) CourseModel::get_course_count_by_instructor( $user_id, $course_post_type ); - $enrolled_student_count = (int) tutor_utils()->get_total_students_by_instructor( $user_id ); + $course_post_type = apply_filters( 'tutor_dashboard_course_list_post_type', array( tutor()->course_post_type ) ); + $active_course_count = (int) CourseModel::get_course_count_by_instructor( $user_id, $course_post_type ); ?>
%s student enrolled', '%s students enrolled', - $enrolled_student_count, + $enrolled_student_count ?? 0, 'tutor' ), esc_html( number_format_i18n( $enrolled_student_count ) ) diff --git a/templates/dashboard/dashboard.php b/templates/dashboard/dashboard.php index 1525d7bbf0..7c4df1ac0d 100644 --- a/templates/dashboard/dashboard.php +++ b/templates/dashboard/dashboard.php @@ -13,4 +13,4 @@ do_action( 'tutor_before_dashboard_content' ); tutor_load_template( 'dashboard.components.profile-completion' ); -tutor_load_template( 'dashboard.instructor.home' ); +tutor_load_template( 'dashboard.instructor.home', array( 'total_students' => $total_students ?? 0 ) ); diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index c92e24b4d9..cd1d55c4f9 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -85,7 +85,9 @@ : 0; // Total Students. -$total_students = tutor_utils()->get_total_students_by_instructor( $user->ID, $date_range( $start_date, $end_date ) ); +if ( $start_date && $end_date ) { + $total_students = tutor_utils()->get_total_students_by_instructor( $user->ID, $date_range( $start_date, $end_date ) ); +} $previous_period_students = ! $is_all_time ? tutor_utils()->get_total_students_by_instructor( $user->ID, $date_range( $previous_dates['previous_start_date'], $previous_dates['previous_end_date'] ) ) : 0; @@ -112,7 +114,7 @@ if ( $tutor_pro_enabled && ! $is_all_time ) { $total_earnings_state_card_details = $stat( $total_earnings, $previous_period_earnings, $previous_dates ); $total_courses_state_card_details = $stat( $total_courses, $previous_period_courses, $previous_dates ); - $total_students_state_card_details = $stat( $total_students, $previous_period_students, $previous_dates ); + $total_students_state_card_details = $stat( $total_students ?? 0, $previous_period_students, $previous_dates ); $total_ratings_state_card_details = $stat( $total_ratings->rating_avg, $previous_period_ratings->rating_avg, $previous_dates ); } @@ -140,7 +142,7 @@ 'variation' => 'exception5', 'title' => esc_html__( 'Total Students', 'tutor' ), 'icon' => Icon::PASSED, - 'value' => $total_students, + 'value' => $total_students ?? 0, 'hover_content' => $total_students_state_card_details, ), array( From 62901e8040eb0367524ea78387f5c66deb663791 Mon Sep 17 00:00:00 2001 From: Anindra Das Bivas Date: Tue, 16 Jun 2026 16:15:00 +0600 Subject: [PATCH 03/12] duplicate queries for instructor home page --- models/CourseModel.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/models/CourseModel.php b/models/CourseModel.php index 85301b8acd..097f8ab510 100644 --- a/models/CourseModel.php +++ b/models/CourseModel.php @@ -1452,7 +1452,15 @@ public static function get_course_count_by_date( $start_date, $end_date, $user_i 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; From fef2d00b88bb3d3f278e1f77d5b8422588cb0f86 Mon Sep 17 00:00:00 2001 From: Anindra Das Bivas Date: Wed, 17 Jun 2026 13:31:10 +0600 Subject: [PATCH 04/12] updated get_course_price is getting called two times --- models/CourseModel.php | 2 +- templates/dashboard/components/header.php | 3 +-- templates/dashboard/my-courses.php | 5 +++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/models/CourseModel.php b/models/CourseModel.php index 097f8ab510..6b9ce4465d 100644 --- a/models/CourseModel.php +++ b/models/CourseModel.php @@ -248,7 +248,7 @@ public static function get_courses_by_args( array $args = array() ) { 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; - $cache_key = 'tutor_course_count_by_instructor_' . $instructor_id . implode( '_', $post_status ) . $search; + $cache_key = 'tutor_course_count_by_instructor_' . $instructor_id . '_' . implode( '_', $post_type ) . '_' . implode( '_', $post_status ) . '_' . $search; $cache = TutorCache::get( $cache_key ); if ( false !== $cache ) { diff --git a/templates/dashboard/components/header.php b/templates/dashboard/components/header.php index 8850440ed4..2b600dfed3 100644 --- a/templates/dashboard/components/header.php +++ b/templates/dashboard/components/header.php @@ -50,8 +50,7 @@
course_post_type ) ); - $active_course_count = (int) CourseModel::get_course_count_by_instructor( $user_id, $course_post_type ); + $active_course_count = (int) CourseModel::get_course_count_by_instructor( $user_id, array( tutor()->course_post_type, tutor()->bundle_post_type ) ); ?>
get_course_price() ) { + $course_price = tutor_utils()->get_course_price(); + if ( null === $course_price ) { esc_html_e( 'Free', 'tutor' ); } else { - echo wp_kses_post( tutor_utils()->get_course_price() ); + echo wp_kses_post( $course_price ); } ?>
From 506c81bdbe09b446338cf3090d07227d9bd90004 Mon Sep 17 00:00:00 2001 From: Anindra Das Bivas Date: Wed, 17 Jun 2026 17:42:21 +0600 Subject: [PATCH 05/12] student dashboard area duplicate query fixes --- classes/Utils.php | 52 +++++++++++++++++++---- templates/dashboard/student/dashboard.php | 6 +-- templates/dashboard/student/stats.php | 5 +-- 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index 5c23a36a21..2c339e445d 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -1469,9 +1469,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; + $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 @@ -1481,10 +1485,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; @@ -2107,6 +2116,12 @@ public function get_completed_courses_ids_by_user( $user_id = 0 ) { $user_id = $this->get_user_id( $user_id ); + $completed_courses_cache_key = 'tutor_completed_courses_ids_by_user_' . $user_id; + $cache = TutorCache::get( $completed_courses_cache_key ); + if ( false !== $cache ) { + return $cache; + } + $course_ids = (array) $wpdb->get_col( $wpdb->prepare( "SELECT comment_post_ID AS course_id @@ -2126,6 +2141,8 @@ public function get_completed_courses_ids_by_user( $user_id = 0 ) { ) ); + TutorCache::set( $completed_courses_cache_key, $course_ids ); + return $course_ids; } @@ -2157,6 +2174,12 @@ public function get_enrolled_courses_ids_by_user( $user_id = 0, $with_bundle_enr ); } + $enrolled_courses_cache_key = 'tutor_enrolled_courses_ids_by_user_' . $user_id . '_bundle_' . $with_bundle_enrolled_courses; + $cache = TutorCache::get( $enrolled_courses_cache_key ); + if ( false !== $cache ) { + return $cache; + } + $course_ids = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT e.post_parent, e.post_date @@ -2173,6 +2196,8 @@ public function get_enrolled_courses_ids_by_user( $user_id = 0, $with_bundle_enr ) ); + TutorCache::set( $enrolled_courses_cache_key, (array) $course_ids ); + return $course_ids; } @@ -8464,17 +8489,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; } diff --git a/templates/dashboard/student/dashboard.php b/templates/dashboard/student/dashboard.php index de3427c77d..fdbf1939a7 100644 --- a/templates/dashboard/student/dashboard.php +++ b/templates/dashboard/student/dashboard.php @@ -11,7 +11,6 @@ defined( 'ABSPATH' ) || exit; -use TUTOR\Course; use Tutor\Models\CourseModel; $user_id = get_current_user_id(); @@ -31,8 +30,9 @@ tutor_load_template( 'dashboard.student.stats', array( - 'user_id' => $user_id, - 'user_data' => $user_data, + 'user_id' => $user_id, + 'user_data' => $user_data, + 'enrolled_course' => $enrolled_course, ) ); diff --git a/templates/dashboard/student/stats.php b/templates/dashboard/student/stats.php index 1827d51363..07496bcf68 100644 --- a/templates/dashboard/student/stats.php +++ b/templates/dashboard/student/stats.php @@ -18,15 +18,14 @@ $user_data = $user_data ?? get_userdata( $user_id ); // Query metrics and stats data. -$enrolled_course = CourseModel::get_enrolled_courses_by_user( $user_id, array( 'private', 'publish' ) ); $completed_courses = CourseModel::get_completed_courses_by_user( $user_id, 0, -1, array( 'post_status' => array( 'private', 'publish' ) ) ); $has_completed_courses = is_object( $completed_courses ) && $completed_courses->have_posts(); $active_courses = CourseModel::get_active_courses_by_user( $user_id, 0, -1, array( 'post_status' => array( 'private', 'publish' ) ) ); -$enrolled_course_count = $enrolled_course ? $enrolled_course->post_count : 0; +$enrolled_course_count = isset( $enrolled_course ) && $enrolled_course ? $enrolled_course->post_count : 0; $completed_course_count = $has_completed_courses ? $completed_courses->post_count : 0; $active_course_count = is_object( $active_courses ) && $active_courses->have_posts() ? $active_courses->post_count : 0; -$enrolled_courses_ids = $enrolled_course_count ? wp_list_pluck( $enrolled_course->posts, 'ID' ) : array(); +$enrolled_courses_ids = $enrolled_course_count ? wp_list_pluck( isset( $enrolled_course ) ? $enrolled_course->posts : array(), 'ID' ) : array(); // Time spent calculation. $time_spent = CourseModel::get_total_estimated_time_spent( $enrolled_courses_ids ); From 0678d40880b94f31b001cfca154e228dc9b0c511 Mon Sep 17 00:00:00 2001 From: Anindra Das Bivas Date: Thu, 18 Jun 2026 17:40:52 +0600 Subject: [PATCH 06/12] Instructor quiz attempt list, analytics and student order invoice duplicate query fixes --- classes/Quiz_Attempts_List.php | 8 +++--- classes/Utils.php | 52 ++++++++++++++++++++++++++++------ models/OrderModel.php | 29 ++++++++++++------- models/QuizModel.php | 17 +++++++---- 4 files changed, 79 insertions(+), 27 deletions(-) diff --git a/classes/Quiz_Attempts_List.php b/classes/Quiz_Attempts_List.php index c1491838b1..989875ab04 100644 --- a/classes/Quiz_Attempts_List.php +++ b/classes/Quiz_Attempts_List.php @@ -260,10 +260,10 @@ public function get_quiz_attempts_nav_data( $quiz_attempts_count = 0, $url = '', $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 = ( '' === $result_filter ) ? (int) $quiz_attempts_count : (int) QuizModel::get_quiz_attempts( 0, 0, $search_filter, $course_filter > 0 ? $course_filter : '', $start_date, $end_date, $order_filter, '', true, true ); + $pending_attempts = ( QuizModel::RESULT_PENDING === $result_filter ) ? (int) $quiz_attempts_count : (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 = ( QuizModel::RESULT_PASS === $result_filter ) ? (int) $quiz_attempts_count : (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 = ( QuizModel::RESULT_FAIL === $result_filter ) ? (int) $quiz_attempts_count : (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 ); diff --git a/classes/Utils.php b/classes/Utils.php index 2c339e445d..db4c6e1e05 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -3277,17 +3277,25 @@ function ( $instructor ) use ( $main_instructor ) { public function get_total_students_by_instructor( $instructor_id, $args = array() ) { global $wpdb; - $course_post_type = tutor()->course_post_type; - $enrollment_date_clause = ''; + $course_post_type = tutor()->course_post_type; + $enrollment_date_clause = ''; + $total_students_cache_key = __FUNCTION__ . "_{$instructor_id}"; if ( ! empty( $args['from'] ) && ! empty( $args['to'] ) ) { $from = Input::sanitize( $args['from'] ); $to = Input::sanitize( $args['to'] ); + $total_students_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( $total_students_cache_key ); + if ( false !== $cached ) { + return $cached; + } + $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT(enrollment.post_author)) @@ -3308,6 +3316,8 @@ public function get_total_students_by_instructor( $instructor_id, $args = array( ) ); + TutorCache::set( $total_students_cache_key, $count ); + return (int) $count; } @@ -3508,9 +3518,17 @@ 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( + + $course_id = sanitize_text_field( $course_id ); + $student_id = sanitize_text_field( $student_id ); + $complete_assignment_cache_key = __FUNCTION__ . "_{$course_id}_{$student_id}"; + + $cached = TutorCache::get( $complete_assignment_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 @@ -3527,6 +3545,8 @@ public function get_completed_assignment( int $course_id, int $student_id ): int 'publish' ) ); + + TutorCache::set( $complete_assignment_cache_key, (int) $count ); return (int) $count; } @@ -3542,9 +3562,16 @@ public function get_completed_assignment( int $course_id, int $student_id ): int */ 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( + $course_id = sanitize_text_field( $course_id ); + $student_id = sanitize_text_field( $student_id ); + $complete_quiz_cache_key = __FUNCTION__ . "_{$course_id}_{$student_id}"; + + $cached = TutorCache::get( $complete_quiz_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 @@ -3557,6 +3584,8 @@ public function get_completed_quiz( int $course_id, int $student_id ): int { 'attempt_ended' ) ); + + TutorCache::set( $complete_quiz_cache_key, (int) $count ); return (int) $count; } @@ -7289,6 +7318,12 @@ 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; + $single_user_comment_cache_key = __FUNCTION__ . "_{$post_id}_{$user_id}"; + + $cached = TutorCache::get( $single_user_comment_cache_key ); + if ( false !== $cached ) { + return $cached; + } $table = $wpdb->prefix . 'comments'; $query = $wpdb->get_row( $wpdb->prepare( @@ -7302,6 +7337,7 @@ public function get_single_comment_user_post_id( $post_id, $user_id ) { $user_id ) ); + TutorCache::set( $single_user_comment_cache_key, $query ); return $query ? $query : false; } diff --git a/models/OrderModel.php b/models/OrderModel.php index c7c95a4c8e..bbc38ebb70 100644 --- a/models/OrderModel.php +++ b/models/OrderModel.php @@ -482,19 +482,26 @@ public function insert_order_items( int $order_id, array $items ): bool { * the user ID from the order data, and returns the modified order data. * * @since 3.0.0 + * @since 4.0.0 param $order added. * * @global wpdb $wpdb WordPress database abstraction object. * - * @param int $order_id The ID of the order to retrieve. + * @param int $order_id The ID of the order to retrieve. + * @param \stdClass $order Optional. The order data if already fetched. * * @return object|false The order data with the student's information included, or false if no order is found. */ - public function get_order_by_id( $order_id ) { - $order_data = QueryHelper::get_row( - $this->table_name, - array( 'id' => $order_id ), - 'id' - ); + public function get_order_by_id( $order_id, $order = null ) { + $order_data = null; + if ( ! $order ) { + $order_data = QueryHelper::get_row( + $this->table_name, + array( 'id' => $order_id ), + 'id' + ); + } else { + $order_data = $order; + } if ( ! $order_data ) { return false; @@ -677,7 +684,6 @@ public function get_order_items_by_id( $order_id ) { $course->id = (int) $course->id; $course->regular_price = (float) $course->regular_price; - $course->image = get_the_post_thumbnail_url( $course->id ); // Add meta items. $order_item_meta = new OrderItemMetaModel(); @@ -2229,9 +2235,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..87c9d654c8 100644 --- a/models/QuizModel.php +++ b/models/QuizModel.php @@ -971,17 +971,24 @@ 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.*, + $quiz_answers_cache_key = __FUNCTION__ . implode( '_', $ids ); + $results = TutorCache::get( $quiz_answers_cache_key ); + + if ( false === $results ) { + //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( $quiz_answers_cache_key, $results ); + } if ( $add_index ) { $new_array = array(); From b2259f3c22095f9aadb363af16be4cdfda9fd089 Mon Sep 17 00:00:00 2001 From: Anindra Das Bivas Date: Fri, 19 Jun 2026 17:52:06 +0600 Subject: [PATCH 07/12] Fix: tax rate duplicate query on checkout and instructor list admin page slow query --- classes/Instructors_List.php | 10 +--------- ecommerce/CheckoutController.php | 2 +- templates/ecommerce/checkout-details.php | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) 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/ecommerce/CheckoutController.php b/ecommerce/CheckoutController.php index 7172c2b4d9..007e8efc34 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() : ''; // Keep calculated price for each item. foreach ( $items as $item ) { diff --git a/templates/ecommerce/checkout-details.php b/templates/ecommerce/checkout-details.php index 5a7fcc7bc9..0445dc0976 100644 --- a/templates/ecommerce/checkout-details.php +++ b/templates/ecommerce/checkout-details.php @@ -50,7 +50,7 @@ $should_calculate_tax = Tax::should_calculate_tax(); $is_tax_included_in_price = Tax::is_tax_included_in_price(); -$tax_rate = Tax::get_user_tax_rate( $user_id ); +$tax_rate = $should_calculate_tax ? Tax::get_user_tax_rate( $user_id ) : ''; $checkout_data = $checkout_controller->prepare_checkout_items( $item_ids, $order_type, $coupon_code ); $show_coupon_box = Settings::is_coupon_usage_enabled() && ! $checkout_data->is_coupon_applied; From 96d7bd2bde180858439716473618be7f2e38c509 Mon Sep 17 00:00:00 2001 From: Anindra Das Bivas Date: Mon, 22 Jun 2026 16:15:28 +0600 Subject: [PATCH 08/12] Fix learning area sidebar duplicate query --- classes/Template.php | 7 ++++--- templates/learning-area/components/sidebar.php | 1 - templates/learning-area/index.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/classes/Template.php b/classes/Template.php index 385a59c3c2..363843d1db 100644 --- a/classes/Template.php +++ b/classes/Template.php @@ -560,16 +560,17 @@ public function load_learning_template( string $template ): string { * * @since 4.0.0 * - * @param string $base_url Nav items base url. + * @param string $base_url Nav items base url. + * @param \stdClass $is_completed_course Whether course is completed. * * @return array */ - public static function make_learning_area_sub_page_nav_items( $base_url = '' ): array { + public static function make_learning_area_sub_page_nav_items( $base_url = '', $is_completed_course = null ): array { if ( empty( $base_url ) ) { $base_url = get_permalink(); } - $menu_items = apply_filters( 'tutor_learning_area_sub_page_nav_item', array(), $base_url ); + $menu_items = apply_filters( 'tutor_learning_area_sub_page_nav_item', array(), $base_url, $is_completed_course ); $menu_items['course-info'] = array( 'title' => __( 'Course Info', 'tutor' ), diff --git a/templates/learning-area/components/sidebar.php b/templates/learning-area/components/sidebar.php index 514ce180a9..cae35d68f4 100644 --- a/templates/learning-area/components/sidebar.php +++ b/templates/learning-area/components/sidebar.php @@ -42,7 +42,6 @@ $is_preview = get_post_meta( $tutor_current_post->ID, '_is_preview', true ); $current_url = trailingslashit( $tutor_course_list_url ) . $tutor_course->post_name; -$menu_items = Template::make_learning_area_sub_page_nav_items( $current_url ); $active_menu = Template::learning_area_active_subpage(); $course_reset_progress = tutor_utils()->get_option( 'course_reset_progress', false ); diff --git a/templates/learning-area/index.php b/templates/learning-area/index.php index a35e3801b8..057e9eb583 100644 --- a/templates/learning-area/index.php +++ b/templates/learning-area/index.php @@ -44,7 +44,7 @@ $tutor_can_retake_course = $tutor_retake_course && ( CourseModel::MODE_FLEXIBLE === $tutor_completion_mode || $tutor_is_course_completed ); // Global variables defined above are used by the 'make_learning_area_sub_page_nav_items' function. -$subpages = Template::make_learning_area_sub_page_nav_items(); +$subpages = Template::make_learning_area_sub_page_nav_items( '', $tutor_is_course_completed ); $subpage = Input::get( 'subpage' ); $attempt_id = Input::get( 'attempt_id', 0, Input::TYPE_INT ); $user_action = Input::get( 'action' ); @@ -139,7 +139,7 @@ class="tutor-learning-area
- + $subpages ) ); ?>
Date: Mon, 22 Jun 2026 17:14:53 +0600 Subject: [PATCH 09/12] fix(types): improve config type definitions and assertions --- assets/core/ts/config/config.ts | 24 +++++++++++++----------- assets/core/ts/declaration.d.ts | 2 +- assets/src/js/v3/@types/index.d.ts | 5 +++-- assets/src/js/v3/shared/config/config.ts | 4 ++-- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/assets/core/ts/config/config.ts b/assets/core/ts/config/config.ts index 810374e8a0..e495944feb 100644 --- a/assets/core/ts/config/config.ts +++ b/assets/core/ts/config/config.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ const defaultTutorConfig = { ID: 0, ajaxurl: '', @@ -35,13 +36,13 @@ const defaultTutorConfig = { tutor_pn_vapid_key: '', tutor_pn_client_id: '', tutor_pn_subscription_saved: '', - difficulty_levels: [], - supported_video_sources: [], - edd_products: [], - bp_groups: [], - timezones: {}, - addons_data: [], - kids_icons_registry: [], + difficulty_levels: [] as any[], + supported_video_sources: [] as any[], + edd_products: [] as any[], + bp_groups: [] as any[], + timezones: {} as Record, + addons_data: [] as any[], + kids_icons_registry: [] as any[], is_kids_mode: false, user_preferences: { auto_play_next: false, @@ -66,10 +67,10 @@ const defaultTutorConfig = { user_status: '', display_name: '', }, - caps: {}, + caps: {} as Record, cap_key: '', - roles: [], - allcaps: {}, + roles: [] as any[], + allcaps: {} as Record, filter: null, }, settings: { @@ -108,7 +109,8 @@ const defaultTutorConfig = { }, }; -export const tutorConfig = window._tutorobject || defaultTutorConfig; +export const tutorConfig = (window._tutorobject || defaultTutorConfig) as typeof defaultTutorConfig & + Record; window.ajaxurl = tutorConfig.ajaxurl; const config = { diff --git a/assets/core/ts/declaration.d.ts b/assets/core/ts/declaration.d.ts index e162a9f0aa..9ca49f5f06 100644 --- a/assets/core/ts/declaration.d.ts +++ b/assets/core/ts/declaration.d.ts @@ -110,7 +110,7 @@ declare global { quicktags?: unknown; // Tutor object from PHP (extend existing type, don't redeclare) - _tutorobject?: Record & { + _tutorobject?: Record & { nonce_key?: string; ajaxurl?: string; tutor_url?: string; diff --git a/assets/src/js/v3/@types/index.d.ts b/assets/src/js/v3/@types/index.d.ts index 88f57101bd..7861b3fc12 100644 --- a/assets/src/js/v3/@types/index.d.ts +++ b/assets/src/js/v3/@types/index.d.ts @@ -188,8 +188,9 @@ declare global { enable_tax: boolean; enable_individual_tax_control: boolean; is_tax_included_in_price: boolean; - pagination_per_page: string; - quiz_attempts_allowed: string; + pagination_per_page: string | number; + quiz_attempts_allowed: string | number; + has_active_membership_plans: boolean; }; tutor_currency: { symbol: string; diff --git a/assets/src/js/v3/shared/config/config.ts b/assets/src/js/v3/shared/config/config.ts index b79fbaa7d5..12a23f0c09 100644 --- a/assets/src/js/v3/shared/config/config.ts +++ b/assets/src/js/v3/shared/config/config.ts @@ -112,8 +112,8 @@ const defaultTutorConfig = { }, }; -export const tutorConfig = (window._tutorobject || - defaultTutorConfig) as unknown as typeof defaultTutorConfig as typeof defaultTutorConfig & Record; +export const tutorConfig = (window._tutorobject || defaultTutorConfig) as typeof defaultTutorConfig & + Record; window.ajaxurl = tutorConfig.ajaxurl; const config = { From 35c9d4c2dde67eff5822316967f8db13fd371ecc Mon Sep 17 00:00:00 2001 From: Anindra Das Bivas Date: Wed, 24 Jun 2026 12:24:35 +0600 Subject: [PATCH 10/12] Fix: My course can be accessed from instructor student view --- templates/dashboard/my-courses.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/templates/dashboard/my-courses.php b/templates/dashboard/my-courses.php index 7d61803c54..1e0169e78d 100644 --- a/templates/dashboard/my-courses.php +++ b/templates/dashboard/my-courses.php @@ -22,8 +22,14 @@ use TUTOR\Icon; use Tutor\Components\SvgIcon; use Tutor\Components\Constants\Color; +use TUTOR\Dashboard; use TUTOR\Input; use Tutor\Models\CourseModel; +use TUTOR\User; + +if ( ! User::is_instructor_view() ) { + tutor_utils()->redirect_to( tutor_utils()->tutor_dashboard_url( Dashboard::COURSES_PAGE_SLUG ) ); +} // Get the user ID and active tab. $current_user_id = get_current_user_id(); From a93b2a61506bca6ea527d6c05076a55c07826879 Mon Sep 17 00:00:00 2001 From: Anindra Das Bivas Date: Mon, 29 Jun 2026 11:31:23 +0600 Subject: [PATCH 11/12] added reviewed changes --- classes/Quiz_Attempts_List.php | 25 +++++----- classes/Template.php | 7 ++- classes/Utils.php | 59 ++++++++++++------------ ecommerce/CheckoutController.php | 2 +- models/OrderModel.php | 19 +++----- models/QuizModel.php | 11 +++-- templates/dashboard/my-quiz-attempts.php | 2 +- templates/dashboard/quiz-attempts.php | 1 - templates/ecommerce/checkout-details.php | 2 +- templates/learning-area/index.php | 2 +- 10 files changed, 62 insertions(+), 68 deletions(-) diff --git a/classes/Quiz_Attempts_List.php b/classes/Quiz_Attempts_List.php index 989875ab04..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 = ( '' === $result_filter ) ? (int) $quiz_attempts_count : (int) QuizModel::get_quiz_attempts( 0, 0, $search_filter, $course_filter > 0 ? $course_filter : '', $start_date, $end_date, $order_filter, '', true, true ); - $pending_attempts = ( QuizModel::RESULT_PENDING === $result_filter ) ? (int) $quiz_attempts_count : (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 = ( QuizModel::RESULT_PASS === $result_filter ) ? (int) $quiz_attempts_count : (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 = ( QuizModel::RESULT_FAIL === $result_filter ) ? (int) $quiz_attempts_count : (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 ); + $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/Template.php b/classes/Template.php index 363843d1db..385a59c3c2 100644 --- a/classes/Template.php +++ b/classes/Template.php @@ -560,17 +560,16 @@ public function load_learning_template( string $template ): string { * * @since 4.0.0 * - * @param string $base_url Nav items base url. - * @param \stdClass $is_completed_course Whether course is completed. + * @param string $base_url Nav items base url. * * @return array */ - public static function make_learning_area_sub_page_nav_items( $base_url = '', $is_completed_course = null ): array { + public static function make_learning_area_sub_page_nav_items( $base_url = '' ): array { if ( empty( $base_url ) ) { $base_url = get_permalink(); } - $menu_items = apply_filters( 'tutor_learning_area_sub_page_nav_item', array(), $base_url, $is_completed_course ); + $menu_items = apply_filters( 'tutor_learning_area_sub_page_nav_item', array(), $base_url ); $menu_items['course-info'] = array( 'title' => __( 'Course Info', 'tutor' ), diff --git a/classes/Utils.php b/classes/Utils.php index 91bfcb1ca9..e28417d33c 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -1470,7 +1470,7 @@ 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(); - $course_first_lesson_cache_key = 'tutor_course_first_lesson_' . $course_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 ) { @@ -2117,8 +2117,8 @@ public function get_completed_courses_ids_by_user( $user_id = 0 ) { $user_id = $this->get_user_id( $user_id ); - $completed_courses_cache_key = 'tutor_completed_courses_ids_by_user_' . $user_id; - $cache = TutorCache::get( $completed_courses_cache_key ); + $cache_key = 'tutor_completed_courses_ids_by_user_' . $user_id; + $cache = TutorCache::get( $cache_key ); if ( false !== $cache ) { return $cache; } @@ -2142,7 +2142,7 @@ public function get_completed_courses_ids_by_user( $user_id = 0 ) { ) ); - TutorCache::set( $completed_courses_cache_key, $course_ids ); + TutorCache::set( $cache_key, $course_ids ); return $course_ids; } @@ -2175,10 +2175,10 @@ public function get_enrolled_courses_ids_by_user( $user_id = 0, $with_bundle_enr ); } - $enrolled_courses_cache_key = 'tutor_enrolled_courses_ids_by_user_' . $user_id . '_bundle_' . $with_bundle_enrolled_courses; - $cache = TutorCache::get( $enrolled_courses_cache_key ); - if ( false !== $cache ) { - return $cache; + $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( @@ -2197,7 +2197,7 @@ public function get_enrolled_courses_ids_by_user( $user_id = 0, $with_bundle_enr ) ); - TutorCache::set( $enrolled_courses_cache_key, (array) $course_ids ); + TutorCache::set( $cache_key, (array) $course_ids ); return $course_ids; } @@ -3278,21 +3278,21 @@ function ( $instructor ) use ( $main_instructor ) { public function get_total_students_by_instructor( $instructor_id, $args = array() ) { global $wpdb; - $course_post_type = tutor()->course_post_type; - $enrollment_date_clause = ''; - $total_students_cache_key = __FUNCTION__ . "_{$instructor_id}"; + $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'] ); - $total_students_cache_key .= "_{$from}_{$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( $total_students_cache_key ); + $cached = TutorCache::get( $cache_key ); if ( false !== $cached ) { return $cached; } @@ -3317,7 +3317,7 @@ public function get_total_students_by_instructor( $instructor_id, $args = array( ) ); - TutorCache::set( $total_students_cache_key, $count ); + TutorCache::set( $cache_key, $count ); return (int) $count; } @@ -3520,11 +3520,10 @@ 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 ); - $complete_assignment_cache_key = __FUNCTION__ . "_{$course_id}_{$student_id}"; - - $cached = TutorCache::get( $complete_assignment_cache_key ); + $course_id = sanitize_text_field( $course_id ); + $student_id = sanitize_text_field( $student_id ); + $cache_key = __FUNCTION__ . "_{$course_id}_{$student_id}"; + $cached = TutorCache::get( $cache_key ); if ( false !== $cached ) { return $cached; } @@ -3547,7 +3546,7 @@ public function get_completed_assignment( int $course_id, int $student_id ): int ) ); - TutorCache::set( $complete_assignment_cache_key, (int) $count ); + TutorCache::set( $cache_key, (int) $count ); return (int) $count; } @@ -7319,9 +7318,8 @@ 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; - $single_user_comment_cache_key = __FUNCTION__ . "_{$post_id}_{$user_id}"; - - $cached = TutorCache::get( $single_user_comment_cache_key ); + $cache_key = __FUNCTION__ . "_{$post_id}_{$user_id}"; + $cached = TutorCache::get( $cache_key ); if ( false !== $cached ) { return $cached; } @@ -7338,7 +7336,8 @@ public function get_single_comment_user_post_id( $post_id, $user_id ) { $user_id ) ); - TutorCache::set( $single_user_comment_cache_key, $query ); + + TutorCache::set( $cache_key, $query ); return $query ? $query : false; } @@ -9327,7 +9326,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; } @@ -9356,9 +9355,9 @@ public function hsl_to_hex( $h, $s, $l ) { /** * Get brand color - * + * * @since 4.0.0 - * + * * @return string */ public function get_brand_color() { @@ -9368,9 +9367,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 007e8efc34..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 = $should_calculate_tax ? 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/OrderModel.php b/models/OrderModel.php index bbc38ebb70..31e783b482 100644 --- a/models/OrderModel.php +++ b/models/OrderModel.php @@ -487,21 +487,15 @@ public function insert_order_items( int $order_id, array $items ): bool { * @global wpdb $wpdb WordPress database abstraction object. * * @param int $order_id The ID of the order to retrieve. - * @param \stdClass $order Optional. The order data if already fetched. * * @return object|false The order data with the student's information included, or false if no order is found. */ - public function get_order_by_id( $order_id, $order = null ) { - $order_data = null; - if ( ! $order ) { - $order_data = QueryHelper::get_row( - $this->table_name, - array( 'id' => $order_id ), - 'id' - ); - } else { - $order_data = $order; - } + public function get_order_by_id( $order_id ) { + $order_data = QueryHelper::get_row( + $this->table_name, + array( 'id' => $order_id ), + 'id' + ); if ( ! $order_data ) { return false; @@ -684,6 +678,7 @@ public function get_order_items_by_id( $order_id ) { $course->id = (int) $course->id; $course->regular_price = (float) $course->regular_price; + $course->image = get_the_post_thumbnail_url( $course->id ); // Add meta items. $order_item_meta = new OrderItemMetaModel(); diff --git a/models/QuizModel.php b/models/QuizModel.php index 87c9d654c8..bf15c1caf7 100644 --- a/models/QuizModel.php +++ b/models/QuizModel.php @@ -971,10 +971,11 @@ public static function get_quiz_answers_by_attempt_id( $attempt_id, $add_index = return array(); } - $quiz_answers_cache_key = __FUNCTION__ . implode( '_', $ids ); - $results = TutorCache::get( $quiz_answers_cache_key ); + $cache_key = __FUNCTION__ . implode( '_', $ids ); + $cached = TutorCache::get( $cache_key ); + $results = array(); - if ( false === $results ) { + if ( false === $cached ) { //phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared $results = $wpdb->get_results( "SELECT answers.*, @@ -987,7 +988,9 @@ public static function get_quiz_answers_by_attempt_id( $attempt_id, $add_index = ); //phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared - TutorCache::set( $quiz_answers_cache_key, $results ); + TutorCache::set( $cache_key, $results ); + } else { + $results = $cached; } if ( $add_index ) { diff --git a/templates/dashboard/my-quiz-attempts.php b/templates/dashboard/my-quiz-attempts.php index 431434f505..48fce3deae 100644 --- a/templates/dashboard/my-quiz-attempts.php +++ b/templates/dashboard/my-quiz-attempts.php @@ -57,7 +57,7 @@ } if ( tutor_utils()->count( $all_quizzes ) ) { - $nav_links = $quiz_attempt_obj->get_quiz_attempts_nav_data( $quiz_attempts_count, $url, $result_filter, '', 0, '', '', $order_filter, $all_quizzes ); + $nav_links = $quiz_attempt_obj->get_quiz_attempts_nav_data( $url, $result_filter, '', 0, '', '', $order_filter, $all_quizzes ); } ?> diff --git a/templates/dashboard/quiz-attempts.php b/templates/dashboard/quiz-attempts.php index ce73dc22f5..aa45755b07 100644 --- a/templates/dashboard/quiz-attempts.php +++ b/templates/dashboard/quiz-attempts.php @@ -59,7 +59,6 @@ $nav_links = $quiz_attempt_obj->get_quiz_attempts_nav_data( - $quiz_attempts_count, $url, $result_filter, $search_filter, diff --git a/templates/ecommerce/checkout-details.php b/templates/ecommerce/checkout-details.php index 0445dc0976..bc259d2ea8 100644 --- a/templates/ecommerce/checkout-details.php +++ b/templates/ecommerce/checkout-details.php @@ -50,7 +50,7 @@ $should_calculate_tax = Tax::should_calculate_tax(); $is_tax_included_in_price = Tax::is_tax_included_in_price(); -$tax_rate = $should_calculate_tax ? Tax::get_user_tax_rate( $user_id ) : ''; +$tax_rate = $should_calculate_tax ? Tax::get_user_tax_rate( $user_id ) : 0; $checkout_data = $checkout_controller->prepare_checkout_items( $item_ids, $order_type, $coupon_code ); $show_coupon_box = Settings::is_coupon_usage_enabled() && ! $checkout_data->is_coupon_applied; diff --git a/templates/learning-area/index.php b/templates/learning-area/index.php index 057e9eb583..40be976147 100644 --- a/templates/learning-area/index.php +++ b/templates/learning-area/index.php @@ -44,7 +44,7 @@ $tutor_can_retake_course = $tutor_retake_course && ( CourseModel::MODE_FLEXIBLE === $tutor_completion_mode || $tutor_is_course_completed ); // Global variables defined above are used by the 'make_learning_area_sub_page_nav_items' function. -$subpages = Template::make_learning_area_sub_page_nav_items( '', $tutor_is_course_completed ); +$subpages = Template::make_learning_area_sub_page_nav_items(); $subpage = Input::get( 'subpage' ); $attempt_id = Input::get( 'attempt_id', 0, Input::TYPE_INT ); $user_action = Input::get( 'action' ); From 701613349fe8f440c3ec204dbb982135d71db694 Mon Sep 17 00:00:00 2001 From: Anindra Das Bivas Date: Mon, 29 Jun 2026 12:29:01 +0600 Subject: [PATCH 12/12] added comments and updated code --- classes/Utils.php | 11 +++++------ models/OrderModel.php | 3 +-- templates/dashboard/components/header.php | 2 ++ templates/dashboard/dashboard.php | 2 ++ templates/dashboard/student/stats.php | 2 ++ templates/learning-area/components/sidebar.php | 2 ++ 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index e28417d33c..b3a7f5c082 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -3562,11 +3562,10 @@ public function get_completed_assignment( int $course_id, int $student_id ): int */ 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 ); - $complete_quiz_cache_key = __FUNCTION__ . "_{$course_id}_{$student_id}"; - - $cached = TutorCache::get( $complete_quiz_cache_key ); + $course_id = sanitize_text_field( $course_id ); + $student_id = sanitize_text_field( $student_id ); + $cache_key = __FUNCTION__ . "_{$course_id}_{$student_id}"; + $cached = TutorCache::get( $cache_key ); if ( false !== $cached ) { return $cached; } @@ -3585,7 +3584,7 @@ public function get_completed_quiz( int $course_id, int $student_id ): int { ) ); - TutorCache::set( $complete_quiz_cache_key, (int) $count ); + TutorCache::set( $cache_key, (int) $count ); return (int) $count; } diff --git a/models/OrderModel.php b/models/OrderModel.php index 31e783b482..44e0db419b 100644 --- a/models/OrderModel.php +++ b/models/OrderModel.php @@ -482,11 +482,10 @@ public function insert_order_items( int $order_id, array $items ): bool { * the user ID from the order data, and returns the modified order data. * * @since 3.0.0 - * @since 4.0.0 param $order added. * * @global wpdb $wpdb WordPress database abstraction object. * - * @param int $order_id The ID of the order to retrieve. + * @param int $order_id The ID of the order to retrieve. * * @return object|false The order data with the student's information included, or false if no order is found. */ diff --git a/templates/dashboard/components/header.php b/templates/dashboard/components/header.php index 2b600dfed3..a6c1e2f847 100644 --- a/templates/dashboard/components/header.php +++ b/templates/dashboard/components/header.php @@ -7,6 +7,8 @@ * @author Themeum * @link https://themeum.com * @since 4.0.0 + * + * @var int $enrolled_student_count passed from tutor\templates\dashboard.php */ defined( 'ABSPATH' ) || exit; diff --git a/templates/dashboard/dashboard.php b/templates/dashboard/dashboard.php index 7c4df1ac0d..fc14784a2a 100644 --- a/templates/dashboard/dashboard.php +++ b/templates/dashboard/dashboard.php @@ -7,6 +7,8 @@ * @author Themeum * @link https://themeum.com * @since 1.4.3 + * + * @var int $total_students passed from tutor\templates\dashboard.php */ defined( 'ABSPATH' ) || exit; diff --git a/templates/dashboard/student/stats.php b/templates/dashboard/student/stats.php index 07496bcf68..fc391b3afa 100644 --- a/templates/dashboard/student/stats.php +++ b/templates/dashboard/student/stats.php @@ -7,6 +7,8 @@ * @author Themeum * @link https://themeum.com * @since 4.0.0 + * + * @var \stdClass $enrolled_course passed from tutor\templates\dashboard\student\dashboard.php */ defined( 'ABSPATH' ) || exit; diff --git a/templates/learning-area/components/sidebar.php b/templates/learning-area/components/sidebar.php index 4cc5aaadc4..bf990035c2 100644 --- a/templates/learning-area/components/sidebar.php +++ b/templates/learning-area/components/sidebar.php @@ -6,6 +6,8 @@ * @author Themeum * @link https://themeum.com * @since 4.0.0 + * + * @var array $menu_items passed from tutor\templates\learning-area\index.php */ defined( 'ABSPATH' ) || exit;