Skip to content

Commit 381dd7f

Browse files
committed
feat: implement publishDraft functionality and related request validation for article publishing
1 parent db5376b commit 381dd7f

File tree

10 files changed

+142
-8
lines changed

10 files changed

+142
-8
lines changed

app/Http/Requests/BaseFormRequest.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,11 @@
66

77
use Illuminate\Foundation\Http\FormRequest;
88

9-
class BaseFormRequest extends FormRequest {}
9+
class BaseFormRequest extends FormRequest
10+
{
11+
protected function prepareForValidation()
12+
{
13+
parent::prepareForValidation();
14+
$this->route('id') && $this->merge(['id' => $this->route('id')]);
15+
}
16+
}

contexts/ArticlePublishing/Application/Coordinators/ArticlePublishingCoordinator.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,15 @@ private function createPublished(CreateArticleDTO $data): Article
5353
return $this->repository->create($article);
5454
}
5555

56-
// TODO
57-
// public function publishDraft(ArticleId $id): void
58-
// {
56+
public function publishDraft(int $id): void
57+
{
58+
$article = $this->repository->getById(new ArticleId($id));
59+
$article->publish();
60+
61+
$this->repository->update($article);
5962

60-
// }
63+
$this->dispatchDomainEvents($article);
64+
}
6165

6266
private function dispatchDomainEvents(Article $article): void
6367
{

contexts/ArticlePublishing/Infrastructure/Records/ArticleRecord.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public static function mapStatusToRecord(ArticleStatus $status): int
4848
return array_search($status->getValue(), self::STATUS_MAPPING);
4949
}
5050

51-
public function toDomain(array $events): Article
51+
public function toDomain(array $events = []): Article
5252
{
5353
return Article::reconstitute(
5454
new ArticleId($this->id),

contexts/ArticlePublishing/Infrastructure/Repositories/ArticleRepository.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Contexts\ArticlePublishing\Infrastructure\Repositories;
66

77
use Contexts\ArticlePublishing\Domain\Models\Article;
8+
use Contexts\ArticlePublishing\Domain\Models\ArticleId;
89
use Contexts\ArticlePublishing\Infrastructure\Records\ArticleRecord;
910

1011
class ArticleRepository
@@ -20,4 +21,25 @@ public function create(Article $article): Article
2021

2122
return $record->toDomain($article->getDomainEvents());
2223
}
24+
25+
public function getById(ArticleId $articleId): Article
26+
{
27+
$record = ArticleRecord::findOrFail($articleId->value);
28+
29+
return $record->toDomain();
30+
}
31+
32+
public function update(Article $article): Article
33+
{
34+
$record = ArticleRecord::findOrFail($article->id->value);
35+
36+
$record->update([
37+
'title' => $article->getTitle(),
38+
'body' => $article->getbody(),
39+
'status' => ArticleRecord::mapStatusToRecord($article->getStatus()),
40+
'created_at' => $article->getCreatedAt(),
41+
]);
42+
43+
return $record->toDomain($article->getDomainEvents());
44+
}
2345
}

contexts/ArticlePublishing/Infrastructure/Routes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88
Route::middleware([])->name('ArticlePublishing.')->group(function () {
99
Route::controller(ArticlePublishingController::class)->prefix('articles')->name('ArticlePublishing.')->group(function () {
1010
Route::post('', 'createArticle')->name('createArticle');
11+
Route::put('{id}/publish', 'publishDraft')->name('publishDraft');
1112
});
1213
});

contexts/ArticlePublishing/Presentation/Controllers/ArticlePublishingController.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Contexts\ArticlePublishing\Application\Coordinators\ArticlePublishingCoordinator;
1010
use Contexts\ArticlePublishing\Presentation\Resources\ArticleResource;
1111
use Contexts\ArticlePublishing\Application\DTOs\CreateArticleDTO;
12+
use Contexts\ArticlePublishing\Presentation\Requests\PublishDraftRequest;
1213

1314
class ArticlePublishingController extends BaseController
1415
{
@@ -18,6 +19,16 @@ public function createArticle(CreateArticleRequest $request)
1819
CreateArticleDTO::fromRequest($request->validated())
1920
);
2021

