chore: commit remaining workspace changes
This commit is contained in:
173
app/Services/Academy/AcademyCourseProgressService.php
Normal file
173
app/Services/Academy/AcademyCourseProgressService.php
Normal file
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Academy;
|
||||
|
||||
use App\Models\AcademyCourse;
|
||||
use App\Models\AcademyCourseEnrollment;
|
||||
use App\Models\AcademyCourseLesson;
|
||||
use App\Models\AcademyLesson;
|
||||
use App\Models\AcademyLessonProgress;
|
||||
use App\Models\User;
|
||||
|
||||
final class AcademyCourseProgressService
|
||||
{
|
||||
public function __construct(private readonly AcademyCourseNavigationService $navigation)
|
||||
{
|
||||
}
|
||||
|
||||
public function getProgress(?User $user, AcademyCourse $course): array
|
||||
{
|
||||
$totalRequired = $this->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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user