Skip to content

Commit 7d46663

Browse files
committed
feat: add sorting functionality to article list retrieval
1 parent 8acf6f2 commit 7d46663

File tree

8 files changed

+73
-21
lines changed

8 files changed

+73
-21
lines changed

contexts/ArticlePublishing/Application/Coordinators/ArticlePublishingCoordinator.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,12 @@ public function getArticleList(GetArticleListDTO $data): LengthAwarePaginator
120120

121121
$viewer = $this->viewerGateway->getCurrentViewer();
122122

123-
$paginator = $this->repository->paginate($data->currentPage, $data->perPage, $data->toCriteria());
123+
$paginator = $this->repository->paginate(
124+
$data->currentPage,
125+
$data->perPage,
126+
$data->toCriteria(),
127+
$data->toSorting()
128+
);
124129

125130
$paginator->getCollection()->transform(function (Article $article) use ($viewer) {
126131
return (new VisibilityPolicy($viewer))->fromArticle($article);

contexts/ArticlePublishing/Application/DTOs/GetArticleListDTO.php

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44

55
namespace Contexts\ArticlePublishing\Application\DTOs;
66

7-
class GetArticleListDTO
7+
use Contexts\Shared\Application\BaseGetListDTO;
8+
9+
class GetArticleListDTO extends BaseGetListDTO
810
{
11+
protected const ALLOWED_SORT_FIELDS = ['id', 'created_at'];
12+
913
public function __construct(
1014
public readonly ?string $id,
1115
public readonly ?string $title,
@@ -15,22 +19,31 @@ public function __construct(
1519
public readonly ?array $createdAtRange,
1620
public readonly int $currentPage,
1721
public readonly int $perPage,
22+
public readonly ?array $sorting
1823
) {}
1924

2025
public static function fromRequest(array $data): self
2126
{
27+
$merged = array_merge($data, self::convertFiltersToCriteria($data['filters'] ?? []));
28+
2229
return new self(
23-
$data['id'] ?? null,
24-
$data['title'] ?? null,
25-
$data['status'] ?? null,
26-
$data['category_id'] ?? null,
27-
$data['author_id'] ?? null,
28-
$data['created_at_range'] ?? null,
29-
$data['current_page'] ?? 1,
30-
$data['per_page'] ?? 10,
30+
$merged['id'] ?? null,
31+
$merged['title'] ?? null,
32+
$merged['status'] ?? null,
33+
$merged['category_id'] ?? null,
34+
$merged['author_id'] ?? null,
35+
$merged['created_at_range'] ?? null,
36+
$merged['current_page'] ?? 1,
37+
$merged['per_page'] ?? 10,
38+
self::normalizeAndFilterSorting($merged)
3139
);
3240
}
3341

42+
public function toSorting(): array
43+
{
44+
return $this->sorting;
45+
}
46+
3447
public function toCriteria(): array
3548
{
3649
return [

contexts/ArticlePublishing/Domain/Repositories/ArticleRepository.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public function getById(ArticleId $articleId): Article;
1616

1717
public function update(Article $article): Article;
1818

19-
public function paginate(int $currentPage = 1, int $perPage = 10, array $criteria = []): LengthAwarePaginator;
19+
public function paginate(int $currentPage = 1, int $perPage = 10, array $criteria = [], array $sorting = []): LengthAwarePaginator;
2020

2121
public function delete(Article $article): void;
2222
}

contexts/ArticlePublishing/Infrastructure/Factories/ArticleFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace Contexts\Authorization\Infrastructure\Factories;
5+
namespace Contexts\ArticlePublishing\Infrastructure\Factories;
66

77
use Contexts\ArticlePublishing\Infrastructure\Records\ArticleRecord;
88
use Illuminate\Database\Eloquent\Factories\Factory;

contexts/ArticlePublishing/Infrastructure/Persistence/ArticlePersistence.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,12 @@ public function update(Article $article): Article
7171
return $record->toDomain($article->getEvents());
7272
}
7373

74-
public function paginate(int $currentPage = 1, int $perPage = 10, array $criteria = []): LengthAwarePaginator
74+
public function paginate(int $currentPage = 1, int $perPage = 10, array $criteria = [], array $sorting = []): LengthAwarePaginator
7575
{
76-
$paginator = ArticleRecord::query()->search($criteria)->paginate($perPage, ['*'], 'current_page', $currentPage);
76+
$paginator = ArticleRecord::query()
77+
->search($criteria)
78+
->sorting($sorting)
79+
->paginate($perPage, ['*'], 'current_page', $currentPage);
7780

7881
$paginator->getCollection()->transform(function ($record) {
7982
return $record->toDomain();

contexts/ArticlePublishing/Infrastructure/Records/ArticleRecord.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
use Contexts\ArticlePublishing\Domain\Models\ArticleId;
1212
use Contexts\ArticlePublishing\Domain\Models\ArticleStatus;
1313
use Contexts\ArticlePublishing\Domain\Models\AuthorId;
14-
use Contexts\Authorization\Infrastructure\Factories\ArticleFactory;
14+
use Contexts\ArticlePublishing\Infrastructure\Factories\ArticleFactory;
1515
use Contexts\CategoryManagement\Infrastructure\Records\CategoryRecord;
1616
use Contexts\Shared\Infrastructure\BaseRecord;
17+
use Contexts\Shared\Infrastructure\Traits\HasSortingScopeTrait;
1718
use Illuminate\Database\Eloquent\Builder;
1819
use Illuminate\Database\Eloquent\Factories\Factory;
20+
use Illuminate\Database\Eloquent\Factories\HasFactory;
1921
use Illuminate\Database\Eloquent\SoftDeletes;
2022
use Illuminate\Support\Carbon;
2123

@@ -30,6 +32,8 @@
3032
*/
3133
class ArticleRecord extends BaseRecord
3234
{
35+
use HasFactory;
36+
use HasSortingScopeTrait;
3337
use SoftDeletes;
3438

3539
protected $table = 'articles';

contexts/ArticlePublishing/Presentation/Requests/GetArticleListRequest.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
namespace Contexts\ArticlePublishing\Presentation\Requests;
66

7-
use Contexts\Shared\Presentation\Requests\BaseRequest;
7+
use Contexts\Shared\Presentation\Requests\BaseListRequest;
88

9-
class GetArticleListRequest extends BaseRequest
9+
class GetArticleListRequest extends BaseListRequest
1010
{
1111
public function rules(): array
1212
{
@@ -16,10 +16,11 @@ public function rules(): array
1616
'status' => ['string', 'in:draft,published'],
1717
'category_id' => ['integer', 'gt:0'],
1818
'author_id' => ['integer', 'gt:0'],
19-
'created_at_range' => ['array', 'size:2'],
20-
'created_at_range.*' => ['date'],
21-
'current_page' => ['integer', 'gt:0'],
22-
'per_page' => ['integer', 'gt:0'],
19+
'created_at' => ['array', 'size:2'],
20+
'created_at.*' => ['date_format:Y-m-d'],
21+
...$this->paginationRule(),
22+
...$this->filtersRule(),
23+
...$this->sortingRule(),
2324
];
2425
}
2526
}

contexts/ArticlePublishing/Tests/Feature/ArticlePublishingTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
declare(strict_types=1);
44

55
use Contexts\ArticlePublishing\Domain\Events\ArticlePublishedEvent;
6+
use Contexts\ArticlePublishing\Infrastructure\Records\ArticleRecord;
67
use Contexts\Authorization\Domain\Policies\RolePolicy;
78
use Contexts\CategoryManagement\Infrastructure\Records\CategoryRecord;
89

@@ -143,6 +144,31 @@
143144
$response->assertStatus(200);
144145
});
145146

147+
it('can get a list of articles with sorting via api', function () {
148+
$initialCount = ArticleRecord::count();
149+
150+
$articles = ArticleRecord::factory(3)->create();
151+
$articles->each(function ($article) {
152+
$article->categories()->attach($this->categories->pluck('id')->toArray());
153+
});
154+
155+
$response = $this->get('articles?sorting=[{"id":"id","desc":false}]');
156+
157+
$response->assertStatus(200);
158+
$response->assertJsonCount(3 + $initialCount, 'data');
159+
160+
$responseIds = collect($response->json('data'))->pluck('id')->all();
161+
$sortedIds = collect($responseIds)->sort()->values()->all();
162+
expect($responseIds)->toBe($sortedIds);
163+
164+
$response = $this->get('articles?sorting=[{"id":"id","desc":true}]');
165+
$response->assertStatus(200);
166+
167+
$responseIds = collect($response->json('data'))->pluck('id')->all();
168+
$sortedIds = collect($responseIds)->sortDesc()->values()->all();
169+
expect($responseIds)->toBe($sortedIds);
170+
});
171+
146172
it('can update an article via api', function () {
147173

148174
$response = $this->postJson('articles', [

0 commit comments

Comments
 (0)