bugfix
All checks were successful
Build & Push Docker Image / docker (push) Successful in 1m48s

This commit is contained in:
Ahmed Darrazi 2025-12-19 15:37:20 +01:00
parent 3d67eedf0c
commit 1b8be73219
4 changed files with 215 additions and 34 deletions

View File

@ -10,12 +10,74 @@ use App\Services\Course\CourseReviewService;
use App\Services\Course\CourseService;
use App\Services\Course\CourseSectionService;
use App\Services\LiveClass\ZoomLiveService;
use App\Models\Course\Course;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\Auth;
use Inertia\Inertia;
class PlayerController extends Controller
{
private function findCourseContentByIdAndType(Course $course, string $contentId, string $contentType): ?array
{
if (!in_array($contentType, ['lesson', 'quiz'], true)) {
return null;
}
foreach ($course->sections as $section) {
if ($contentType === 'lesson') {
foreach ($section->section_lessons as $lesson) {
if ((string) $lesson->id === (string) $contentId) {
return ['type' => 'lesson', 'id' => $lesson->id];
}
}
} else {
foreach ($section->section_quizzes as $quiz) {
if ((string) $quiz->id === (string) $contentId) {
return ['type' => 'quiz', 'id' => $quiz->id];
}
}
}
}
return null;
}
private function findCourseContentById(Course $course, string $contentId): ?array
{
foreach ($course->sections as $section) {
foreach ($section->section_lessons as $lesson) {
if ((string) $lesson->id === (string) $contentId) {
return ['type' => 'lesson', 'id' => $lesson->id];
}
}
foreach ($section->section_quizzes as $quiz) {
if ((string) $quiz->id === (string) $contentId) {
return ['type' => 'quiz', 'id' => $quiz->id];
}
}
}
return null;
}
private function findFirstCourseContent(Course $course): ?array
{
foreach ($course->sections as $section) {
$lesson = $section->section_lessons->first();
if ($lesson) {
return ['type' => 'lesson', 'id' => $lesson->id];
}
$quiz = $section->section_quizzes->first();
if ($quiz) {
return ['type' => 'quiz', 'id' => $quiz->id];
}
}
return null;
}
public function __construct(
protected CourseService $courseService,
protected CoursePlayerService $coursePlay,
@ -37,6 +99,10 @@ class PlayerController extends Controller
$user = Auth::user();
$watchHistory = $this->sectionService->initWatchHistory($request->course_id, 'lesson', $user->id);
if (! $watchHistory) {
return back()->with('error', 'Dieser Kurs hat noch keine Lektionen oder Quizze.');
}
return redirect()->route('course.player', [
'type' => $watchHistory->current_watching_type,
'watch_history' => $watchHistory->id,
@ -53,7 +119,47 @@ class PlayerController extends Controller
$watching_type = in_array($type, ['lesson', 'quiz'], true) ? $type : ($watch_history->current_watching_type ?? 'lesson');
$course = $this->courseService->getUserCourseById($watch_history->course_id, $user);
$watching = $this->coursePlay->getWatchingLesson($watching_id, $watching_type);
if (! $course) {
return redirect()
->route('category.courses', ['category' => 'all'])
->with('error', 'Der Kurs konnte nicht gefunden werden.');
}
// Fix wrong `type`/`lesson_id` combinations by resolving the content inside the course.
$resolved = $this->findCourseContentByIdAndType($course, (string) $watching_id, $watching_type);
if (! $resolved) {
$resolved = $this->findCourseContentById($course, (string) $watching_id);
}
if (! $resolved) {
$resolved = $this->findCourseContentByIdAndType($course, (string) $watch_history->current_watching_id, (string) $watch_history->current_watching_type);
}
if (! $resolved) {
$resolved = $this->findCourseContentById($course, (string) $watch_history->current_watching_id);
}
if (! $resolved) {
$resolved = $this->findFirstCourseContent($course);
}
if (! $resolved) {
return redirect()
->route('course.details', ['slug' => $course->slug, 'id' => $course->id])
->with('error', 'Dieser Kurs hat noch keine Lektionen oder Quizze.');
}
$watching_id = (string) $resolved['id'];
$watching_type = $resolved['type'];
// Canonicalize URL if it doesnt match the resolved content.
if ($type !== $watching_type || (string) $lesson_id !== (string) $watching_id) {
return redirect()->route('course.player', [
'type' => $watching_type,
'watch_history' => $watch_history->id,
'lesson_id' => $watching_id,
]);
}
$watching = $this->coursePlay->getWatchingLesson($watching_id, $watching_type, (string) $course->id);
$reviews = $this->reviewService->getReviews(['course_id' => $course->id, ...$request->all()], true);
$userReview = $this->reviewService->userReview($course->id, $user->id);
$totalReviews = $this->reviewService->totalReviews($course->id);
@ -74,7 +180,7 @@ class PlayerController extends Controller
// }
return Inertia::render('course-player/index', [
'type' => $type,
'type' => $watching_type,
'course' => $course,
'section' => $section,
'reviews' => $reviews,
@ -86,29 +192,55 @@ class PlayerController extends Controller
'zoomConfig' => $zoomConfig,
]);
} catch (ModelNotFoundException $th) {
$fallbackId = $watch_history->current_watching_id;
$fallbackType = $watch_history->current_watching_type;
$user = Auth::user();
$course = $this->courseService->getUserCourseById($watch_history->course_id, $user);
if ($course) {
$watchHistoryForUser = WatchHistory::where('course_id', $course->id)
->where('user_id', $user->id)
->first();
$watchHistoryId = $watchHistoryForUser?->id ?? $watch_history->id;
$requestedId = $lesson_id;
$requestedType = $watching_type ?? $type;
if ($fallbackId && in_array($fallbackType, ['lesson', 'quiz'], true)) {
$isDifferentFromRequested = ((string) $fallbackId !== (string) $lesson_id) || ($fallbackType !== $requestedType);
if ($isDifferentFromRequested) {
try {
$this->coursePlay->getWatchingLesson((string) $fallbackId, $fallbackType);
// If the ID exists in the course but the type is wrong, redirect with the correct type.
$resolved = $this->findCourseContentById($course, $requestedId);
if ($resolved && $resolved['type'] !== $requestedType) {
return redirect()->route('course.player', [
'type' => $fallbackType,
'watch_history' => $watch_history->id,
'lesson_id' => $fallbackId,
'type' => $resolved['type'],
'watch_history' => $watchHistoryId,
'lesson_id' => $resolved['id'],
]);
} catch (ModelNotFoundException $fallbackException) {
// Fall through to generic error below.
}
}
}
return redirect()->route('category.courses', ['category' => 'all'])->with('error', 'The requested content could not be found.');
// If the watch history points to a valid item, jump there.
$resolved = $this->findCourseContentById($course, (string) $watch_history->current_watching_id);
if ($resolved && ((string) $resolved['id'] !== (string) $requestedId || $resolved['type'] !== $requestedType)) {
return redirect()->route('course.player', [
'type' => $resolved['type'],
'watch_history' => $watchHistoryId,
'lesson_id' => $resolved['id'],
]);
}
// Last resort: send the user to the first available content in the course.
$first = $this->findFirstCourseContent($course);
if ($first && ((string) $first['id'] !== (string) $requestedId || $first['type'] !== $requestedType)) {
return redirect()->route('course.player', [
'type' => $first['type'],
'watch_history' => $watchHistoryId,
'lesson_id' => $first['id'],
]);
}
return redirect()
->route('course.details', ['slug' => $course->slug, 'id' => $course->id])
->with('error', 'Dieser Kurs hat noch keine Lektionen oder Quizze.');
}
return redirect()->route('category.courses', ['category' => 'all'])->with('error', 'Der angeforderte Inhalt konnte nicht gefunden werden.');
} catch (\Throwable $th) {
return redirect()->route('category.courses', ['category' => 'all'])->with('error', $th->getMessage());
}

View File

@ -26,11 +26,19 @@ class CheckEnroll
}
$watchHistory = $request->route('watch_history');
if (!WatchHistory::find($watchHistory)) {
if (!($watchHistory instanceof WatchHistory)) {
$watchHistory = WatchHistory::find($watchHistory);
}
if (!$watchHistory) {
return back()->with('error', 'Invalid watch history');
}
$course = Course::find($watchHistory->course_id);
if (!$course) {
return back()->with('error', 'Invalid course');
}
if ($user->role == 'instructor' && $user->instructor_id == $course->instructor_id) {
return $next($request);

View File

@ -12,12 +12,12 @@ use Illuminate\Support\Facades\Auth;
class CoursePlayerService
{
function getWatchingLesson(string $lesson_id, string $watching_type): SectionLesson | SectionQuiz
function getWatchingLesson(string $lesson_id, string $watching_type, ?string $course_id = null): SectionLesson | SectionQuiz
{
$user = Auth::user();
if ($watching_type === 'lesson') {
return SectionLesson::with([
$query = SectionLesson::with([
'resources',
'forums' => function ($query) {
$query->with([
@ -27,15 +27,27 @@ class CoursePlayerService
},
]);
},
])->findOrFail($lesson_id);
]);
if ($course_id) {
$query->where('course_id', $course_id);
}
return SectionQuiz::with([
return $query->findOrFail($lesson_id);
}
$query = SectionQuiz::with([
'quiz_questions',
'quiz_submissions' => function ($query) use ($user) {
$query->where('user_id', $user->id);
}
])->findOrFail($lesson_id);
]);
if ($course_id) {
$query->where('course_id', $course_id);
}
return $query->findOrFail($lesson_id);
}
function getWatchHistory(string $course_id, ?string $user_id): ?WatchHistory

View File

@ -289,21 +289,50 @@ class CourseSectionService extends MediaService
public function initWatchHistory(string $course_id, string $watching_type, string $user_id): ?WatchHistory
{
$lesson = SectionLesson::query()->where('course_id', $course_id);
$history = WatchHistory::where('course_id', $course_id)
->where('user_id', $user_id)
->first();
if ($lesson->count() >= 0 && !$history) {
$lesson = $lesson->orderBy('sort', 'asc')->first();
$coursePlay = new CoursePlayerService();
$course = Course::where('id', $course_id)->with('sections')->first();
return $coursePlay->watchHistory($course, $lesson->id, $watching_type, $user_id);
if ($history) {
return $history;
}
return $history;
$course = Course::where('id', $course_id)->with([
'sections.section_lessons',
'sections.section_quizzes',
])->first();
if (! $course) {
return null;
}
$preferredTypes = $watching_type === 'quiz' ? ['quiz', 'lesson'] : ['lesson', 'quiz'];
$firstItem = null;
foreach ($preferredTypes as $preferredType) {
foreach ($course->sections as $section) {
if ($preferredType === 'lesson') {
$lesson = $section->section_lessons->first();
if ($lesson) {
$firstItem = ['type' => 'lesson', 'id' => $lesson->id];
break 2;
}
} else {
$quiz = $section->section_quizzes->first();
if ($quiz) {
$firstItem = ['type' => 'quiz', 'id' => $quiz->id];
break 2;
}
}
}
}
if (! $firstItem) {
return null;
}
$coursePlay = new CoursePlayerService();
return $coursePlay->watchHistory($course, (string) $firstItem['id'], $firstItem['type'], $user_id);
}
/**