validateParentSet($dto); $position = $this->nextPosition( $dto->set, $dto->parentElement, ); $model = ElementModel::create([ 'set_id' => $dto->set->getId(), 'name' => $dto->name, 'kind' => $dto->kind, 'parent_element_id' => $dto->parentElement?->getId(), 'position' => $position, ]); return new Element( id: $model->id, name: $model->name, kind: $model->kind, set: $dto->set, parentElement: $dto->parentElement, position: $model->position, ); } public function find(int $id): ?Element { $model = ElementModel::find($id); return $model === null ? null : $this->toDomain($model); } public function findTopLevelBySet(Set $set): array { $models = ElementModel::query() ->where('set_id', $set->getId()) ->whereNull('parent_element_id') ->orderBy('position') ->orderBy('id') ->get(); $elements = []; foreach ($models as $model) { $elements[] = $this->toDomainWithRelations( model: $model, set: $set, parentElement: null, ); } return $elements; } public function findByParentElement(Element $parentElement): array { $models = ElementModel::query() ->where('set_id', $parentElement->getSet()->getId()) ->where('parent_element_id', $parentElement->getId()) ->orderBy('position') ->orderBy('id') ->get(); $elements = []; foreach ($models as $model) { $elements[] = $this->toDomainWithRelations( model: $model, set: $parentElement->getSet(), parentElement: $parentElement, ); } return $elements; } /** * @throws DomainException */ private function validateParentSet(CreateElementDto $dto): void { $parentElement = $dto->parentElement; if ($parentElement === null) { return; } if ($parentElement->getSet()->getId() !== $dto->set->getId()) { throw new DomainException( 'parent element must belong to the same set', ); } } private function nextPosition( Set $set, ?Element $parentElement, ): int { $query = ElementModel::query() ->where('set_id', $set->getId()); if ($parentElement === null) { $query->whereNull('parent_element_id'); } else { $query->where('parent_element_id', $parentElement->getId()); } $currentMaximum = $query->max('position'); if ($currentMaximum === null) { return 1; } return (int) $currentMaximum + 1; } private function toDomain(ElementModel $model): Element { $set = $this->findSet($model->set_id); $parentElement = null; if ($model->parent_element_id !== null) { $parentElement = $this->find($model->parent_element_id); if ($parentElement === null) { throw new RuntimeException('element parent not found'); } } return $this->toDomainWithRelations( model: $model, set: $set, parentElement: $parentElement, ); } private function toDomainWithRelations( ElementModel $model, Set $set, ?Element $parentElement, ): Element { return new Element( id: $model->id, name: $model->name, kind: $model->kind, set: $set, parentElement: $parentElement, position: $model->position, ); } private function findSet(int $id): Set { foreach ($this->setRepository->all() as $set) { if ($set->getId() === $id) { return $set; } } throw new RuntimeException('element set not found'); } }