add persisted set ordering

This commit is contained in:
Yisroel Baum 2026-07-31 11:00:41 +03:00
parent fde12ad561
commit dc034bf393
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9
9 changed files with 340 additions and 6 deletions

View file

@ -3,6 +3,7 @@
namespace App\Set;
use DomainException;
use Illuminate\Support\Facades\DB;
class EloquentSetRepository implements SetRepository
{
@ -12,6 +13,7 @@ class EloquentSetRepository implements SetRepository
'name' => $dto->name,
'description' => $dto->description,
'icon_image_url' => $dto->iconImageUrl,
'sort_order' => $this->nextSortOrder(),
]);
return $this->toDomain($model);
@ -55,7 +57,7 @@ class EloquentSetRepository implements SetRepository
public function getAll(): array
{
$models = SetModel::orderBy('id')->get();
$models = SetModel::orderBy('sort_order')->orderBy('id')->get();
$sets = [];
foreach ($models as $model) {
$sets[] = $this->toDomain($model);
@ -64,6 +66,30 @@ class EloquentSetRepository implements SetRepository
return $sets;
}
public function reorder(array $setIds): array
{
DB::transaction(function () use ($setIds): void {
$sortOrder = 1;
foreach ($setIds as $setId) {
SetModel::where('id', $setId)
->update(['sort_order' => $sortOrder]);
$sortOrder++;
}
});
return $this->getAll();
}
private function nextSortOrder(): int
{
$currentMaxSortOrder = SetModel::max('sort_order');
if ($currentMaxSortOrder === null) {
return 1;
}
return (int) $currentMaxSortOrder + 1;
}
private function toDomain(SetModel $model): Set
{
return new Set(

View file

@ -10,12 +10,14 @@ use Illuminate\Database\Eloquent\Model;
* @property string $name
* @property string $description
* @property string $icon_image_url
* @property int $sort_order
*
* @method static Builder<static>|SetModel newModelQuery()
* @method static Builder<static>|SetModel newQuery()
* @method static Builder<static>|SetModel query()
* @method static Builder<static>|SetModel whereId($value)
* @method static Builder<static>|SetModel whereName($value)
* @method static Builder<static>|SetModel whereSortOrder($value)
*
* @mixin \Eloquent
*/
@ -25,5 +27,14 @@ class SetModel extends Model
public $timestamps = false;
protected $fillable = ['name', 'description', 'icon_image_url'];
protected $fillable = [
'name',
'description',
'icon_image_url',
'sort_order',
];
protected $casts = [
'sort_order' => 'integer',
];
}

View file

@ -16,4 +16,10 @@ interface SetRepository
* @return Set[]
*/
public function getAll(): array;
/**
* @param int[] $setIds
* @return Set[]
*/
public function reorder(array $setIds): array;
}

View file

@ -0,0 +1,128 @@
<?php
namespace App\Set\UseCases\ReorderSets;
use App\Exceptions\BadRequestException;
use App\Set\Set;
use App\Set\SetRepository;
class ReorderSets
{
public function __construct(private SetRepository $setRepository)
{
}
/**
* @return Set[]
* @throws BadRequestException
*/
public function execute(ReorderSetsRequest $request): array
{
if ($request->setIds === null) {
throw new BadRequestException('setIds is required');
}
$setIds = $this->validatedSetIds($request->setIds);
$existingSetIds = $this->setIds($this->setRepository->getAll());
$this->validateNoDuplicateIds($setIds);
$this->validateAllIdsAreSets($setIds, $existingSetIds);
$this->validateEverySetWasSubmitted($setIds, $existingSetIds);
return $this->setRepository->reorder($setIds);
}
/**
* @param mixed[] $setIds
* @return int[]
* @throws BadRequestException
*/
private function validatedSetIds(array $setIds): array
{
$validatedSetIds = [];
foreach ($setIds as $setId) {
if (! is_int($setId)) {
throw new BadRequestException(
'setIds must contain integers',
);
}
$validatedSetIds[] = $setId;
}
return $validatedSetIds;
}
/**
* @param int[] $setIds
* @throws BadRequestException
*/
private function validateNoDuplicateIds(array $setIds): void
{
$seenSetIds = [];
foreach ($setIds as $setId) {
if (isset($seenSetIds[$setId])) {
throw new BadRequestException(
'Set order contains duplicate ids',
);
}
$seenSetIds[$setId] = true;
}
}
/**
* @param int[] $setIds
* @param int[] $existingSetIds
* @throws BadRequestException
*/
private function validateAllIdsAreSets(
array $setIds,
array $existingSetIds,
): void {
$existingSetIdsById = [];
foreach ($existingSetIds as $existingSetId) {
$existingSetIdsById[$existingSetId] = true;
}
foreach ($setIds as $setId) {
if (! isset($existingSetIdsById[$setId])) {
throw new BadRequestException(
'Set order contains invalid set',
);
}
}
}
/**
* @param int[] $setIds
* @param int[] $existingSetIds
* @throws BadRequestException
*/
private function validateEverySetWasSubmitted(
array $setIds,
array $existingSetIds,
): void {
if (count($setIds) === count($existingSetIds)) {
return;
}
throw new BadRequestException(
'Set order must include every set',
);
}
/**
* @param Set[] $sets
* @return int[]
*/
private function setIds(array $sets): array
{
$setIds = [];
foreach ($sets as $set) {
$setIds[] = $set->getId();
}
return $setIds;
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace App\Set\UseCases\ReorderSets;
class ReorderSetsRequest
{
/**
* @param mixed[]|null $setIds
*/
public function __construct(public ?array $setIds)
{
}
}