Skip to content

Commit 7efd57d

Browse files
feat: [LAR-132] create update article action and add locale field on … (#260)
2 parents e60b432 + 96c998e commit 7efd57d

23 files changed

+240
-66
lines changed

app/Actions/Article/CreateArticleAction.php

+12-11
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@
44

55
namespace App\Actions\Article;
66

7-
use App\Data\CreateArticleData;
7+
use App\Data\ArticleData;
88
use App\Models\Article;
9-
use App\Models\User;
109
use Carbon\Carbon;
1110
use Illuminate\Support\Facades\Auth;
1211

1312
final class CreateArticleAction
1413
{
15-
public function execute(CreateArticleData $articleData): Article
14+
public function execute(ArticleData $articleData): Article
1615
{
1716
if ($articleData->published_at) {
1817
$articleData->published_at = new Carbon(
@@ -21,20 +20,22 @@ public function execute(CreateArticleData $articleData): Article
2120
);
2221
}
2322

24-
/** @var User $author */
25-
$author = Auth::user();
23+
if ($articleData->submitted_at) {
24+
$articleData->submitted_at = new Carbon(
25+
time: $articleData->submitted_at,
26+
timezone: config('app.timezone')
27+
);
28+
}
2629

27-
/** @var Article $article */
28-
$article = Article::query()->create([
30+
// @phpstan-ignore-next-line
31+
return Article::query()->create([
2932
'title' => $articleData->title,
3033
'slug' => $articleData->slug,
3134
'body' => $articleData->body,
3235
'published_at' => $articleData->published_at,
33-
'submitted_at' => $articleData->is_draft ? null : now(),
36+
'submitted_at' => $articleData->submitted_at,
3437
'canonical_url' => $articleData->canonical_url,
35-
'user_id' => $author->id,
38+
'user_id' => Auth::id(),
3639
]);
37-
38-
return $article;
3940
}
4041
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Actions\Article;
6+
7+
use App\Data\ArticleData;
8+
use App\Exceptions\CannotUpdateApprovedArticle;
9+
use App\Models\Article;
10+
use Carbon\Carbon;
11+
12+
final class UpdateArticleAction
13+
{
14+
public function execute(ArticleData $articleData, Article $article): Article
15+
{
16+
if ($article->isApproved()) {
17+
throw new CannotUpdateApprovedArticle(__('notifications.exceptions.approved_article'));
18+
}
19+
20+
if ($articleData->published_at) {
21+
$articleData->published_at = new Carbon(
22+
time: $articleData->published_at,
23+
timezone: config('app.timezone')
24+
);
25+
}
26+
27+
if ($articleData->submitted_at) {
28+
$articleData->submitted_at = new Carbon(
29+
time: $articleData->submitted_at,
30+
timezone: config('app.timezone')
31+
);
32+
}
33+
34+
$article->update($articleData->toArray());
35+
36+
$article->refresh();
37+
38+
return $article;
39+
}
40+
}

app/Data/CreateArticleData.php renamed to app/Data/ArticleData.php

+5-4
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@
77
use Carbon\Carbon;
88
use Spatie\LaravelData\Data;
99

10-
final class CreateArticleData extends Data
10+
final class ArticleData extends Data
1111
{
1212
public function __construct(
1313
public string $title,
1414
public string $slug,
1515
public string $body,
16-
public string $canonical_url,
17-
public ?Carbon $published_at,
18-
public bool $is_draft = false,
16+
public string $locale,
17+
public ?string $canonical_url = null,
18+
public ?Carbon $published_at = null,
19+
public ?Carbon $submitted_at = null,
1920
) {}
2021
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Exceptions;
6+
7+
final class CannotUpdateApprovedArticle extends \Exception {}

app/Livewire/Components/Slideovers/ArticleForm.php

+28-16
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
namespace App\Livewire\Components\Slideovers;
66

77
use App\Actions\Article\CreateArticleAction;
8-
use App\Data\CreateArticleData;
8+
use App\Actions\Article\UpdateArticleAction;
9+
use App\Data\ArticleData;
910
use App\Exceptions\UnverifiedUserException;
1011
use App\Livewire\Traits\WithAuthenticatedUser;
1112
use App\Models\Article;
@@ -44,6 +45,7 @@ public function mount(?int $articleId = null): void
4445
$this->form->fill(array_merge($this->article->toArray(), [
4546
'is_draft' => ! $this->article->published_at, // @phpstan-ignore-line
4647
'published_at' => $this->article->published_at, // @phpstan-ignore-line
48+
'locale' => $this->article->locale ?? app()->getLocale(),
4749
]));
4850
}
4951

@@ -114,6 +116,11 @@ public function form(Form $form): Form
114116
->required()
115117
->minItems(1)
116118
->maxItems(3),
119+
Forms\Components\ToggleButtons::make('locale')
120+
->label(__('validation.attributes.locale'))
121+
->options(['en' => 'En', 'fr' => 'Fr'])
122+
->helperText(__('global.locale_help'))
123+
->grouped(),
117124
])
118125
->columnSpan(1),
119126
Forms\Components\Group::make()
@@ -163,42 +170,47 @@ public function save(): void
163170

164171
$this->validate();
165172

166-
$validated = $this->form->getState();
173+
$state = $this->form->getState();
174+
175+
$publishedFields = [
176+
'published_at' => data_get($state, 'published_at')
177+
? new Carbon(data_get($state, 'published_at'))
178+
: null,
179+
'submitted_at' => data_get($state, 'is_draft') ? null : now(),
180+
];
167181

168182
if ($this->article?->id) {
169-
$this->article->update(array_merge($validated, [
170-
'submitted_at' => $validated['is_draft'] ? null : now(),
171-
]));
172-
$this->form->model($this->article)->saveRelationships();
173-
$this->article->fresh();
183+
$article = app(UpdateArticleAction::class)->execute(
184+
articleData: ArticleData::from(array_merge($state, $publishedFields)),
185+
article: $this->article
186+
);
174187

175188
Notification::make()
176189
->title(
177-
$this->article->submitted_at
190+
$article->submitted_at
178191
? __('notifications.article.submitted')
179192
: __('notifications.article.updated'),
180193
)
181194
->success()
182195
->send();
183196
} else {
184-
$article = app(CreateArticleAction::class)->execute(CreateArticleData::from(array_merge($validated, [
185-
'published_at' => array_key_exists('published_at', $validated)
186-
? new Carbon($validated['published_at'])
187-
: null,
188-
])));
189-
$this->form->model($article)->saveRelationships();
197+
$article = app(CreateArticleAction::class)->execute(
198+
ArticleData::from(array_merge($state, $publishedFields))
199+
);
190200

191201
Notification::make()
192202
->title(
193-
$validated['is_draft'] === false
203+
data_get($state, 'is_draft') === false
194204
? __('notifications.article.submitted')
195205
: __('notifications.article.created'),
196206
)
197207
->success()
198208
->send();
199209
}
200210

201-
$this->redirect(route('articles.show', ['article' => $article ?? $this->article]), navigate: true);
211+
$this->form->model($article)->saveRelationships();
212+
213+
$this->redirect(route('articles.show', ['article' => $article]), navigate: true);
202214
}
203215

204216
public function render(): View

app/Livewire/Components/Slideovers/DiscussionForm.php

+9-4
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ public function mount(?int $discussionId = null): void
3939
? Discussion::query()->findOrFail($discussionId)
4040
: new Discussion;
4141

42-
$this->form->fill(array_merge(
43-
$this->discussion->toArray(),
44-
['user_id' => $this->discussion->user_id ?? Auth::id()]
45-
));
42+
$this->form->fill(array_merge($this->discussion->toArray(), [
43+
'user_id' => $this->discussion->user_id ?? Auth::id(),
44+
'locale' => $this->discussion->locale ?? app()->getLocale(),
45+
]));
4646
}
4747

