add explicit set levels

This commit is contained in:
Yisroel Baum 2026-08-11 22:47:34 +03:00
parent 7e8850b1b2
commit 0d102fcee0
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9
7 changed files with 223 additions and 0 deletions

View file

@ -19,7 +19,9 @@ use App\Email\LaravelEmailFactory;
use App\Element\ElementRepository; use App\Element\ElementRepository;
use App\Element\EloquentElementRepository; use App\Element\EloquentElementRepository;
use App\Set\EloquentSetRepository; use App\Set\EloquentSetRepository;
use App\Set\EloquentSetLevelRepository;
use App\Set\SetRepository; use App\Set\SetRepository;
use App\Set\SetLevelRepository;
use App\Schedule\EloquentScheduleRepository; use App\Schedule\EloquentScheduleRepository;
use App\Schedule\ScheduleRepository; use App\Schedule\ScheduleRepository;
use App\User\EloquentUserRepository; use App\User\EloquentUserRepository;
@ -55,6 +57,10 @@ class AppServiceProvider extends ServiceProvider
SetRepository::class, SetRepository::class,
EloquentSetRepository::class, EloquentSetRepository::class,
); );
$this->app->bind(
SetLevelRepository::class,
EloquentSetLevelRepository::class,
);
$this->app->bind( $this->app->bind(
ElementRepository::class, ElementRepository::class,
EloquentElementRepository::class, EloquentElementRepository::class,

View file

@ -0,0 +1,11 @@
<?php
namespace App\Set;
final readonly class CreateSetLevelDto
{
public function __construct(
public Set $set,
public string $kind,
) {}
}

View file

@ -0,0 +1,88 @@
<?php
namespace App\Set;
use DomainException;
use RuntimeException;
class EloquentSetLevelRepository implements SetLevelRepository
{
public function __construct(
private SetRepository $setRepository,
) {}
public function create(CreateSetLevelDto $dto): SetLevel
{
$kindExists = SetLevelModel::query()
->where('set_id', $dto->set->getId())
->where('kind', $dto->kind)
->exists();
if ($kindExists) {
throw new DomainException(
'level kind must be unique within set',
);
}
$currentMaximum = SetLevelModel::query()
->where('set_id', $dto->set->getId())
->max('depth');
$depth = $currentMaximum === null
? 0
: (int) $currentMaximum + 1;
$model = SetLevelModel::create([
'set_id' => $dto->set->getId(),
'kind' => $dto->kind,
'depth' => $depth,
]);
return new SetLevel(
id: $model->id,
set: $dto->set,
kind: $model->kind,
depth: $model->depth,
);
}
public function find(int $id): ?SetLevel
{
$model = SetLevelModel::find($id);
return $model === null ? null : $this->toDomain($model);
}
public function findBySet(Set $set): array
{
$models = SetLevelModel::query()
->where('set_id', $set->getId())
->orderBy('depth')
->orderBy('id')
->get();
$levels = [];
foreach ($models as $model) {
$levels[] = new SetLevel(
id: $model->id,
set: $set,
kind: $model->kind,
depth: $model->depth,
);
}
return $levels;
}
private function toDomain(SetLevelModel $model): SetLevel
{
$set = $this->setRepository->find($model->set_id);
if ($set === null) {
throw new RuntimeException('set level set not found');
}
return new SetLevel(
id: $model->id,
set: $set,
kind: $model->kind,
depth: $model->depth,
);
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace App\Set;
final readonly class SetLevel
{
public function __construct(
private int $id,
private Set $set,
private string $kind,
private int $depth,
) {}
public function getId(): int
{
return $this->id;
}
public function getSet(): Set
{
return $this->set;
}
public function getKind(): string
{
return $this->kind;
}
public function getDepth(): int
{
return $this->depth;
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace App\Set;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property int $set_id
* @property string $kind
* @property int $depth
*
* @method static Builder<static>|SetLevelModel newModelQuery()
* @method static Builder<static>|SetLevelModel newQuery()
* @method static Builder<static>|SetLevelModel query()
*
* @mixin \Eloquent
*/
#[Fillable(['set_id', 'kind', 'depth'])]
class SetLevelModel extends Model
{
protected $table = 'set_levels';
public $timestamps = false;
/**
* @return array<string, string>
*/
protected function casts(): array
{
return [
'set_id' => 'integer',
'depth' => 'integer',
];
}
}

View file

@ -0,0 +1,20 @@
<?php
namespace App\Set;
use DomainException;
interface SetLevelRepository
{
/**
* @throws DomainException
*/
public function create(CreateSetLevelDto $dto): SetLevel;
public function find(int $id): ?SetLevel;
/**
* @return list<SetLevel>
*/
public function findBySet(Set $set): array;
}

View file

@ -0,0 +1,27 @@
<?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::create('set_levels', function (Blueprint $table): void {
$table->id();
$table->foreignId('set_id')
->constrained('sets')
->cascadeOnDelete();
$table->string('kind');
$table->unsignedInteger('depth');
$table->unique(['set_id', 'kind']);
$table->unique(['set_id', 'depth']);
});
}
public function down(): void
{
Schema::dropIfExists('set_levels');
}
};