Skip to content

Commit a61112d

Browse files
committed
feat: introduce ArticleViewer and ViewerGateway for article visibility management
1 parent 2b5ba78 commit a61112d

File tree

9 files changed

+164
-24
lines changed

9 files changed

+164
-24
lines changed

contexts/ArticlePublishing/Application/Coordinators/ArticlePublishingCoordinator.php

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@
1212
use Contexts\ArticlePublishing\Application\DTOs\UpdateArticleDTO;
1313
use Contexts\ArticlePublishing\Domain\Gateway\CategoryGateway;
1414
use Contexts\ArticlePublishing\Domain\Gateway\CurrentUserGateway;
15+
use Contexts\ArticlePublishing\Domain\Gateway\ViewerGateway;
1516
use Contexts\ArticlePublishing\Domain\Models\Article;
1617
use Contexts\ArticlePublishing\Domain\Models\ArticleId;
1718
use Contexts\ArticlePublishing\Domain\Models\ArticleStatus;
19+
use Contexts\ArticlePublishing\Domain\Models\ArticleVisibility;
1820
use Contexts\ArticlePublishing\Domain\Models\AuthorId;
1921
use Contexts\ArticlePublishing\Domain\Policies\GlobalPermissionPolicy;
22+
use Contexts\ArticlePublishing\Domain\Policies\VisibilityPolicy;
2023
use Contexts\ArticlePublishing\Infrastructure\Repositories\ArticleRepository;
2124
use Contexts\Shared\Policies\CompositePolicy;
2225
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
@@ -26,10 +29,11 @@ class ArticlePublishingCoordinator extends BaseCoordinator
2629
public function __construct(
2730
private ArticleRepository $repository,
2831
private CategoryGateway $categoryGateway,
29-
private CurrentUserGateway $currentUserGateway
32+
private CurrentUserGateway $currentUserGateway,
33+
private ViewerGateway $viewerGateway,
3034
) {}
3135

