'boolean', 'order_num' => 'integer', 'estimated_minutes' => 'integer', 'lessons_count_cache' => 'integer', 'published_at' => 'datetime', ]; public function scopePublished(Builder $query): Builder { return $query ->where('status', self::STATUS_PUBLISHED) ->where(function (Builder $builder): void { $builder->whereNull('published_at')->orWhere('published_at', '<=', now()); }); } public function scopeFeatured(Builder $query): Builder { return $query->where('is_featured', true); } public function scopeOrdered(Builder $query): Builder { return $query ->orderByDesc('is_featured') ->orderBy('order_num') ->orderByDesc('published_at') ->orderBy('id'); } public function scopeFree(Builder $query): Builder { return $query->where('access_level', 'free'); } public function scopePremium(Builder $query): Builder { return $query->where('access_level', 'premium'); } public function scopeMixed(Builder $query): Builder { return $query->where('access_level', 'mixed'); } public function sections(): HasMany { return $this->hasMany(AcademyCourseSection::class, 'course_id') ->orderBy('order_num') ->orderBy('id'); } public function courseLessons(): HasMany { return $this->hasMany(AcademyCourseLesson::class, 'course_id') ->orderBy('order_num') ->orderBy('id'); } public function lessons(): BelongsToMany { return $this->belongsToMany(AcademyLesson::class, 'academy_course_lessons', 'course_id', 'lesson_id') ->using(AcademyCourseLesson::class) ->withPivot(['section_id', 'order_num', 'is_required', 'access_override', 'unlock_after_lesson_id']) ->withTimestamps() ->orderBy('academy_course_lessons.order_num') ->orderBy('academy_course_lessons.id'); } public function enrollments(): HasMany { return $this->hasMany(AcademyCourseEnrollment::class, 'course_id'); } public function isPublished(): bool { return (string) $this->status === self::STATUS_PUBLISHED && ($this->published_at === null || $this->published_at->lte(now())); } public function isFree(): bool { return (string) $this->access_level === 'free'; } public function isPremium(): bool { return (string) $this->access_level === 'premium'; } public function isMixed(): bool { return (string) $this->access_level === 'mixed'; } public function getPublicUrl(): string { return route('academy.courses.show', ['course' => $this->slug]); } public function getContinueUrl(?User $user): string { $lastLesson = $user?->academyCourseEnrollments() ->where('course_id', $this->id) ->with('lastLesson') ->first()?->lastLesson; if ($lastLesson instanceof AcademyLesson) { return route('academy.courses.lessons.show', ['course' => $this->slug, 'lesson' => $lastLesson->slug]); } $firstLesson = $this->courseLessons() ->with('lesson') ->get() ->map(fn (AcademyCourseLesson $courseLesson): ?AcademyLesson => $courseLesson->lesson) ->first(fn (?AcademyLesson $lesson): bool => $lesson instanceof AcademyLesson); if ($firstLesson instanceof AcademyLesson) { return route('academy.courses.lessons.show', ['course' => $this->slug, 'lesson' => $firstLesson->slug]); } return $this->getPublicUrl(); } }