4848
public function form(Form $form): Form
@@ -71,6 +71,11 @@ public function form(Form $form): Form
7171
->minItems(1)
7272
->maxItems(3)
7373
->preload(),
74+
Forms\Components\ToggleButtons::make('locale')
75+
->label(__('validation.attributes.locale'))
76+
->options(['en' => 'En', 'fr' => 'Fr'])
77+
->helperText(__('global.locale_help'))
78+
->grouped(),
7479
Forms\Components\MarkdownEditor::make('body')
7580
->toolbarButtons([
7681
'blockquote',

app/Livewire/Components/Slideovers/ThreadForm.php

+10-5
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ public function mount(?int $threadId = null): void
3939
? Thread::query()->findOrFail($threadId)
4040
: new Thread;
4141

42-
$this->form->fill(array_merge(
43-
$this->thread->toArray(),
44-
['user_id' => $this->thread->user_id ?? Auth::id()]
45-
));
42+
$this->form->fill(array_merge($this->thread->toArray(), [
43+
'user_id' => $this->thread->user_id ?? Auth::id(),
44+
'locale' => $this->thread->locale ?? app()->getLocale(),
45+
]));
4646
}
4747

4848
public static function panelMaxWidth(): string
@@ -78,10 +78,15 @@ public function form(Form $form): Form
7878
Forms\Components\Select::make('channels')
7979
->multiple()
8080
->relationship(titleAttribute: 'name')
81-
->searchable()
81+
->preload()
8282
->required()
8383
->minItems(1)
8484
->maxItems(3),
85+
Forms\Components\ToggleButtons::make('locale')
86+
->label(__('validation.attributes.locale'))
87+
->options(['en' => 'En', 'fr' => 'Fr'])
88+
->helperText(__('global.locale_help'))
89+
->grouped(),
8590
Forms\Components\MarkdownEditor::make('body')
8691
->fileAttachmentsDisk('public')
8792
->toolbarButtons([

app/Models/Article.php

+10-8
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,16 @@
3131
* @property string | null $canonical_url
3232
* @property int | null $tweet_id
3333
* @property int $user_id
34+
* @property string | null $locale
3435
* @property-read User $user
35-
* @property \Illuminate\Support\Carbon | null $published_at
36-
* @property \Illuminate\Support\Carbon | null $submitted_at
37-
* @property \Illuminate\Support\Carbon | null $approved_at
38-
* @property \Illuminate\Support\Carbon | null $shared_at
39-
* @property \Illuminate\Support\Carbon | null $declined_at
40-
* @property \Illuminate\Support\Carbon | null $sponsored_at
41-
* @property \Illuminate\Support\Carbon $created_at
42-
* @property \Illuminate\Support\Carbon $updated_at
36+
* @property \Carbon\Carbon | null $published_at
37+
* @property \Carbon\Carbon | null $submitted_at
38+
* @property \Carbon\Carbon | null $approved_at
39+
* @property \Carbon\Carbon | null $shared_at
40+
* @property \Carbon\Carbon | null $declined_at
41+
* @property \Carbon\Carbon | null $sponsored_at
42+
* @property \Carbon\Carbon $created_at
43+
* @property \Carbon\Carbon $updated_at
4344
* @property \Illuminate\Database\Eloquent\Collection | Tag[] $tags
4445
*/
4546
final class Article extends Model implements HasMedia, ReactableInterface, Viewable
@@ -69,6 +70,7 @@ final class Article extends Model implements HasMedia, ReactableInterface, Viewa
6970
'shared_at',
7071
'sponsored_at',
7172
'published_at',
73+
'locale',
7274
];
7375

7476
protected $casts = [

app/Models/Discussion.php

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
* @property string $body
3535
* @property bool $locked
3636
* @property bool $is_pinned
37+
* @property string | null $locale
3738
* @property int $user_id
3839
* @property-read int $count_all_replies_with_child
3940
* @property Carbon $created_at
@@ -62,6 +63,7 @@ final class Discussion extends Model implements ReactableInterface, ReplyInterfa
6263
'user_id',
6364
'is_pinned',
6465
'locked',
66+
'locale',
6567
];
6668

6769
protected $casts = [

app/Models/Thread.php

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
* @property int $user_id
4444
* @property int $solution_reply_id
4545
* @property bool $locked
46+
* @property string | null $locale
4647
* @property Carbon | null $last_posted_at
4748
* @property Carbon $created_at
4849
* @property Carbon $updated_at
@@ -73,6 +74,7 @@ final class Thread extends Model implements Feedable, ReactableInterface, ReplyI
7374
'body',
7475
'slug',
7576
'user_id',
77+
'locale',
7678
];
7779

7880
protected $casts = [

database/factories/ArticleFactory.php

+22
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,28 @@ public function definition(): array
1919
'title' => $this->faker->sentence(),
2020
'body' => $this->faker->paragraphs(3, true),
2121
'slug' => $this->faker->unique()->slug(),
22+
'locale' => $this->faker->randomElement(['en', 'fr']),
2223
];
2324
}
25+
26+
public function approved(): self
27+
{
28+
return $this->state(function (array $attributes): array {
29+
return [
30+
'published_at' => now()->addDays(2),
31+
'submitted_at' => now(),
32+
'approved_at' => now(),
33+
];
34+
});
35+
}
36+
37+
public function submitted(): self
38+
{
39+
return $this->state(function (array $attributes): array {
40+
return [
41+
'submitted_at' => now(),
42+
'published_at' => now()->addDay(),
43+
];
44+
});
45+
}
2446
}

database/factories/TagFactory.php

+10
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,14 @@ public function definition(): array
1919
'concerns' => ['post', 'discussion', 'tutorial'],
2020
];
2121
}
22+
23+
public function article(): self
24+
{
25+
return $this->state(['concerns' => ['post', 'tutorial']]);
26+
}
27+
28+
public function discussion(): self
29+
{
30+
return $this->state(['concerns' => ['discussion']]);
31+
}
2232
}

lang/en/global.php

+1
Original file line numberDiff line numberDiff line change
@@ -102,5 +102,6 @@
102102
'website' => 'Website',
103103
'characters' => ':number characters',
104104
'like' => ':count like',
105+
'locale_help' => 'The language in which your content will be available on the site.',
105106

106107
];

lang/en/notifications.php

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'exceptions' => [
2323
'unverified_user' => 'You are not authorized to do this. Your e-mail is not verified',
2424
'spam_exist' => 'Spam already reported.',
25+
'approved_article' => 'An approved article cannot be updated.',
2526
],
2627

2728
'reply' => [

0 commit comments

Comments
 (0)