extend Post entity with feature slot
Adds nullable feature_slot column (unique) plus repo findByFeatureSlot/findFeatured/update methods so admins can pin a post into one of two slots.
This commit is contained in:
parent
64a334c63e
commit
f73e5a1af5
6 changed files with 160 additions and 4 deletions
|
|
@ -4,6 +4,7 @@ namespace App\Post;
|
||||||
|
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use DateTimeZone;
|
use DateTimeZone;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
class EloquentPostRepository implements PostRepository
|
class EloquentPostRepository implements PostRepository
|
||||||
{
|
{
|
||||||
|
|
@ -65,6 +66,53 @@ class EloquentPostRepository implements PostRepository
|
||||||
PostModel::query()->where('id', $id)->delete();
|
PostModel::query()->where('id', $id)->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
public function update(Post $post): Post
|
||||||
|
{
|
||||||
|
$model = PostModel::find($post->getId());
|
||||||
|
if ($model === null) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Post with id: {$post->getId()} does not exist"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$model->user_id = $post->getUserId();
|
||||||
|
$model->title = $post->getTitle();
|
||||||
|
$model->body = $post->getBody();
|
||||||
|
$model->created_at = $post->getCreatedAt();
|
||||||
|
$model->feature_slot = $post->getFeatureSlot();
|
||||||
|
$model->save();
|
||||||
|
|
||||||
|
return $this->toDomain($model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findByFeatureSlot(int $slot): ?Post
|
||||||
|
{
|
||||||
|
$model = PostModel::query()
|
||||||
|
->where('feature_slot', $slot)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
return $model === null ? null : $this->toDomain($model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Post[]
|
||||||
|
*/
|
||||||
|
public function findFeatured(): array
|
||||||
|
{
|
||||||
|
$models = PostModel::query()
|
||||||
|
->whereNotNull('feature_slot')
|
||||||
|
->orderBy('feature_slot', 'asc')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return $models->map(
|
||||||
|
function (PostModel $model) {
|
||||||
|
return $this->toDomain($model);
|
||||||
|
},
|
||||||
|
)->all();
|
||||||
|
}
|
||||||
|
|
||||||
private function toDomain(PostModel $model): Post
|
private function toDomain(PostModel $model): Post
|
||||||
{
|
{
|
||||||
$utc = new DateTimeZone('UTC');
|
$utc = new DateTimeZone('UTC');
|
||||||
|
|
@ -78,6 +126,7 @@ class EloquentPostRepository implements PostRepository
|
||||||
$model->created_at->toDateTimeString(),
|
$model->created_at->toDateTimeString(),
|
||||||
$utc,
|
$utc,
|
||||||
),
|
),
|
||||||
|
featureSlot: $model->feature_slot,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ class Post
|
||||||
private string $title,
|
private string $title,
|
||||||
private string $body,
|
private string $body,
|
||||||
private DateTimeImmutable $createdAt,
|
private DateTimeImmutable $createdAt,
|
||||||
|
private ?int $featureSlot,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function getId(): int
|
public function getId(): int
|
||||||
|
|
@ -38,4 +39,14 @@ class Post
|
||||||
{
|
{
|
||||||
return $this->createdAt;
|
return $this->createdAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFeatureSlot(): ?int
|
||||||
|
{
|
||||||
|
return $this->featureSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isFeatured(): bool
|
||||||
|
{
|
||||||
|
return $this->featureSlot !== null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,27 @@
|
||||||
|
|
||||||
namespace App\Post;
|
namespace App\Post;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Carbon;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
* @property int $user_id
|
* @property int $user_id
|
||||||
* @property string $title
|
* @property string $title
|
||||||
* @property string $body
|
* @property string $body
|
||||||
* @property Carbon $created_at
|
* @property DateTimeImmutable $created_at
|
||||||
|
* @property ?int $feature_slot
|
||||||
*
|
*
|
||||||
* @method static Builder<static>|PostModel newModelQuery()
|
* @method static Builder<static>|PostModel newModelQuery()
|
||||||
* @method static Builder<static>|PostModel newQuery()
|
* @method static Builder<static>|PostModel newQuery()
|
||||||
* @method static Builder<static>|PostModel query()
|
* @method static Builder<static>|PostModel query()
|
||||||
* @method static Builder<static>|PostModel whereId($value)
|
* @method static Builder<static>|PostModel whereId($value)
|
||||||
* @method static Builder<static>|PostModel whereUserId($value)
|
* @method static Builder<static>|PostModel whereUserId($value)
|
||||||
|
* @method static Builder<static>|PostModel whereTitle($value)
|
||||||
|
* @method static Builder<static>|PostModel whereBody($value)
|
||||||
|
* @method static Builder<static>|PostModel whereCreatedAt($value)
|
||||||
|
* @method static Builder<static>|PostModel whereFeatureSlot($value)
|
||||||
*
|
*
|
||||||
* @mixin \Eloquent
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
|
|
@ -32,9 +37,11 @@ class PostModel extends Model
|
||||||
'title',
|
'title',
|
||||||
'body',
|
'body',
|
||||||
'created_at',
|
'created_at',
|
||||||
|
'feature_slot',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'created_at' => 'datetime',
|
'created_at' => 'immutable_datetime',
|
||||||
|
'feature_slot' => 'integer',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace App\Post;
|
namespace App\Post;
|
||||||
|
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
interface PostRepository
|
interface PostRepository
|
||||||
{
|
{
|
||||||
public function create(CreatePostDto $dto): Post;
|
public function create(CreatePostDto $dto): Post;
|
||||||
|
|
@ -19,4 +21,16 @@ interface PostRepository
|
||||||
public function findRecent(int $limit): array;
|
public function findRecent(int $limit): array;
|
||||||
|
|
||||||
public function delete(int $id): void;
|
public function delete(int $id): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
public function update(Post $post): Post;
|
||||||
|
|
||||||
|
public function findByFeatureSlot(int $slot): ?Post;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Post[]
|
||||||
|
*/
|
||||||
|
public function findFeatured(): array;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('posts', function (Blueprint $table) {
|
||||||
|
$table->unsignedTinyInteger('feature_slot')->nullable();
|
||||||
|
$table->unique('feature_slot');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('posts', function (Blueprint $table) {
|
||||||
|
$table->dropUnique(['feature_slot']);
|
||||||
|
$table->dropColumn('feature_slot');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -5,6 +5,7 @@ namespace Tests\Fakes;
|
||||||
use App\Post\CreatePostDto;
|
use App\Post\CreatePostDto;
|
||||||
use App\Post\Post;
|
use App\Post\Post;
|
||||||
use App\Post\PostRepository;
|
use App\Post\PostRepository;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
class FakePostRepository implements PostRepository
|
class FakePostRepository implements PostRepository
|
||||||
{
|
{
|
||||||
|
|
@ -22,10 +23,11 @@ class FakePostRepository implements PostRepository
|
||||||
title: $dto->title,
|
title: $dto->title,
|
||||||
body: $dto->body,
|
body: $dto->body,
|
||||||
createdAt: $dto->createdAt,
|
createdAt: $dto->createdAt,
|
||||||
|
featureSlot: null,
|
||||||
);
|
);
|
||||||
$this->existingPosts[$id] = $post;
|
$this->existingPosts[$id] = $post;
|
||||||
|
|
||||||
return $post;
|
return $this->copy($post);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function find(int $id): ?Post
|
public function find(int $id): ?Post
|
||||||
|
|
@ -85,6 +87,54 @@ class FakePostRepository implements PostRepository
|
||||||
unset($this->existingPosts[$id]);
|
unset($this->existingPosts[$id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
public function update(Post $post): Post
|
||||||
|
{
|
||||||
|
$id = $post->getId();
|
||||||
|
if (! isset($this->existingPosts[$id])) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Post with id: $id does not exist"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$this->existingPosts[$id] = $post;
|
||||||
|
|
||||||
|
return $this->copy($post);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findByFeatureSlot(int $slot): ?Post
|
||||||
|
{
|
||||||
|
foreach ($this->existingPosts as $post) {
|
||||||
|
if ($post->getFeatureSlot() === $slot) {
|
||||||
|
return $this->copy($post);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Post[]
|
||||||
|
*/
|
||||||
|
public function findFeatured(): array
|
||||||
|
{
|
||||||
|
$featured = [];
|
||||||
|
foreach ($this->existingPosts as $post) {
|
||||||
|
if ($post->isFeatured()) {
|
||||||
|
$featured[] = $this->copy($post);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usort(
|
||||||
|
$featured,
|
||||||
|
function (Post $left, Post $right) {
|
||||||
|
return $left->getFeatureSlot() <=> $right->getFeatureSlot();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return $featured;
|
||||||
|
}
|
||||||
|
|
||||||
private function copy(Post $post): Post
|
private function copy(Post $post): Post
|
||||||
{
|
{
|
||||||
return new Post(
|
return new Post(
|
||||||
|
|
@ -93,6 +143,7 @@ class FakePostRepository implements PostRepository
|
||||||
title: $post->getTitle(),
|
title: $post->getTitle(),
|
||||||
body: $post->getBody(),
|
body: $post->getBody(),
|
||||||
createdAt: $post->getCreatedAt(),
|
createdAt: $post->getCreatedAt(),
|
||||||
|
featureSlot: $post->getFeatureSlot(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue