function ngx_find_next_lesson() { global $post; $out = array( 'current_id' => $post ? (int) $post->ID : 0, 'current_type' => $post ? get_post_type( $post ) : 'none', 'course_id' => 0, 'section_id' => 0, 'ordered' => array(), 'index' => -1, 'next_id' => 0, 'why' => '', ); if ( ! $post || 'lesson' !== get_post_type( $post ) ) { $out['why'] = 'Not on a lesson.'; return $out; } // Prefer LLMS classes if available to get course. if ( class_exists( 'LLMS_Lesson' ) ) { $lesson = new LLMS_Lesson( $post->ID ); // Direct next-ID via class (fast path) if ( method_exists( $lesson, 'get_next_lesson_id' ) ) { $nid = (int) $lesson->get_next_lesson_id(); if ( $nid ) { $out['next_id'] = $nid; $out['why'] = 'Found via LLMS_Lesson::get_next_lesson_id()'; return $out; } } if ( method_exists( $lesson, 'get_next_lesson' ) ) { $n = $lesson->get_next_lesson(); if ( $n && method_exists( $n, 'get' ) ) { $nid = (int) $n->get( 'id' ); if ( $nid ) { $out['next_id'] = $nid; $out['why'] = 'Found via LLMS_Lesson::get_next_lesson()->get("id")'; return $out; } } } // derive course id if ( method_exists( $lesson, 'get_course' ) ) { $course = $lesson->get_course(); if ( $course ) { $out['course_id'] = is_object( $course ) && method_exists( $course, 'get' ) ? (int) $course->get( 'id' ) : (int) $course; } } if ( ! $out['course_id'] && method_exists( $lesson, 'get_parent_course' ) ) { $out['course_id'] = (int) $lesson->get_parent_course(); } } // Fallback course/section from meta. if ( ! $out['course_id'] ) { $out['section_id'] = (int) get_post_meta( $post->ID, '_llms_parent_section', true ); $out['course_id'] = (int) get_post_meta( $post->ID, '_llms_parent_course', true ); if ( ! $out['course_id'] && $out['section_id'] ) { $out['course_id'] = (int) get_post_meta( $out['section_id'], '_llms_parent_course', true ); } } if ( ! $out['course_id'] ) { $out['why'] = 'Could not determine course.'; return $out; } // -------- Strategy C: Try a builder index meta (often _llms_order) -------- // If present, it gives a single global order across the course. $q_order = new WP_Query( array( 'post_type' => 'lesson', 'post_status' => 'publish', 'posts_per_page' => -1, 'fields' => 'ids', 'meta_query' => array( 'course' => array( 'key' => '_llms_parent_course', 'value' => $out['course_id'], ), 'has_order' => array( 'key' => '_llms_order', 'compare' => 'EXISTS', ), ), 'orderby' => 'meta_value_num', 'meta_key' => '_llms_order', 'order' => 'ASC', 'no_found_rows' => true, 'cache_results' => true, 'update_post_meta_cache' => true, 'update_post_term_cache' => false, ) ); if ( ! empty( $q_order->posts ) ) { $ordered = array_map( 'intval', $q_order->posts ); } else { // -------- Strategy D: Sections first (menu_order), then lessons per section (menu_order) -------- // Get all sections for the course by meta (do NOT rely on WP parent). $sections_q = new WP_Query( array( 'post_type' => 'section', 'post_status' => array( 'publish', 'draft' ), 'posts_per_page' => -1, 'fields' => 'ids', 'meta_query' => array( array( 'key' => '_llms_parent_course', 'value' => $out['course_id'], ), ), 'orderby' => 'menu_order', 'order' => 'ASC', 'no_found_rows' => true, ) ); $ordered = array(); // Lessons that belong to the course but have NO section meta. $unsectioned = new WP_Query( array( 'post_type' => 'lesson', 'post_status' => array( 'publish', 'draft' ), 'posts_per_page' => -1, 'fields' => 'ids', 'orderby' => 'menu_order', 'order' => 'ASC', 'no_found_rows' => true, 'meta_query' => array( 'course' => array( 'key' => '_llms_parent_course', 'value' => $out['course_id'], ), 'no_section' => array( 'key' => '_llms_parent_section', 'compare' => 'NOT EXISTS', ), ), ) ); // Depending on course builder behavior, these often come BEFORE sections. if ( ! empty( $unsectioned->posts ) ) { foreach ( $unsectioned->posts as $lid ) { $ordered[] = (int) $lid; } } // Now walk each section in order and append its lessons in order. if ( ! empty( $sections_q->posts ) ) { foreach ( $sections_q->posts as $sec_id ) { $lessons_q = new WP_Query( array( 'post_type' => 'lesson', 'post_status' => array( 'publish', 'draft' ), 'posts_per_page' => -1, 'fields' => 'ids', 'orderby' => 'menu_order', 'order' => 'ASC', 'no_found_rows' => true, 'meta_query' => array( array( 'key' => '_llms_parent_section', 'value' => $sec_id, ), array( 'key' => '_llms_parent_course', 'value' => $out['course_id'], ), ), ) ); if ( ! empty( $lessons_q->posts ) ) { foreach ( $lessons_q->posts as $lid ) { $ordered[] = (int) $lid; } } } } // Also consider lessons that DO have section meta but the section wasn’t found (edge cases). $remaining = new WP_Query( array( 'post_type' => 'lesson', 'post_status' => array( 'publish', 'draft' ), 'posts_per_page' => -1, 'fields' => 'ids', 'orderby' => 'menu_order', 'order' => 'ASC', 'no_found_rows' => true, 'meta_query' => array( array( 'key' => '_llms_parent_course', 'value' => $out['course_id'], ), array( 'key' => '_llms_parent_section', 'compare' => 'EXISTS', ), ), ) ); if ( ! empty( $remaining->posts ) ) { foreach ( $remaining->posts as $lid ) { $lid = (int) $lid; if ( ! in_array( $lid, $ordered, true ) ) { $ordered[] = $lid; } } } } $out['ordered'] = $ordered; if ( empty( $ordered ) ) { $out['why'] = 'No lessons found for course.'; return $out; } $idx = array_search( (int) $post->ID, $ordered, true ); $out['index'] = ( false === $idx ) ? -1 : (int) $idx; if ( false === $idx ) { $out['why'] = 'Current lesson not found in computed order.'; return $out; } $next_idx = $idx + 1; if ( isset( $ordered[ $next_idx ] ) ) { $out['next_id'] = (int) $ordered[ $next_idx ]; $out['why'] = 'Found via builder-style ordering.'; } else { $out['why'] = 'This is the last lesson in the course.'; } return $out; }