32-
public function create(CreateArticleDTO $data): Article
36+
public function create(CreateArticleDTO $data): ArticleVisibility
3337
{
3438
CompositePolicy::allOf([
3539
new GlobalPermissionPolicy('publish_article'),
@@ -48,7 +52,9 @@ public function create(CreateArticleDTO $data): Article
4852

4953
$this->dispatchDomainEvents($article);
5054

51-
return $article;
55+
$viewer = $this->viewerGateway->getCurrentViewer();
56+
57+
return (new VisibilityPolicy($viewer))->fromArticle($article);
5258
}
5359

5460
private function createDraft(CreateArticleDTO $data, AuthorId $authorId): Article
@@ -93,17 +99,29 @@ public function publishDraft(int $id): void
9399
$this->dispatchDomainEvents($article);
94100
}
95101

96-
public function getArticle(int $id): Article
102+
public function getArticle(int $id): ArticleVisibility
97103
{
98-
return $this->repository->getById(ArticleId::fromInt($id));
104+
$article = $this->repository->getById(ArticleId::fromInt($id));
105+
106+
$viewer = $this->viewerGateway->getCurrentViewer();
107+
108+
return (new VisibilityPolicy($viewer))->fromArticle($article);
99109
}
100110

101111
public function getArticleList(GetArticleListDTO $data): LengthAwarePaginator
102112
{
103-
return $this->repository->paginate($data->page, $data->perPage, $data->toCriteria());
113+
$viewer = $this->viewerGateway->getCurrentViewer();
114+
115+
$paginator = $this->repository->paginate($data->page, $data->perPage, $data->toCriteria());
116+
117+
$paginator->getCollection()->transform(function (Article $article) use ($viewer) {
118+
return (new VisibilityPolicy($viewer))->fromArticle($article);
119+
});
120+
121+
return $paginator;
104122
}
105123

106-
public function updateArticle(int $id, UpdateArticleDTO $data): Article
124+
public function updateArticle(int $id, UpdateArticleDTO $data): ArticleVisibility
107125
{
108126
CompositePolicy::allOf([
109127
new GlobalPermissionPolicy('publish_article'),
@@ -123,10 +141,12 @@ public function updateArticle(int $id, UpdateArticleDTO $data): Article
123141

124142
$this->dispatchDomainEvents($article);
125143

126-
return $article;
144+
$viewer = $this->viewerGateway->getCurrentViewer();
145+
146+
return (new VisibilityPolicy($viewer))->fromArticle($article);
127147
}
128148

129-
public function archiveArticle(int $id)
149+
public function archiveArticle(int $id): ArticleVisibility
130150
{
131151
CompositePolicy::allOf([
132152
new GlobalPermissionPolicy('publish_article'),
@@ -137,10 +157,12 @@ public function archiveArticle(int $id)
137157
$article->archive();
138158
$this->repository->update($article);
139159

140-
return $article;
160+
$viewer = $this->viewerGateway->getCurrentViewer();
161+
162+
return (new VisibilityPolicy($viewer))->fromArticle($article);
141163
}
142164

143-
public function deleteArticle(int $id)
165+
public function deleteArticle(int $id): ArticleVisibility
144166
{
145167
CompositePolicy::allOf([
146168
new GlobalPermissionPolicy('publish_article'),
@@ -151,6 +173,8 @@ public function deleteArticle(int $id)
151173

152174
$this->repository->delete($article);
153175

154-
return $article;
176+
$viewer = $this->viewerGateway->getCurrentViewer();
177+
178+
return (new VisibilityPolicy($viewer))->fromArticle($article);
155179
}
156180
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Contexts\ArticlePublishing\Domain\Gateway;
6+
7+
use Contexts\ArticlePublishing\Domain\Models\ArticleViewer;
8+
9+
interface ViewerGateway
10+
{
11+
public function getCurrentViewer(): ArticleViewer;
12+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Contexts\ArticlePublishing\Domain\Models;
6+
7+
use Contexts\Shared\ValueObjects\Viewer;
8+
9+
class ArticleViewer extends Viewer {}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Contexts\ArticlePublishing\Domain\Models;
6+
7+
use Carbon\CarbonImmutable;
8+
9+
class ArticleVisibility
10+
{
11+
public function __construct(
12+
private int $id,
13+
private string $title,
14+
private string $body,
15+
private string $status,
16+
private array $categories,
17+
private int $authorId,
18+
private ?CarbonImmutable $created_at = null,
19+
private ?CarbonImmutable $updated_at = null
20+
) {}
21+
22+
public function getId(): int
23+
{
24+
return $this->id;
25+
}
26+
27+
public function getTitle(): string
28+
{
29+
return $this->title;
30+
}
31+
32+
public function getBody(): string
33+
{
34+
return $this->body;
35+
}
36+
37+
public function getStatus(): string
38+
{
39+
return $this->status;
40+
}
41+
42+
public function getCategories(): array
43+
{
44+
return $this->categories;
45+
}
46+
47+
public function getAuthorId(): int
48+
{
49+
return $this->authorId;
50+
}
51+
52+
public function getCreatedAt(): ?CarbonImmutable
53+
{
54+
return $this->created_at;
55+
}
56+
57+
public function getUpdatedAt(): ?CarbonImmutable
58+
{
59+
return $this->updated_at;
60+
}
61+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Contexts\ArticlePublishing\Infrastructure\Adapters;
6+
7+
use Contexts\ArticlePublishing\Domain\Gateway\ViewerGateway;
8+
use Contexts\ArticlePublishing\Domain\Models\ArticleViewer;
9+
use Contexts\Authorization\Contracts\V1\Services\CurrentUserService;
10+
use Contexts\Shared\ValueObjects\ViewerId;
11+
use Contexts\Shared\ValueObjects\ViewerRoleCollection;
12+
13+
class ViewerAdapter implements ViewerGateway
14+
{
15+
public function __construct(
16+
private CurrentUserService $currentUserService
17+
) {}
18+
19+
public function getCurrentViewer(): ArticleViewer
20+
{
21+
$currentUser = $this->currentUserService->getCurrentUser();
22+
$roleCollection = ViewerRoleCollection::fromPlainArray($currentUser->roles);
23+
24+
return new ArticleViewer(
25+
ViewerId::fromInt($currentUser->id),
26+
$currentUser->displayName,
27+
$currentUser->email,
28+
$roleCollection
29+
);
30+
}
31+
}

contexts/ArticlePublishing/Infrastructure/Repositories/ArticleRepository.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
use Contexts\ArticlePublishing\Domain\Models\ArticleId;
1111
use Contexts\ArticlePublishing\Domain\Models\ArticleStatus;
1212
use Contexts\ArticlePublishing\Infrastructure\Records\ArticleRecord;
13-
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
1413
use Illuminate\Database\Eloquent\ModelNotFoundException;
14+
use Illuminate\Pagination\LengthAwarePaginator;
1515
use Illuminate\Support\Facades\DB;
1616

1717
class ArticleRepository

contexts/ArticlePublishing/Infrastructure/ServiceProvider.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
use Contexts\ArticlePublishing\Domain\Gateway\AuthorizationGateway;
99
use Contexts\ArticlePublishing\Domain\Gateway\CategoryGateway;
1010
use Contexts\ArticlePublishing\Domain\Gateway\CurrentUserGateway;
11+
use Contexts\ArticlePublishing\Domain\Gateway\ViewerGateway;
1112
use Contexts\ArticlePublishing\Infrastructure\Adapters\AuthorizationAdapter;
1213
use Contexts\ArticlePublishing\Infrastructure\Adapters\CategoryAdapter;
1314
use Contexts\ArticlePublishing\Infrastructure\Adapters\CurrentUserAdapter;
15+
use Contexts\ArticlePublishing\Infrastructure\Adapters\ViewerAdapter;
1416
use Contexts\ArticlePublishing\Infrastructure\EventListeners\ConsoleOutputListener;
1517
use Contexts\CategoryManagement\Application\Coordinators\CategoryManagementCoordinator;
1618
use Illuminate\Foundation\Support\Providers\RouteServiceProvider;
@@ -52,6 +54,7 @@ public function map(): void
5254

5355
$this->app->bind(AuthorizationGateway::class, AuthorizationAdapter::class);
5456
$this->app->bind(CurrentUserGateway::class, CurrentUserAdapter::class);
57+
$this->app->bind(ViewerGateway::class, ViewerAdapter::class);
5558
}
5659

5760
public function provides(): array

contexts/ArticlePublishing/Presentation/Controllers/ArticlePublishingController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public function archiveArticle(ArticleIdRequest $request)
7171
$id = (int) ($request->validated()['id']);
7272
$result = app(ArticlePublishingCoordinator::class)->archiveArticle($id);
7373

74-
return $this->success(['id' => $result->getId()->getValue()])
74+
return $this->success(['id' => $result->getId()])
7575
->message('Article archived successfully')
7676
->send();
7777
}
@@ -81,7 +81,7 @@ public function deleteArticle(ArticleIdRequest $request)
8181
$id = (int) ($request->validated()['id']);
8282
$result = app(ArticlePublishingCoordinator::class)->deleteArticle($id);
8383

84-
return $this->success(['id' => $result->getId()->getValue()])
84+
return $this->success(['id' => $result->getId()])
8585
->message('Article deleted successfully')
8686
->send();
8787
}

contexts/ArticlePublishing/Presentation/Resources/ArticleResource.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,23 @@ class ArticleResource extends JsonResource
1212
public function toArray(Request $request): array
1313
{
1414
/**
15-
* @var \Contexts\ArticlePublishing\Domain\Models\Article $article
15+
* @var \Contexts\ArticlePublishing\Domain\Models\ArticleVisibility $article
1616
*/
1717
$article = $this->resource;
1818

1919
return [
20-
'id' => (int) $article->getId()->getValue(),
20+
'id' => (int) $article->getId(),
2121

2222
'title' => (string) $article->getTitle(),
2323
'body' => (string) $article->getbody(),
24-
'status' => (string) $article->getStatus()->getValue(),
25-
'categories' => $article->getCategories()->map(fn ($category) => [
26-
'id' => $category->getId(),
27-
'label' => $category->getLabel(),
28-
])->toArray(),
29-
'author_id' => (int) $article->getAuthorId()->getValue(),
24+
'status' => (string) $article->getStatus(),
25+
'categories' => $article->getCategories(),
26+
'author_id' => (int) $article->getAuthorId(),
3027
'created_at' => $article->getCreatedAt()->format('Y-m-d H:i:s'),
31-
'updated_at' => $article->getUpdatedAt()?->format('Y-m-d H:i:s'),
28+
'updated_at' => $this->when(
29+
(bool) $article->getUpdatedAt(),
30+
$article->getUpdatedAt()?->format('Y-m-d H:i:s')
31+
),
3232
];
3333
}
3434
}

0 commit comments

Comments
 (0)