diff --git a/src/Http/Controllers/ContentApiController.php b/src/Http/Controllers/ContentApiController.php index db41b9c8..a5326205 100644 --- a/src/Http/Controllers/ContentApiController.php +++ b/src/Http/Controllers/ContentApiController.php @@ -2,6 +2,7 @@ namespace EscolaLms\HeadlessH5P\Http\Controllers; +use EscolaLms\Core\Dtos\OrderDto; use EscolaLms\Core\Http\Controllers\EscolaLmsBaseController; use EscolaLms\HeadlessH5P\Dtos\ContentFilterCriteriaDto; use EscolaLms\HeadlessH5P\Http\Controllers\Swagger\ContentApiSwagger; @@ -34,6 +35,7 @@ public function __construct(HeadlessH5PServiceContract $hh5pService, H5PContentR public function index(ContentListRequest $request): JsonResponse { $contentFilterDto = ContentFilterCriteriaDto::instantiateFromRequest($request); + $orderDto = OrderDto::instantiateFromRequest($request); $columns = [ 'hh5p_contents.id', 'hh5p_contents.uuid', @@ -43,8 +45,8 @@ public function index(ContentListRequest $request): JsonResponse 'hh5p_contents.parameters', ]; $list = $request->get('per_page') !== null && $request->get('per_page') == 0 ? - $this->contentRepository->unpaginatedList($contentFilterDto, $columns) : - $this->contentRepository->list($contentFilterDto, $request->get('per_page'), $columns); + $this->contentRepository->unpaginatedList($contentFilterDto, $columns, $orderDto) : + $this->contentRepository->list($contentFilterDto, $request->get('per_page'), $columns, $orderDto); return $this->sendResponseForResource(ContentIndexResource::collection($list)); } diff --git a/src/Http/Requests/ContentListRequest.php b/src/Http/Requests/ContentListRequest.php index d0ecf09f..519d9e5a 100644 --- a/src/Http/Requests/ContentListRequest.php +++ b/src/Http/Requests/ContentListRequest.php @@ -18,6 +18,8 @@ public function rules(): array return [ 'title' => ['sometimes', 'nullable', 'string'], 'library_id' => ['sometimes', 'nullable', 'integer'], + 'order_by' => ['sometimes', 'nullable', 'string', 'in:id,title,library_id,library_title'], + 'order' => ['sometimes', 'nullable', 'string', 'in:ASC,DESC'], ]; } } diff --git a/src/Repositories/Contracts/H5PContentRepositoryContract.php b/src/Repositories/Contracts/H5PContentRepositoryContract.php index 824cf81a..050d942b 100644 --- a/src/Repositories/Contracts/H5PContentRepositoryContract.php +++ b/src/Repositories/Contracts/H5PContentRepositoryContract.php @@ -1,6 +1,7 @@ getQueryContent($contentFilterDto, $columns); + $query = $this->orderBy($query, $orderDto); $paginator = $query->paginate(intval($per_page)); $paginator->getCollection()->transform(function ($content) { @@ -150,9 +154,12 @@ public function list( public function unpaginatedList( ContentFilterCriteriaDto $contentFilterDto, - array $columns = ['hh5p_contents.*'] - ): Collection { + array $columns = ['hh5p_contents.*'], + ?OrderDto $orderDto = null, + ): Collection + { $query = $this->getQueryContent($contentFilterDto, $columns); + $query = $this->orderBy($query, $orderDto); $list = $query->get(); $list->transform(function ($content) { @@ -254,8 +261,7 @@ private function applyCriteria(Builder $query, array $criteria): Builder private function getQueryContent(ContentFilterCriteriaDto $contentFilterDto, array $columns = ['*']): Builder { $query = H5PContent::with(['library']) - ->select($columns) - ->orderBy('id', 'desc'); + ->select($columns); $query = self::applyQueryJoin($query); $query = self::applyQuerySelect($query); @@ -282,4 +288,18 @@ private function filterParameters(H5PContent $h5pContent, H5PLibrary $h5pLibrary $this->hh5pService->getCore()->filterParameters($content); } + + private function orderBy(Builder $query, ?OrderDto $dto): Builder + { + if ($dto) { + match ($dto->getOrderBy()) { + 'library_title' => $query + ->withAggregate('library', 'title') + ->orderBy('library_title', $dto->getOrder() ?? 'asc'), + 'title' => $query->orderBy('parameters->metadata->title', $dto->getOrder() ?? 'asc'), + default => $query->orderBy($dto->getOrderBy() ?? 'id', $dto->getOrder() ?? 'desc'), + }; + } + return $query; + } } diff --git a/tests/Api/ContentApiTest.php b/tests/Api/ContentApiTest.php index 905e8819..64fe0d72 100644 --- a/tests/Api/ContentApiTest.php +++ b/tests/Api/ContentApiTest.php @@ -204,6 +204,112 @@ public function testContentList(): void $this->assertEquals($h5pContents->first()->getKey(), $data[count($data) - 1]->id); } + public function testContentListWithSorts(): void + { + $libraryOne = H5PLibrary::factory()->create([ + 'name' => 'A Library', + 'title' => 'A Library', + 'runnable' => 1 + ]); + $libraryTwo = H5PLibrary::factory()->create([ + 'name' => 'B Library', + 'title' => 'B Library', + 'runnable' => 1 + ]); + + $contentOne = H5PContent::factory()->create([ + 'parameters' => '{"params":{"taskDescription":"Documentation tool","pagesList":[{"params":{"elementList":[{"params":{},"library":"H5P.Text 1.1","metadata":{"contentType":"Text","license":"U","title":"Untitled Text","authors":[],"changes":[],"extraTitle":"Untitled Text"},"subContentId":"da3387da-355a-49fb-92bc-3a9a4e4646a9"}],"helpTextLabel":"More information","helpText":""},"library":"H5P.StandardPage 1.5","metadata":{"contentType":"Standard page","license":"U","title":"Untitled Standard page","authors":[],"changes":[],"extraTitle":"Untitled Standard page"},"subContentId":"ac6ffdac-be02-448c-861c-969e6a09dbd5"}],"i10n":{"previousLabel":"poprzedni","nextLabel":"Next","closeLabel":"Close"}},"metadata":{"license":"U","authors":[],"changes":[],"extraTitle":"A title","title":"A title"}}', + 'library_id' => $libraryOne->getKey(), + ]); + + $contentTwo = H5PContent::factory()->create([ + 'parameters' => '{"params":{"taskDescription":"Documentation tool","pagesList":[{"params":{"elementList":[{"params":{},"library":"H5P.Text 1.1","metadata":{"contentType":"Text","license":"U","title":"Untitled Text","authors":[],"changes":[],"extraTitle":"Untitled Text"},"subContentId":"da3387da-355a-49fb-92bc-3a9a4e4646a9"}],"helpTextLabel":"More information","helpText":""},"library":"H5P.StandardPage 1.5","metadata":{"contentType":"Standard page","license":"U","title":"Untitled Standard page","authors":[],"changes":[],"extraTitle":"Untitled Standard page"},"subContentId":"ac6ffdac-be02-448c-861c-969e6a09dbd5"}],"i10n":{"previousLabel":"poprzedni","nextLabel":"Next","closeLabel":"Close"}},"metadata":{"license":"U","authors":[],"changes":[],"extraTitle":"B title","title":"B title"}}', + 'library_id' => $libraryTwo->getKey(), + ]); + + $this->authenticateAsAdmin(); + + $response = $this + ->actingAs($this->user, 'api') + ->json('GET', '/api/admin/hh5p/content', [ + 'order_by' => 'library_title', + 'order' => 'ASC' + ]); + + $this->assertTrue($response->getData()->data[0]->library->name === $libraryOne->name); + $this->assertTrue($response->getData()->data[1]->library->name === $libraryTwo->name); + + $response = $this + ->actingAs($this->user, 'api') + ->json('GET', '/api/admin/hh5p/content', [ + 'order_by' => 'library_title', + 'order' => 'DESC' + ]); + + $this->assertTrue($response->getData()->data[0]->library->name === $libraryTwo->name); + $this->assertTrue($response->getData()->data[1]->library->name === $libraryOne->name); + + $response = $this + ->actingAs($this->user, 'api') + ->json('GET', '/api/admin/hh5p/content', [ + 'order_by' => 'title', + 'order' => 'ASC' + ]); + + $this->assertTrue($response->getData()->data[0]->title === 'A title'); + $this->assertTrue($response->getData()->data[1]->title === 'B title'); + + $response = $this + ->actingAs($this->user, 'api') + ->json('GET', '/api/admin/hh5p/content', [ + 'order_by' => 'title', + 'order' => 'DESC' + ]); + + $this->assertTrue($response->getData()->data[0]->title === 'B title'); + $this->assertTrue($response->getData()->data[1]->title === 'A title'); + + $response = $this + ->actingAs($this->user, 'api') + ->json('GET', '/api/admin/hh5p/content', [ + 'order_by' => 'id', + 'order' => 'ASC' + ]); + + $this->assertTrue($response->getData()->data[0]->id === $contentOne->getKey()); + $this->assertTrue($response->getData()->data[1]->id === $contentTwo->getKey()); + + $response = $this + ->actingAs($this->user, 'api') + ->json('GET', '/api/admin/hh5p/content', [ + 'order_by' => 'id', + 'order' => 'DESC' + ]); + + $this->assertTrue($response->getData()->data[0]->id === $contentTwo->getKey()); + $this->assertTrue($response->getData()->data[1]->id === $contentOne->getKey()); + + $response = $this + ->actingAs($this->user, 'api') + ->json('GET', '/api/admin/hh5p/content', [ + 'order_by' => 'library_id', + 'order' => 'ASC' + ]); + + $this->assertTrue($response->getData()->data[0]->library_id === $libraryOne->getKey()); + $this->assertTrue($response->getData()->data[1]->library_id === $libraryTwo->getKey()); + + $response = $this + ->actingAs($this->user, 'api') + ->json('GET', '/api/admin/hh5p/content', [ + 'order_by' => 'library_id', + 'order' => 'DESC' + ]); + + $this->assertTrue($response->getData()->data[0]->library_id === $libraryTwo->getKey()); + $this->assertTrue($response->getData()->data[1]->library_id === $libraryOne->getKey()); + } + public function testContentUnpaginatedList(): void { $h5pContents = H5PContent::factory() @@ -530,7 +636,7 @@ public function testContentUploading(): void $this->assertEquals('Arithmetic Quiz', $data->title); $this->assertNotEquals('New Content (from file)', $data->title); - $this->assertEquals($data->uuid,$result->uuid); + $this->assertEquals($data->uuid, $result->uuid); $this->assertEquals('Arithmetic Quiz', $result->title); }