diff --git a/.github/workflows/stan.yml b/.github/workflows/stan.yml index 3ff0192..b078b3f 100644 --- a/.github/workflows/stan.yml +++ b/.github/workflows/stan.yml @@ -36,4 +36,4 @@ jobs: run: vendor/bin/testbench migrate:fresh - name: Run tests - run: vendor/bin/phpstan analyse --level max src + run: vendor/bin/phpstan analyse diff --git a/composer.json b/composer.json index 2a35bbb..15ee352 100644 --- a/composer.json +++ b/composer.json @@ -19,8 +19,8 @@ }, "require-dev": { "orchestra/testbench": ">=5.0", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^9.0", + "nunomaduro/larastan": "^2.0" }, "license": "MIT", "authors": [ diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..e349cbf --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,10 @@ +includes: + - ./vendor/nunomaduro/larastan/extension.neon + +parameters: + + paths: + - src/ + + # The level 9 is the highest level + level: 2 diff --git a/src/Http/Controllers/CourseAPIController.php b/src/Http/Controllers/CourseAPIController.php index 812977b..ec5095d 100644 --- a/src/Http/Controllers/CourseAPIController.php +++ b/src/Http/Controllers/CourseAPIController.php @@ -105,6 +105,7 @@ public function show($id, GetCourseAPIRequest $request): JsonResponse public function program($id, GetCourseCurriculumAPIRequest $request): JsonResponse { + /** @var Course $course */ $course = $this->courseRepository->findWith( $id, ['*'], @@ -135,7 +136,7 @@ public function update($id, UpdateCourseAPIRequest $request): JsonResponse { $input = $request->all(); - /** @var Course $course */ + /** @var Course|null $course */ $course = $this->courseRepository->find($id); if (empty($course)) { diff --git a/src/Http/Controllers/LessonAPIController.php b/src/Http/Controllers/LessonAPIController.php index 9f676da..1c22926 100644 --- a/src/Http/Controllers/LessonAPIController.php +++ b/src/Http/Controllers/LessonAPIController.php @@ -67,7 +67,7 @@ public function update($id, UpdateLessonAPIRequest $request) { $input = $request->all(); - /** @var Lesson $lesson */ + /** @var Lesson|null $lesson */ $lesson = $this->lessonRepository->find($id); if (empty($lesson)) { diff --git a/src/Http/Controllers/Swagger/TopicAPISwagger.php b/src/Http/Controllers/Swagger/TopicAPISwagger.php index 257a447..072f10b 100644 --- a/src/Http/Controllers/Swagger/TopicAPISwagger.php +++ b/src/Http/Controllers/Swagger/TopicAPISwagger.php @@ -8,6 +8,7 @@ use EscolaLms\Courses\Http\Requests\DeleteTopicAPIRequest; use EscolaLms\Courses\Http\Requests\GetTopicAPIRequest; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; interface TopicAPISwagger @@ -51,7 +52,7 @@ public function index(Request $request); /** * @param CreateTopicAPIRequest $request - * @return Response + * @return JsonResponse * * @OA\Post( * path="/api/admin/topics", @@ -94,7 +95,7 @@ public function index(Request $request); * ) */ - public function store(CreateTopicAPIRequest $request); + public function store(CreateTopicAPIRequest $request): JsonResponse; /** * @OA\Get( @@ -240,8 +241,7 @@ public function update($id, UpdateTopicAPIRequest $request); public function destroy($id, DeleteTopicAPIRequest $request); /** - * @param Request $request - * @return Response + * @return JsonResponse * * @OA\Get( * path="/api/admin/topics/types", @@ -276,12 +276,11 @@ public function destroy($id, DeleteTopicAPIRequest $request); * ) * ) */ - - public function classes(); + public function classes(): JsonResponse; /** * @param CloneTopicAPIRequest $request - * @return Response + * @return JsonResponse * * @OA\Post( * path="/api/admin/topics/{id}/clone", @@ -324,6 +323,5 @@ public function classes(); * ) * ) */ - - public function clone(CloneTopicAPIRequest $request); + public function clone(CloneTopicAPIRequest $request): JsonResponse; } diff --git a/src/Http/Controllers/TopicAPIController.php b/src/Http/Controllers/TopicAPIController.php index f8919e6..9e89ba7 100644 --- a/src/Http/Controllers/TopicAPIController.php +++ b/src/Http/Controllers/TopicAPIController.php @@ -14,6 +14,7 @@ use EscolaLms\Courses\Http\Resources\TopicResource; use EscolaLms\Courses\Repositories\Contracts\TopicRepositoryContract; use EscolaLms\Courses\Services\Contracts\TopicServiceContract; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -22,8 +23,7 @@ */ class TopicAPIController extends AppBaseController implements TopicAPISwagger { - /** @var TopicRepository */ - private $topicRepository; + private TopicRepositoryContract $topicRepository; private TopicServiceContract $topicService; @@ -44,7 +44,7 @@ public function index(Request $request) return $this->sendResponseForResource(TopicResource::collection($topics), __('Topics retrieved successfully')); } - public function store(CreateTopicAPIRequest $request) + public function store(CreateTopicAPIRequest $request): JsonResponse { try { $topic = $this->topicRepository->createFromRequest($request); @@ -110,14 +110,14 @@ public function destroy($id, DeleteTopicAPIRequest $request) return $this->sendSuccess(__('Topic deleted successfully')); } - public function classes() + public function classes(): JsonResponse { $classes = $this->topicRepository->availableContentClasses(); return $this->sendResponse($classes, __('Topic content available list')); } - public function clone(CloneTopicAPIRequest $request) + public function clone(CloneTopicAPIRequest $request): JsonResponse { $topic = $request->getTopic(); diff --git a/src/Http/Requests/SortAPIRequest.php b/src/Http/Requests/SortAPIRequest.php index 6d7548e..034c5d0 100644 --- a/src/Http/Requests/SortAPIRequest.php +++ b/src/Http/Requests/SortAPIRequest.php @@ -5,6 +5,7 @@ use EscolaLms\Courses\Models\Topic; use EscolaLms\Courses\Models\Lesson; use EscolaLms\Courses\Models\Course; +use Illuminate\Database\Eloquent\Collection; use Illuminate\Foundation\Http\FormRequest; class SortAPIRequest extends FormRequest @@ -26,6 +27,7 @@ public function authorize(): bool && $lessons->pluck('course_id')->unique()->count() === 1 && $lessons->pluck('parent_lesson_id')->unique()->count() === 1; case 'Topic': + /** @var Collection $topics */ $topics = Topic::whereIn('id', $ids)->get(); if ($topics->count() !== count($ids) || $topics->pluck('lesson_id')->unique()->count() != 1) { diff --git a/src/Http/Resources/Admin/LessonWithTopicsAdminResource.php b/src/Http/Resources/Admin/LessonWithTopicsAdminResource.php index 7c4838a..d1a440c 100644 --- a/src/Http/Resources/Admin/LessonWithTopicsAdminResource.php +++ b/src/Http/Resources/Admin/LessonWithTopicsAdminResource.php @@ -30,17 +30,17 @@ public function toArray($request): array $lesson = $this->getResource(); return [ - 'id' => $this->id, - 'title' => $this->title, - 'summary' => $this->summary, - 'duration' => $this->duration, - 'active' => $this->active, + 'id' => $this->resource->id, + 'title' => $this->resource->title, + 'summary' => $this->resource->summary, + 'duration' => $this->resource->duration, + 'active' => $this->resource->active, 'topics' => TopicAdminResource::collection($lesson->topics->sortBy('order')), 'topics_count' => $lesson->topics->count(), - 'order' => $this->order, + 'order' => $this->resource->order, 'lessons' => LessonWithTopicsAdminResource::collection($lesson->lessons->sortBy('order')), - 'active_from' => $this->active_from, - 'active_to' => $this->active_to, + 'active_from' => $this->resource->active_from, + 'active_to' => $this->resource->active_to, ...ModelFields::getExtraAttributesValues($this->resource, MetaFieldVisibilityEnum::PUBLIC) ]; } diff --git a/src/Http/Resources/Admin/TopicAdminResource.php b/src/Http/Resources/Admin/TopicAdminResource.php index ed5aeed..69a6ee8 100644 --- a/src/Http/Resources/Admin/TopicAdminResource.php +++ b/src/Http/Resources/Admin/TopicAdminResource.php @@ -17,31 +17,31 @@ class TopicAdminResource extends JsonResource */ public function toArray($request) { - $topicable = $this->topicable; + $topicable = $this->resource->topicable; - if (Topic::getResourceClass($this->topicable_type, 'admin')) { - $resourceClass = Topic::getResourceClass($this->topicable_type, 'admin'); - $resource = new $resourceClass($this->topicable); + if (Topic::getResourceClass($this->resource->topicable_type, 'admin')) { + $resourceClass = Topic::getResourceClass($this->resource->topicable_type, 'admin'); + $resource = new $resourceClass($this->resource->topicable); $topicable = $resource->toArray($request); } return [ - 'id' => $this->id, - 'title' => $this->title, - 'lesson_id' => $this->lesson_id, - 'active' => $this->active, - 'preview' => $this->preview, - 'topicable_id' => $this->topicable_id, - 'topicable_type' => $this->topicable_type, + 'id' => $this->resource->id, + 'title' => $this->resource->title, + 'lesson_id' => $this->resource->lesson_id, + 'active' => $this->resource->active, + 'preview' => $this->resource->preview, + 'topicable_id' => $this->resource->topicable_id, + 'topicable_type' => $this->resource->topicable_type, 'topicable' => $topicable, - 'summary' => $this->summary, - 'introduction' => $this->introduction, - 'description' => $this->description, - 'resources' => TopicResourceResource::collection($this->resources), - 'order' => $this->order, - 'json' => $this->json, - 'can_skip' => $this->can_skip, - 'duration' => $this->duration, + 'summary' => $this->resource->summary, + 'introduction' => $this->resource->introduction, + 'description' => $this->resource->description, + 'resources' => TopicResourceResource::collection($this->resource->resources), + 'order' => $this->resource->order, + 'json' => $this->resource->json, + 'can_skip' => $this->resource->can_skip, + 'duration' => $this->resource->duration, ]; } } diff --git a/src/Http/Resources/CourseListResource.php b/src/Http/Resources/CourseListResource.php index 55b04f7..e0aeb29 100644 --- a/src/Http/Resources/CourseListResource.php +++ b/src/Http/Resources/CourseListResource.php @@ -12,38 +12,38 @@ class CourseListResource extends JsonResource public function toArray($request) { $fields = [ - 'id' => $this->id, - 'created_at' => $this->created_at, - 'updated_at' => $this->updated_at, - 'title' => $this->title, - 'summary' => $this->summary, - 'image_path' => $this->image_path, - 'video_path' => $this->video_path, - 'duration' => $this->duration, - 'author_id' => $this->author_id, - 'author' => $this->author ? TutorResource::make($this->author) : null, - 'authors' => $this->authors ? TutorResource::collection($this->authors) : [], - 'status' => $this->status, - 'subtitle' => $this->subtitle, - 'language' => $this->language, - 'description' => $this->description, - 'categories' => $this->categories, - 'tags' => $this->tags, - 'level' => $this->level, - 'poster_path' => $this->poster_path, - 'active_from' => $this->active_from, - 'active_to' => $this->active_to, - 'hours_to_complete' => $this->hours_to_complete, - 'findable' => $this->findable, - 'scorm_sco_id' => $this->scorm_sco_id, - 'target_group' => $this->target_group, - 'users_count' => $this->users_count, - 'image_url' => $this->image_url, - 'video_url' => $this->video_url, - 'poster_url' => $this->poster_url, - 'teaser_url' => $this->teaser_url, - 'public' => $this->public ?? false, - 'fields' => $this->fields, + 'id' => $this->resource->id, + 'created_at' => $this->resource->created_at, + 'updated_at' => $this->resource->updated_at, + 'title' => $this->resource->title, + 'summary' => $this->resource->summary, + 'image_path' => $this->resource->image_path, + 'video_path' => $this->resource->video_path, + 'duration' => $this->resource->duration, + 'author_id' => $this->resource->author_id, + 'author' => $this->resource->author ? TutorResource::make($this->resource->author) : null, + 'authors' => $this->resource->authors ? TutorResource::collection($this->resource->authors) : [], + 'status' => $this->resource->status, + 'subtitle' => $this->resource->subtitle, + 'language' => $this->resource->language, + 'description' => $this->resource->description, + 'categories' => $this->resource->categories, + 'tags' => $this->resource->tags, + 'level' => $this->resource->level, + 'poster_path' => $this->resource->poster_path, + 'active_from' => $this->resource->active_from, + 'active_to' => $this->resource->active_to, + 'hours_to_complete' => $this->resource->hours_to_complete, + 'findable' => $this->resource->findable, + 'scorm_sco_id' => $this->resource->scorm_sco_id, + 'target_group' => $this->resource->target_group, + 'users_count' => $this->resource->users_count, + 'image_url' => $this->resource->image_url, + 'video_url' => $this->resource->video_url, + 'poster_url' => $this->resource->poster_url, + 'teaser_url' => $this->resource->teaser_url, + 'public' => $this->resource->public ?? false, + 'fields' => $this->resource->fields, ]; return self::apply($fields, $this); diff --git a/src/Http/Resources/CourseSimpleResource.php b/src/Http/Resources/CourseSimpleResource.php index a6151ae..4be96ed 100644 --- a/src/Http/Resources/CourseSimpleResource.php +++ b/src/Http/Resources/CourseSimpleResource.php @@ -12,39 +12,39 @@ class CourseSimpleResource extends JsonResource public function toArray($request) { $fields = [ - 'id' => $this->id, - 'created_at' => $this->created_at, - 'updated_at' => $this->updated_at, - 'title' => $this->title, - 'summary' => $this->summary, - 'image_path' => $this->image_path, - 'video_path' => $this->video_path, - 'duration' => $this->duration, - 'author_id' => $this->author_id, - 'author' => $this->author ? TutorResource::make($this->author) : null, - 'authors' => $this->authors ? TutorResource::collection($this->authors) : [], - 'status' => $this->status, - 'subtitle' => $this->subtitle, - 'language' => $this->language, - 'description' => $this->description, - 'categories' => $this->categories, - 'tags' => $this->tags, - 'level' => $this->level, - 'lessons' => LessonSimpleResource::collection($this->lessons()->main()->active()->orderBy('order')->get()), - 'poster_path' => $this->poster_path, - 'active_from' => $this->active_from, - 'active_to' => $this->active_to, - 'hours_to_complete' => $this->hours_to_complete, - 'findable' => $this->findable, - 'scorm_sco_id' => $this->scorm_sco_id, - 'target_group' => $this->target_group, - 'users_count' => $this->users_count, - 'image_url' => $this->image_url, - 'video_url' => $this->video_url, - 'poster_url' => $this->poster_url, - 'teaser_url' => $this->teaser_url, - 'public' => $this->public ?? false, - 'fields' => $this->fields, + 'id' => $this->resource->id, + 'created_at' => $this->resource->created_at, + 'updated_at' => $this->resource->updated_at, + 'title' => $this->resource->title, + 'summary' => $this->resource->summary, + 'image_path' => $this->resource->image_path, + 'video_path' => $this->resource->video_path, + 'duration' => $this->resource->duration, + 'author_id' => $this->resource->author_id, + 'author' => $this->resource->author ? TutorResource::make($this->resource->author) : null, + 'authors' => $this->resource->authors ? TutorResource::collection($this->resource->authors) : [], + 'status' => $this->resource->status, + 'subtitle' => $this->resource->subtitle, + 'language' => $this->resource->language, + 'description' => $this->resource->description, + 'categories' => $this->resource->categories, + 'tags' => $this->resource->tags, + 'level' => $this->resource->level, + 'lessons' => LessonSimpleResource::collection($this->resource->lessons()->main()->active()->orderBy('order')->get()), + 'poster_path' => $this->resource->poster_path, + 'active_from' => $this->resource->active_from, + 'active_to' => $this->resource->active_to, + 'hours_to_complete' => $this->resource->hours_to_complete, + 'findable' => $this->resource->findable, + 'scorm_sco_id' => $this->resource->scorm_sco_id, + 'target_group' => $this->resource->target_group, + 'users_count' => $this->resource->users_count, + 'image_url' => $this->resource->image_url, + 'video_url' => $this->resource->video_url, + 'poster_url' => $this->resource->poster_url, + 'teaser_url' => $this->resource->teaser_url, + 'public' => $this->resource->public ?? false, + 'fields' => $this->resource->fields, ]; return self::apply($fields, $this); } diff --git a/src/Http/Resources/CourseWithProgramResource.php b/src/Http/Resources/CourseWithProgramResource.php index a22968c..cacd5ae 100644 --- a/src/Http/Resources/CourseWithProgramResource.php +++ b/src/Http/Resources/CourseWithProgramResource.php @@ -55,8 +55,8 @@ public function toArray($request): array 'findable' => $course->findable, 'target_group' => $course->target_group, 'teaser_url' => $course->teaser_url, - 'public' => $this->public ?? false, - 'fields' => $this->fields, + 'public' => $this->resource->public ?? false, + 'fields' => $this->resource->fields, ]; } } diff --git a/src/Http/Resources/LessonResource.php b/src/Http/Resources/LessonResource.php index 9173875..b9a00ea 100644 --- a/src/Http/Resources/LessonResource.php +++ b/src/Http/Resources/LessonResource.php @@ -15,16 +15,16 @@ class LessonResource extends JsonResource public function toArray($request): array { return [ - 'id' => $this->id, - 'title' => $this->title, - 'summary' => $this->summary, - 'duration' => $this->duration, - 'active' => $this->active, - 'order' => $this->order, - 'course_id' => $this->course_id, - 'active_from' => $this->active_from, - 'active_to' => $this->active_to, - 'lessons' => LessonSimpleResource::collection($this->lessons->filter(fn (Lesson $lesson) => $lesson->active)->sortBy('order')), + 'id' => $this->resource->id, + 'title' => $this->resource->title, + 'summary' => $this->resource->summary, + 'duration' => $this->resource->duration, + 'active' => $this->resource->active, + 'order' => $this->resource->order, + 'course_id' => $this->resource->course_id, + 'active_from' => $this->resource->active_from, + 'active_to' => $this->resource->active_to, + 'lessons' => LessonSimpleResource::collection($this->resource->lessons->filter(fn (Lesson $lesson) => $lesson->active)->sortBy('order')), ...ModelFields::getExtraAttributesValues($this->resource, MetaFieldVisibilityEnum::PUBLIC) ]; } diff --git a/src/Http/Resources/LessonWithTopicsResource.php b/src/Http/Resources/LessonWithTopicsResource.php index 1214b2f..6758268 100644 --- a/src/Http/Resources/LessonWithTopicsResource.php +++ b/src/Http/Resources/LessonWithTopicsResource.php @@ -31,15 +31,15 @@ public function toArray($request): array $lessons = $lesson->lessons->filter(fn (Lesson $lesson) => $lesson->active)->sortBy('order'); return [ - 'id' => $this->id, - 'title' => $this->title, - 'summary' => $this->summary, - 'duration' => $this->duration, - 'active' => $this->active, + 'id' => $this->resource->id, + 'title' => $this->resource->title, + 'summary' => $this->resource->summary, + 'duration' => $this->resource->duration, + 'active' => $this->resource->active, 'topics' => TopicResource::collection($topics), - 'order' => $this->order, - 'active_from' => $this->active_from, - 'active_to' => $this->active_to, + 'order' => $this->resource->order, + 'active_from' => $this->resource->active_from, + 'active_to' => $this->resource->active_to, 'lessons' => LessonWithTopicsResource::collection($lessons), ...ModelFields::getExtraAttributesValues($this->resource, MetaFieldVisibilityEnum::PUBLIC) ]; diff --git a/src/Http/Resources/ScormResource.php b/src/Http/Resources/ScormResource.php index 785759a..63a2aef 100644 --- a/src/Http/Resources/ScormResource.php +++ b/src/Http/Resources/ScormResource.php @@ -24,6 +24,7 @@ public function toArray($request): array 'id' => $this->getResource()->getKey(), 'uuid' => $this->getResource()->uuid, 'version' => $this->getResource()->version, + // @phpstan-ignore-next-line 'scos' => ScormScoResource::collection($this->getResource()->scos), ]; } diff --git a/src/Http/Resources/ScormScoResource.php b/src/Http/Resources/ScormScoResource.php index 4d17637..08c3c08 100644 --- a/src/Http/Resources/ScormScoResource.php +++ b/src/Http/Resources/ScormScoResource.php @@ -22,8 +22,11 @@ public function toArray($request): array { return [ 'id' => $this->getResource()->getKey(), + // @phpstan-ignore-next-line 'uuid' => $this->getResource()->uuid, + // @phpstan-ignore-next-line 'entry_url' => $this->getResource()->entry_url, + // @phpstan-ignore-next-line 'title' => $this->getResource()->title, ]; } diff --git a/src/Http/Resources/TopicResource.php b/src/Http/Resources/TopicResource.php index 815810c..c5079e9 100644 --- a/src/Http/Resources/TopicResource.php +++ b/src/Http/Resources/TopicResource.php @@ -16,33 +16,33 @@ class TopicResource extends JsonResource */ public function toArray($request) { - $topicable = $this->topicable; + $topicable = $this->resource->topicable; - if ($this->lesson && !$this->lesson->isActive()) { + if ($this->resource->lesson && !$this->resource->lesson->isActive()) { $topicable = null; - } elseif (Topic::getResourceClass($this->topicable_type, 'client')) { - $resourceClass = Topic::getResourceClass($this->topicable_type, 'client'); - $resource = new $resourceClass($this->topicable); + } elseif (Topic::getResourceClass($this->resource->topicable_type, 'client')) { + $resourceClass = Topic::getResourceClass($this->resource->topicable_type, 'client'); + $resource = new $resourceClass($this->resource->topicable); $topicable = $resource->toArray($request); } return [ - 'id' => $this->id, - 'title' => $this->title, - 'lesson_id' => $this->lesson_id, - 'active' => $this->active, - 'preview' => $this->preview, - 'topicable_id' => $this->topicable_id, - 'topicable_type' => $this->topicable_type, + 'id' => $this->resource->id, + 'title' => $this->resource->title, + 'lesson_id' => $this->resource->lesson_id, + 'active' => $this->resource->active, + 'preview' => $this->resource->preview, + 'topicable_id' => $this->resource->topicable_id, + 'topicable_type' => $this->resource->topicable_type, 'topicable' => $topicable, - 'summary' => $this->summary, - 'introduction' => $this->introduction, - 'description' => $this->description, - 'resources' => TopicResourceResource::collection($this->resources), - 'order' => $this->order, - 'json' => $this->json, - 'can_skip' => $this->can_skip, - 'duration' => $this->duration, + 'summary' => $this->resource->summary, + 'introduction' => $this->resource->introduction, + 'description' => $this->resource->description, + 'resources' => TopicResourceResource::collection($this->resource->resources), + 'order' => $this->resource->order, + 'json' => $this->resource->json, + 'can_skip' => $this->resource->can_skip, + 'duration' => $this->resource->duration, ]; } } diff --git a/src/Http/Resources/TopicSimpleResource.php b/src/Http/Resources/TopicSimpleResource.php index d671b3e..6899cfc 100644 --- a/src/Http/Resources/TopicSimpleResource.php +++ b/src/Http/Resources/TopicSimpleResource.php @@ -11,7 +11,7 @@ class TopicSimpleResource extends TopicResource public function toArray($request) { $response = parent::toArray($request); - if (!$this->preview) { + if (!$this->resource->preview) { unset($response['topicable']); unset($response['resources']); } diff --git a/src/Http/Resources/TutorInterestResource.php b/src/Http/Resources/TutorInterestResource.php index d2fb5c8..afde138 100644 --- a/src/Http/Resources/TutorInterestResource.php +++ b/src/Http/Resources/TutorInterestResource.php @@ -10,16 +10,16 @@ class TutorInterestResource extends JsonResource public function toArray($request) { return [ - 'id' => $this->id, - 'name' => $this->name, - 'name_with_breadcrumbs' => $this->name_with_breadcrumbs, - 'slug' => $this->slug, - 'icon' => $this->icon ? Storage::url($this->icon) : null, - 'icon_class' => $this->icon_class, - 'is_active' => $this->is_active, - 'created_at' => $this->created_at, - 'updated_at' => $this->updated_at, - 'parent_id' => $this->parent_id, + 'id' => $this->resource->id, + 'name' => $this->resource->name, + 'name_with_breadcrumbs' => $this->resource->name_with_breadcrumbs, + 'slug' => $this->resource->slug, + 'icon' => $this->resource->icon ? Storage::url($this->resource->icon) : null, + 'icon_class' => $this->resource->icon_class, + 'is_active' => $this->resource->is_active, + 'created_at' => $this->resource->created_at, + 'updated_at' => $this->resource->updated_at, + 'parent_id' => $this->resource->parent_id, ]; } } diff --git a/src/Http/Resources/TutorResource.php b/src/Http/Resources/TutorResource.php index f94d5dc..72735b1 100644 --- a/src/Http/Resources/TutorResource.php +++ b/src/Http/Resources/TutorResource.php @@ -11,18 +11,18 @@ class TutorResource extends JsonResource public function toArray($request) { $fields = [ - 'id' => $this->id, - 'first_name' => $this->first_name, - 'last_name' => $this->last_name, - 'email' => $this->email, - 'path_avatar' => $this->path_avatar, - 'url_avatar' => $this->avatar_url, - 'interests' => TutorInterestResource::collection($this->interests), + 'id' => $this->resource->id, + 'first_name' => $this->resource->first_name, + 'last_name' => $this->resource->last_name, + 'email' => $this->resource->email, + 'path_avatar' => $this->resource->path_avatar, + 'url_avatar' => $this->resource->avatar_url, + 'interests' => TutorInterestResource::collection($this->resource->interests), ]; return array_merge( $fields, - ['categories' => $this->categories], + ['categories' => $this->resource->categories], ModelFields::getExtraAttributesValues($this->resource, MetaFieldVisibilityEnum::PUBLIC) ); } diff --git a/src/Jobs/CheckFinishedLessons.php b/src/Jobs/CheckFinishedLessons.php index 6959f2a..4c7b28c 100644 --- a/src/Jobs/CheckFinishedLessons.php +++ b/src/Jobs/CheckFinishedLessons.php @@ -7,6 +7,8 @@ use EscolaLms\Courses\Events\LessonFinished; use EscolaLms\Courses\Models\CourseProgress; use EscolaLms\Courses\Models\Lesson; +use EscolaLms\Courses\Models\Topic; +use EscolaLms\Courses\Models\User; use EscolaLms\Courses\Repositories\Contracts\TopicRepositoryContract; use EscolaLms\Courses\ValueObjects\CourseProgressCollection; use Illuminate\Bus\Queueable; @@ -31,7 +33,9 @@ public function __construct(int $topicId, int $userId) public function handle(TopicRepositoryContract $topicRepository, UserRepositoryContract $userRepository): void { + /** @var Topic $topic */ $topic = $topicRepository->find($this->topicId); + /** @var User $user */ $user = $userRepository->find($this->userId); if (!$user || !$topic || !$topic->course) { diff --git a/src/Jobs/CheckForDeadlines.php b/src/Jobs/CheckForDeadlines.php index beb423c..63dac93 100644 --- a/src/Jobs/CheckForDeadlines.php +++ b/src/Jobs/CheckForDeadlines.php @@ -35,19 +35,20 @@ public function __construct() */ public function handle() { - /** @var Collection $futureDeadlines */ $startDate = Carbon::now() ->modify('+ ' . config('escolalms_courses.reminder_of_deadline_count_days') . ' days') ->subMinutes(30); $endDate = Carbon::now() ->modify('+ ' . config('escolalms_courses.reminder_of_deadline_count_days') . ' days') ->addMinutes(30); + /** @var Collection $futureDeadlines */ $futureDeadlines = CourseUserPivot::where('deadline', '>=', $startDate) ->where('deadline', '<', $endDate) ->get(); Log::debug('Checking for deadlines'); + /** @var CourseUserPivot $courseUserPivot */ foreach ($futureDeadlines as $courseUserPivot) { event(new CourseDeadlineSoon($courseUserPivot->user, $courseUserPivot->course)); } diff --git a/src/Models/Course.php b/src/Models/Course.php index e454768..4196fc8 100644 --- a/src/Models/Course.php +++ b/src/Models/Course.php @@ -167,6 +167,27 @@ * * @property-read \Illuminate\Database\Eloquent\Collection|\EscolaLms\Courses\Models\Lesson[] $lessons * @property-read \Illuminate\Database\Eloquent\Collection|\EscolaLms\Courses\Models\Topic[] $topics + * @property string $status + * @property ?Carbon $active_from + * @property ?Carbon $active_to + * @property bool $is_active + * @property string $title + * @property ?string $summary + * @property ?string $image_path + * @property ?string $video_path + * @property ?string $duration + * @property ?int $scorm_sco_id + * @property ?string $subtitle + * @property ?string $language + * @property ?string $description + * @property ?string $level + * @property ?string $poster_path + * @property int $hours_to_complete + * @property bool $findable + * @property ?string $target_group + * @property ?string $teaser_url + * @property ?bool $public + * @property array $fields */ class Course extends Model @@ -235,7 +256,7 @@ class Course extends Model /** * Validation rules * - * @var array + * @return array */ public static function rules(): array { diff --git a/src/Models/CourseProgress.php b/src/Models/CourseProgress.php index bce05ab..6e0dfa9 100644 --- a/src/Models/CourseProgress.php +++ b/src/Models/CourseProgress.php @@ -18,6 +18,7 @@ * @property \Illuminate\Support\Carbon|null $finished_at * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at + * @property \Illuminate\Support\Carbon|null $started_at * @property int $seconds * @method static \Illuminate\Database\Eloquent\Builder|CourseProgress newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|CourseProgress newQuery() diff --git a/src/Models/CourseUserPivot.php b/src/Models/CourseUserPivot.php index 6a336d1..abbbb2e 100644 --- a/src/Models/CourseUserPivot.php +++ b/src/Models/CourseUserPivot.php @@ -2,10 +2,15 @@ namespace EscolaLms\Courses\Models; +use Carbon\Carbon; use EscolaLms\Core\Models\User; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\Pivot; +/** + * @property Carbon|null $deadline + * @property Carbon|null $end_date + */ class CourseUserPivot extends Pivot { protected $table = 'course_user'; diff --git a/src/Models/Lesson.php b/src/Models/Lesson.php index 0d1d35e..e1efc60 100644 --- a/src/Models/Lesson.php +++ b/src/Models/Lesson.php @@ -68,6 +68,12 @@ * @property bool $active * @property Carbon $active_from * @property Carbon $active_to + * @property int $course_id + * @property int $order + * @property int $parent_lesson_id + * @property string $title + * @property string $summary + * @property string $duration * @property-read \Illuminate\Database\Eloquent\Collection|\EscolaLms\Courses\Models\Topic[] $topics * @property-read \Illuminate\Database\Eloquent\Collection|\EscolaLms\Courses\Models\Lesson[] $lessons * @property-read \EscolaLms\Courses\Models\Lesson|null $parentLesson diff --git a/src/Models/Topic.php b/src/Models/Topic.php index 6db6248..637e741 100644 --- a/src/Models/Topic.php +++ b/src/Models/Topic.php @@ -96,6 +96,11 @@ * * @property bool $active * @property \EscolaLms\Courses\Models\Lesson|null $lesson + * @property int $lesson_id + * @property int $topic_id + * @property int $order + * @property int $topicable_id + * @property string $topicable_type */ class Topic extends Model { diff --git a/src/Models/TopicResource.php b/src/Models/TopicResource.php index 300beab..1a9995c 100644 --- a/src/Models/TopicResource.php +++ b/src/Models/TopicResource.php @@ -35,6 +35,9 @@ * ) * * @property \EscolaLms\Courses\Models\Topic|null $topic + * @property string $path + * @property string $name + * @property int $topic_id */ class TopicResource extends Model { diff --git a/src/Repositories/BaseRepository.php b/src/Repositories/BaseRepository.php index db6da7e..95bd505 100644 --- a/src/Repositories/BaseRepository.php +++ b/src/Repositories/BaseRepository.php @@ -13,8 +13,6 @@ abstract class BaseRepository extends BaseEscolaRepository * @param int $id * @param array $columns * @param array $with relations - * - * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection|Model|null */ public function findWith(int $id, array $columns = ['*'], array $with = [], array $withCount = []): Model { diff --git a/src/Repositories/Contracts/TopicRepositoryContract.php b/src/Repositories/Contracts/TopicRepositoryContract.php index d55dc37..7acccbd 100644 --- a/src/Repositories/Contracts/TopicRepositoryContract.php +++ b/src/Repositories/Contracts/TopicRepositoryContract.php @@ -28,4 +28,5 @@ public function registerResourceClasses(string $topicTypeClass, array $resourceC public function getResourceClass(string $topicTypeClass = null, string $type = 'client'): string; public function deleteModel(Topic $topic): ?bool; + public function availableContentClasses(): array; } diff --git a/src/Repositories/CourseRepository.php b/src/Repositories/CourseRepository.php index b136e46..2d06ed0 100644 --- a/src/Repositories/CourseRepository.php +++ b/src/Repositories/CourseRepository.php @@ -105,14 +105,14 @@ public function queryAll(): Builder public function allQueryBuilder(array $search = [], array $criteria = []): Builder { /** search main category and all subcategories */ - if (isset($search) && isset($search['category_id'])) { + if (isset($search['category_id'])) { $collection = Category::where('id', $search['category_id'])->with('children')->get(); $flat = self::flatten($collection, 'children'); $flat_ids = array_map(fn($cat) => $cat->id, $flat); unset($search['category_id']); } - if (isset($search) && isset($search['categories'])) { + if (isset($search['categories'])) { $flat_ids = []; foreach ($search['categories'] as $category_id) { $collection = Category::where('id', $category_id)->with('children')->get(); @@ -166,12 +166,13 @@ public function allQueryBuilder(array $search = [], array $criteria = []): Build */ public function create(array $input): Model { + /** @var Course $model */ $model = $this->model->newInstance($input); $model->save(); $update = []; - $courseId = $model->id; + $courseId = $model->getKey(); if (isset($input['video'])) { /** @var UploadedFile $video */ @@ -213,6 +214,7 @@ public function update(array $input, int $id): Model { $query = $this->model->newQuery(); + /** @var Course $model */ $model = $query->findOrFail($id); $isActive = $model->is_active; diff --git a/src/Repositories/TopicRepository.php b/src/Repositories/TopicRepository.php index 443743b..60cd585 100644 --- a/src/Repositories/TopicRepository.php +++ b/src/Repositories/TopicRepository.php @@ -168,12 +168,13 @@ public function create(array $input): Topic /** * Update model record for given id. * - * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection|Model + * @return Topic */ public function update(array $input, int $id): Topic { $query = $this->model->newQuery(); + /** @var Topic $model */ $model = $query->with('topicable')->findOrFail($id); $model->fill($input); @@ -237,8 +238,6 @@ public function updateFromRequest(UpdateTopicAPIRequest $request): Topic } /** - * @return TopicContentContract|TopicFileContentContract|Model - * * @throws TopicException */ private function createTopicContentModelFromRequest(FormRequest $request, Topic $topic): Model @@ -319,6 +318,7 @@ private function getRulesForTopicContentUpdate(FormRequest $request, TopicConten if (!$request->has($fileKeyName)) { unset($partialRules[$fileKeyName]); } else { + // @phpstan-ignore-next-line $prefixPath = 'course/' . $topicContent->topic->course->getKey(); $partialRules[$fileKeyName] = new FileOrStringRule($partialRules[$fileKeyName], $prefixPath); } diff --git a/src/Repositories/TopicResourceRepository.php b/src/Repositories/TopicResourceRepository.php index 37fc4ae..a5f6735 100644 --- a/src/Repositories/TopicResourceRepository.php +++ b/src/Repositories/TopicResourceRepository.php @@ -53,6 +53,7 @@ public function storeUploadedResourceForTopic(Topic $topic, $file): TopicResourc public function delete(int $id): ?bool { + /** @var TopicResource $topicResource */ $topicResource = $this->model->query()->findOrFail($id); $path = $topicResource->path; diff --git a/src/Services/CourseService.php b/src/Services/CourseService.php index cb42ce1..c14b29c 100644 --- a/src/Services/CourseService.php +++ b/src/Services/CourseService.php @@ -116,6 +116,7 @@ public function getScormPlayer(int $courseId) } $sco = ScormScoModel::where('id', $course->scorm_sco_id)->first(); + // @phpstan-ignore-next-line return $this->scormService->getScoViewDataByUuid($sco->uuid); } diff --git a/src/Services/ProgressService.php b/src/Services/ProgressService.php index 2c47e94..f81ef79 100644 --- a/src/Services/ProgressService.php +++ b/src/Services/ProgressService.php @@ -41,11 +41,12 @@ public function getByUser(User $user): Collection { $progresses = new Collection(); if (!$user instanceof CoursesUser) { + /** @var CoursesUser $user */ $user = CoursesUser::find($user->getKey()); } $courses = $user->courses()->where('status', '=', CourseStatusEnum::PUBLISHED)->get(); - /** @var CoursesUser $user */ + /** @var Course $course */ foreach ($courses as $course) { $progresses->push(CourseProgressCollection::make($user, $course)); } @@ -136,6 +137,10 @@ public function ping(User $user, Topic $topic): CourseProgressCollection if ($courseProgressCollection->topicCanBeProgressed($topic)) { $courseProgressCollection->ping($topic); + if (!$user instanceof CoursesUser) { + /** @var CoursesUser $user */ + $user = CoursesUser::find($user->getKey()); + } if (!$courseProgressCollection->isFinished() && $user->finishedCourse($course->getKey())) { $user->courses()->updateExistingPivot($course->getKey(), ['finished' => false]); } diff --git a/src/Services/TopicService.php b/src/Services/TopicService.php index 39a3b02..7f9ca9a 100644 --- a/src/Services/TopicService.php +++ b/src/Services/TopicService.php @@ -28,6 +28,7 @@ public function cloneTopic(Topic $topic): Model { $clonedTopicArray = $topic->replicate()->toArray(); unset($clonedTopicArray['order']); + /** @var Topic $clonedTopic */ $clonedTopic = $this->topicRepository->create($clonedTopicArray); $clonedTopicable = $topic->topicable->replicate(); @@ -55,7 +56,7 @@ public function cloneTopic(Topic $topic): Model return $clonedTopic; } - private function cloneTopicResource(Model $clonedTopic, TopicResource $topicResource): Model + private function cloneTopicResource(Topic $clonedTopic, TopicResource $topicResource): Model { $pathFrom = $topicResource->path; $pathTo = $clonedTopic->getStorageDirectoryAttribute() . 'resources' . DIRECTORY_SEPARATOR . $topicResource->name; diff --git a/src/ValueObjects/CourseContent.php b/src/ValueObjects/CourseContent.php index f43c8ce..2e8e605 100644 --- a/src/ValueObjects/CourseContent.php +++ b/src/ValueObjects/CourseContent.php @@ -46,26 +46,8 @@ public function getCourse(): Course return $this->course; } - /** - * @return Collection - */ - public function getSections(): Collection - { - return $this->course->sections; - } - - public function getRelated(): Collection - { - return $this->courseService->related($this->course); - } - public function countCertificates(): int { return 1; } - - public function countFlashcards(): int - { - return $this->getCourse()->flashcards()->count(); - } }