getTotalRequiredLessonsCount($course); $completedRequired = $user ? $this->getCompletedRequiredLessonsCount($user, $course) : 0; $enrollment = $user ? AcademyCourseEnrollment::query()->with('lastLesson')->where('user_id', $user->id)->where('course_id', $course->id)->first() : null; return [ 'total_required' => $totalRequired, 'completed_required' => $completedRequired, 'progress_percent' => $this->getProgressPercent($user, $course), 'enrollment' => $enrollment, 'next_lesson' => $user ? $this->getNextLesson($user, $course) : null, 'continue_lesson' => $user ? $this->getContinueLesson($user, $course) : null, 'completed' => $enrollment?->status === AcademyCourseEnrollment::STATUS_COMPLETED, ]; } public function getCompletedRequiredLessonsCount(User $user, AcademyCourse $course): int { $lessonIds = $course->courseLessons() ->where('is_required', true) ->pluck('lesson_id') ->filter() ->values() ->all(); if ($lessonIds === []) { return 0; } return AcademyLessonProgress::query() ->where('user_id', $user->id) ->whereIn('lesson_id', $lessonIds) ->whereNotNull('completed_at') ->count(); } public function getCompletedLessonIds(User $user, AcademyCourse $course): array { $lessonIds = $course->courseLessons() ->pluck('lesson_id') ->filter() ->map(fn ($lessonId): int => (int) $lessonId) ->values() ->all(); if ($lessonIds === []) { return []; } return AcademyLessonProgress::query() ->where('user_id', $user->id) ->whereIn('lesson_id', $lessonIds) ->whereNotNull('completed_at') ->pluck('lesson_id') ->map(fn ($lessonId): int => (int) $lessonId) ->values() ->all(); } public function getTotalRequiredLessonsCount(AcademyCourse $course): int { return $course->courseLessons()->where('is_required', true)->count(); } public function getProgressPercent(?User $user, AcademyCourse $course): int { if (! $user) { return 0; } $totalRequired = $this->getTotalRequiredLessonsCount($course); if ($totalRequired < 1) { return 0; } return (int) floor(($this->getCompletedRequiredLessonsCount($user, $course) / $totalRequired) * 100); } public function getNextLesson(User $user, AcademyCourse $course): ?AcademyCourseLesson { $completedLessonIds = $this->getCompletedLessonIds($user, $course); return $this->navigation->orderedCourseLessons($course) ->first(fn (AcademyCourseLesson $courseLesson): bool => ! in_array((int) $courseLesson->lesson_id, $completedLessonIds, true)); } public function getPreviousLesson(AcademyCourse $course, AcademyLesson $lesson): ?AcademyCourseLesson { return $this->navigation->previousLesson($course, $lesson); } public function getContinueLesson(User $user, AcademyCourse $course): ?AcademyCourseLesson { $enrollment = AcademyCourseEnrollment::query() ->where('user_id', $user->id) ->where('course_id', $course->id) ->first(); if ($enrollment?->last_lesson_id) { $lastLesson = AcademyLesson::query()->find($enrollment->last_lesson_id); if ($lastLesson instanceof AcademyLesson) { $nextLesson = $this->navigation->nextLesson($course, $lastLesson); return $nextLesson ?? $this->navigation->findCourseLesson($course, $lastLesson); } } return $this->getNextLesson($user, $course) ?? $this->navigation->firstPublishedLesson($course); } public function markEnrollmentStarted(User $user, AcademyCourse $course): AcademyCourseEnrollment { return AcademyCourseEnrollment::query()->updateOrCreate( [ 'user_id' => $user->id, 'course_id' => $course->id, ], [ 'status' => AcademyCourseEnrollment::STATUS_ACTIVE, 'started_at' => now(), 'completed_at' => null, ], ); } public function updateLastLesson(User $user, AcademyCourse $course, AcademyLesson $lesson): AcademyCourseEnrollment { $enrollment = $this->markEnrollmentStarted($user, $course); $enrollment->forceFill([ 'last_lesson_id' => $lesson->id, ])->save(); return $enrollment; } public function markCourseCompletedIfFinished(User $user, AcademyCourse $course): AcademyCourseEnrollment { $enrollment = $this->markEnrollmentStarted($user, $course); $progressPercent = $this->getProgressPercent($user, $course); $isComplete = $this->getTotalRequiredLessonsCount($course) > 0 && $progressPercent >= 100; $enrollment->forceFill([ 'status' => $isComplete ? AcademyCourseEnrollment::STATUS_COMPLETED : AcademyCourseEnrollment::STATUS_ACTIVE, 'completed_at' => $isComplete ? ($enrollment->completed_at ?? now()) : null, ])->save(); return $enrollment; } }