getElement->execute( new GetElementRequest(id: $id) ); } catch (BadRequestException $exception) { return new JsonResponse([ 'error' => $exception->getMessage(), ], 400); } catch (NotFoundException $exception) { return new JsonResponse([ 'error' => $exception->getMessage(), ], 404); } $element = $result->getElement(); return new JsonResponse([ 'childElements' => $this->buildElementSummaryPayloads( $result->getChildElements(), ), 'element' => $this->buildElementPayload($element), 'siblingElements' => $this->buildElementSummaryPayloads( $result->getSiblingElements(), ), ], 200); } public function createChild(Request $request, ?int $parentId): JsonResponse { try { $element = $this->createChildElement->execute( new CreateChildElementRequest( parentElementId: $parentId, title: $this->stringInput($request, 'title'), ) ); } catch (BadRequestException $exception) { return new JsonResponse([ 'error' => $exception->getMessage(), ], 400); } catch (NotFoundException $exception) { return new JsonResponse([ 'error' => $exception->getMessage(), ], 404); } return new JsonResponse([ 'element' => $this->buildElementPayload($element), ], 201); } public function deleteChild(?int $parentId, ?int $childId): JsonResponse { try { $this->deleteChildElement->execute( new DeleteChildElementRequest( parentElementId: $parentId, childElementId: $childId, ) ); } catch (BadRequestException $exception) { return new JsonResponse([ 'error' => $exception->getMessage(), ], 400); } catch (NotFoundException $exception) { return new JsonResponse([ 'error' => $exception->getMessage(), ], 404); } return new JsonResponse(null, 204); } public function reorderChildren( Request $request, ?int $parentId, ): JsonResponse { try { $childElements = $this->reorderChildElements->execute( new ReorderChildElementsRequest( parentElementId: $parentId, childElementIds: $this->intArrayInput( $request, 'childElementIds', ), ) ); } catch (BadRequestException $exception) { return new JsonResponse([ 'error' => $exception->getMessage(), ], 400); } catch (NotFoundException $exception) { return new JsonResponse([ 'error' => $exception->getMessage(), ], 404); } return new JsonResponse([ 'childElements' => $this->buildElementSummaryPayloads( $childElements, ), ], 200); } public function update(Request $request): JsonResponse { $file = $this->uploadedFileInput($request, 'file'); try { $element = $this->updateElement->execute( new UpdateElementRequest( id: $this->intInput($request, 'elementId'), title: $this->stringInput($request, 'title'), description: $this->stringInput($request, 'description'), iconImageUrl: $this->stringInput( $request, 'iconImageUrl', ), richText: $this->stringInput($request, 'richText'), shortPdfPath: $this->stringInput($request, 'shortPdfPath'), longPdfPath: $this->stringInput($request, 'longPdfPath'), youtubeUrl: $this->stringInput($request, 'youtubeUrl'), fileType: $this->stringInput($request, 'fileType'), fileContents: $file['contents'], fileOriginalName: $file['originalName'], fileMimeType: $file['mimeType'], fileSizeBytes: $file['sizeBytes'], ) ); } catch (BadRequestException $exception) { return new JsonResponse([ 'error' => $exception->getMessage(), ], 400); } catch (NotFoundException $exception) { return new JsonResponse([ 'error' => $exception->getMessage(), ], 404); } return new JsonResponse([ 'element' => $this->buildElementPayload($element), ], 200); } private function intInput(Request $request, string $key): ?int { $value = $request->input($key); if (is_int($value)) { return $value; } if (is_string($value) && ctype_digit($value)) { return (int) $value; } return null; } /** * @return int[]|null */ private function intArrayInput(Request $request, string $key): ?array { if (! $request->exists($key)) { return null; } $value = $request->input($key); if (! is_array($value)) { return null; } $integerValues = []; foreach ($value as $item) { if (is_int($item)) { $integerValues[] = $item; continue; } if (is_string($item) && ctype_digit($item)) { $integerValues[] = (int) $item; continue; } return null; } return $integerValues; } private function stringInput(Request $request, string $key): ?string { if (! $request->exists($key)) { return null; } $value = $request->input($key); if ($value === null) { return ''; } if (! is_string($value)) { return null; } return $value; } /** * @return array{ * contents: string|null, * originalName: string|null, * mimeType: string|null, * sizeBytes: int|null * } */ private function uploadedFileInput(Request $request, string $key): array { $emptyFileInput = [ 'contents' => null, 'originalName' => null, 'mimeType' => null, 'sizeBytes' => null, ]; if (! $request->hasFile($key)) { return $emptyFileInput; } $file = $request->file($key); if (! $file instanceof UploadedFile) { return $emptyFileInput; } $realPath = $file->getRealPath(); if ($realPath === false) { return $emptyFileInput; } return [ 'contents' => (string) file_get_contents($realPath), 'originalName' => $file->getClientOriginalName(), 'mimeType' => (string) $file->getMimeType(), 'sizeBytes' => (int) $file->getSize(), ]; } /** * @return array{ * id: int, * title: string, * description: string, * iconImageUrl: string|null, * richText: string, * shortPdfPath: string|null, * longPdfPath: string|null, * youtubeUrl: string|null, * } */ private function buildElementPayload(Element $element): array { return [ 'id' => $element->getId(), 'title' => $element->getTitle(), 'description' => $element->getDescription(), 'iconImageUrl' => $this->storageUrl( $element->getIconImageUrl(), 'element-icons/', ), 'richText' => $element->getRichText(), 'shortPdfPath' => $this->storageUrl( $element->getShortPdfPath(), 'element-pdfs/', ), 'longPdfPath' => $this->storageUrl( $element->getLongPdfPath(), 'element-pdfs/', ), 'youtubeUrl' => $element->getYoutubeUrl(), ]; } /** * @param Element[] $elements * @return array */ private function buildElementSummaryPayloads(array $elements): array { $payloads = []; foreach ($elements as $element) { $payloads[] = [ 'id' => $element->getId(), 'title' => $element->getTitle(), 'description' => $element->getDescription(), ]; } return $payloads; } private function storageUrl(?string $path, string $folderPrefix): ?string { if ($path === null) { return null; } if (str_starts_with($path, $folderPrefix)) { return $this->fileUploader->url($path); } return $path; } }