21-
return $this->success($result, ArticleResource::class)->send(201);
22+
return $this->success($result, ArticleResource::class)
23+
->message('Article created successfully')
24+
->send(201);
25+
}
26+
27+
public function publishDraft(PublishDraftRequest $request)
28+
{
29+
$id = (int)($request->validated()['id']);
30+
app(ArticlePublishingCoordinator::class)->publishDraft($id);
31+
32+
return $this->success('Article published successfully')->send();
2233
}
2334
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Contexts\ArticlePublishing\Presentation\Requests;
6+
7+
use App\Http\Requests\BaseFormRequest;
8+
9+
class PublishDraftRequest extends BaseFormRequest
10+
{
11+
public function rules(): array
12+
{
13+
return [
14+
'id' => ['required', 'integer', 'gt:0', 'exists:articles,id'],
15+
];
16+
}
17+
}

contexts/ArticlePublishing/Tests/Feature/ArticlePublishingTest.php

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

55
use Contexts\ArticlePublishing\Domain\Events\ArticlePublishedEvent;
6-
use Contexts\ArticlePublishing\Infrastructure\EventListeners\ConsoleOutputListener;
76

87
it('can publish aritcle drafts via api', function () {
98
$response = $this->postJson('articles', [
@@ -36,3 +35,19 @@
3635

3736
Event::assertDispatched(ArticlePublishedEvent::class);
3837
});
38+
39+
it('can publish a draft article', function () {
40+
$response = $this->postJson('articles', [
41+
'title' => 'My Article',
42+
'body' => 'This is my article body',
43+
'status' => 'draft',
44+
]);
45+
46+
$response->assertStatus(201);
47+
48+
$id = $response->json('data.id');
49+
50+
$response = $this->putJson("articles/{$id}/publish");
51+
52+
$response->assertStatus(200);
53+
});

contexts/ArticlePublishing/Tests/Feature/Infrastructure/Repositories/ArticleRepositoryTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,51 @@
3434
'status' => ArticleRecord::mapStatusToRecord(ArticleStatus::published()),
3535
]);
3636
});
37+
38+
39+
it('can retrieve an article by ID', function () {
40+
// Create a test article in the database
41+
$createdArticle = Article::createDraft(new ArticleId(0), 'Test Article', 'Test Content', new CarbonImmutable());
42+
$articleRepository = new ArticleRepository();
43+
$savedArticle = $articleRepository->create($createdArticle);
44+
45+
// Retrieve the article using getById
46+
$retrievedArticle = $articleRepository->getById($savedArticle->id);
47+
48+
// Assert the retrieved article matches the created one
49+
expect($retrievedArticle->id->value)->toBe($savedArticle->id->value);
50+
expect($retrievedArticle->getTitle())->toBe('Test Article');
51+
expect($retrievedArticle->getBody())->toBe('Test Content');
52+
expect($retrievedArticle->getStatus()->equals(ArticleStatus::draft()))->toBeTrue();
53+
});
54+
55+
it('can update an article', function () {
56+
// Create a test article in the database
57+
$createdArticle = Article::createDraft(new ArticleId(0), 'Original Title', 'Original Content', new CarbonImmutable());
58+
$articleRepository = new ArticleRepository();
59+
$savedArticle = $articleRepository->create($createdArticle);
60+
61+
// Create an updated version of the article
62+
$updatedArticle = Article::createPublished(
63+
$savedArticle->id,
64+
'Updated Title',
65+
'Updated Content',
66+
new CarbonImmutable()
67+
);
68+
69+
// Update the article
70+
$result = $articleRepository->update($updatedArticle);
71+
72+
// Verify database was updated
73+
$this->assertDatabaseHas('articles', [
74+
'id' => $savedArticle->id->value,
75+
'title' => 'Updated Title',
76+
'body' => 'Updated Content',
77+
'status' => ArticleRecord::mapStatusToRecord(ArticleStatus::published()),
78+
]);
79+
80+
// Verify returned object reflects updates
81+
expect($result->getTitle())->toBe('Updated Title');
82+
expect($result->getBody())->toBe('Updated Content');
83+
expect($result->getStatus()->equals(ArticleStatus::published()))->toBeTrue();
84+
});
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+
it('[smoke test] id must greater than 0', function () {
6+
$response = $this->putJson('articles/-1/publish');
7+
8+
$this->assertValidationError($response, 'id', 'greater than');
9+
});

0 commit comments

Comments
 (0)