dokumentRepository = $dokumentRepository; $this->seiteRepository = $seiteRepository; $this->chunkRepository = $chunkRepository; $this->searchService = $searchService; } /** * GET /api/v1/explorer/stats */ public function stats(): void { try { $this->json([ 'success' => true, 'data' => [ 'dokumente' => $this->dokumentRepository->countDokumente(), 'seiten' => $this->seiteRepository->countSeiten(), 'chunks' => $this->chunkRepository->getChunkStats(), 'taxonomy_categories' => $this->chunkRepository->getTopTaxonomyCategories(Constants::DEFAULT_LIMIT), ], ]); } catch (\Exception $e) { error_log(sprintf('[API.Explorer.stats] %s: %s', get_class($e), $e->getMessage())); $this->jsonError($e->getMessage()); } } /** * GET /api/v1/explorer/dokumente */ public function listDokumente(): void { try { $dokumente = $this->dokumentRepository->getDokumenteWithFullStats(); $this->json([ 'success' => true, 'data' => $dokumente, 'meta' => ['total' => count($dokumente)], ]); } catch (\Exception $e) { error_log(sprintf('[API.Explorer.listDokumente] %s: %s', get_class($e), $e->getMessage())); $this->jsonError($e->getMessage()); } } /** * GET /api/v1/explorer/dokumente/{id} */ public function getDokument(string $id): void { try { $dokument = $this->dokumentRepository->getDokumentRoot((int) $id); if ($dokument === null) { $this->json(['success' => false, 'error' => 'Dokument nicht gefunden'], 404); return; } $this->json([ 'success' => true, 'data' => [ 'dokument' => $dokument, 'seiten' => $this->seiteRepository->getSeitenWithStatsForParent((int) $id), 'taxonomy' => $this->seiteRepository->getTaxonomyForDokumentTree((int) $id), ], ]); } catch (\Exception $e) { error_log(sprintf('[API.Explorer.getDokument] %s: %s', get_class($e), $e->getMessage())); $this->jsonError($e->getMessage()); } } /** * GET /api/v1/explorer/seiten */ public function listSeiten(): void { try { $search = $this->getString('search'); $parentId = $this->getString('parent_id'); $limit = $this->getLimit(Constants::DEFAULT_LIMIT, Constants::PERCENT_HALF); $offset = $this->getInt('offset'); $total = $this->seiteRepository->countSeitenFiltered($search, $parentId); $seiten = $this->seiteRepository->getSeitenPaginated($search, $parentId, $limit, $offset); $this->json([ 'success' => true, 'data' => $seiten, 'meta' => [ 'total' => $total, 'limit' => $limit, 'offset' => $offset, ], ]); } catch (\Exception $e) { error_log(sprintf('[API.Explorer.listSeiten] %s: %s', get_class($e), $e->getMessage())); $this->jsonError($e->getMessage()); } } /** * GET /api/v1/explorer/seiten/{id} */ public function getSeite(string $id): void { try { $seite = $this->seiteRepository->getSeiteWithParent((int) $id); if ($seite === null) { $this->json(['success' => false, 'error' => 'Seite nicht gefunden'], 404); return; } $chunks = $this->chunkRepository->getChunksDetailedForDokument((int) $id); // Decode JSON fields in chunks foreach ($chunks as &$c) { $c['entities'] = $this->decodeJsonArray($c['entities'] ?? null); $c['keywords'] = $this->decodeJsonArray($c['keywords'] ?? null); $c['taxonomy_path'] = $this->decodeJsonArray($c['taxonomy_path'] ?? null); } $this->json([ 'success' => true, 'data' => [ 'seite' => $seite, 'chunks' => $chunks, 'unterseiten' => $this->seiteRepository->getUnterseiten((int) $id), ], ]); } catch (\Exception $e) { $this->jsonError($e->getMessage()); } } /** * GET /api/v1/explorer/chunks */ public function listChunks(): void { try { $category = $this->getString('category'); $status = $this->getString('status'); $search = $this->getString('search'); $limit = $this->getLimit(Constants::DEFAULT_LIMIT, Constants::PERCENT_HALF); $offset = $this->getInt('offset'); $total = $this->chunkRepository->countChunksFiltered($category, $status, $search); $chunks = $this->chunkRepository->getChunksFilteredPaginated($category, $status, $search, $limit, $offset); $this->json([ 'success' => true, 'data' => $chunks, 'meta' => [ 'total' => $total, 'limit' => $limit, 'offset' => $offset, ], ]); } catch (\Exception $e) { $this->jsonError($e->getMessage()); } } /** * GET /api/v1/explorer/chunks/{id} */ public function getChunk(string $id): void { try { $chunk = $this->chunkRepository->getChunk((int) $id); if ($chunk === null) { $this->json(['success' => false, 'error' => 'Chunk nicht gefunden'], 404); return; } $this->json([ 'success' => true, 'data' => $chunk, ]); } catch (\Exception $e) { $this->jsonError($e->getMessage()); } } /** * GET /api/v1/explorer/taxonomie */ public function taxonomie(): void { try { $this->json([ 'success' => true, 'data' => [ 'categories' => $this->chunkRepository->getTopTaxonomyCategories(Constants::DEFAULT_LIMIT), 'top_keywords' => $this->chunkRepository->getTopKeywords(50), ], ]); } catch (\Exception $e) { $this->jsonError($e->getMessage()); } } /** * GET /api/v1/explorer/entities */ public function entities(): void { try { $this->json([ 'success' => true, 'data' => $this->chunkRepository->getEntitiesGrouped(Constants::DEFAULT_LIMIT), ]); } catch (\Exception $e) { $this->jsonError($e->getMessage()); } } /** * POST /api/v1/explorer/suche */ public function suche(): void { try { $input = $this->getJsonInput(); $query = trim($input['query'] ?? ''); if ($query === '') { $this->json(['success' => false, 'error' => 'Query ist erforderlich'], 400); return; } $filters = []; if (!empty($input['category'])) { $filters['taxonomy_category'] = $input['category']; } $limit = min((int) ($input['limit'] ?? 10), 50); $results = $this->searchService->search($query, $filters, $limit); $suggestions = $this->searchService->suggestRelatedSearches($results); $this->json([ 'success' => true, 'data' => [ 'query' => $query, 'results' => $results, 'suggestions' => $suggestions, 'count' => count($results), ], ]); } catch (\Exception $e) { $this->jsonError($e->getMessage()); } } }