{
"event": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Infrastructure\/Persistence\/DependencyGraphRepository.php",
"content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Infrastructure\\Persistence;\n\n\/\/ @responsibility: Persistenz für Dependency-Graph (code_dependencies Tabelle)\n\nuse Domain\\Repository\\DependencyGraphRepositoryInterface;\nuse PDO;\n\nclass DependencyGraphRepository implements DependencyGraphRepositoryInterface\n{\n private PDO $pdo;\n\n public function __construct(PDO $pdo)\n {\n $this->pdo = $pdo;\n }\n\n \/**\n * Find all files that depend on a given FQCN (reverse lookup for impact analysis).\n *\n * @param string $fqcn The fully qualified class name to search for\n * @param string|null $dependencyType Filter by type (use, extends, implements, trait, constructor)\n * @return array<array{id: int, file_name: string, file_path: string, namespace: string|null, dependency_type: string}>\n *\/\n public function findDependents(string $fqcn, ?string $dependencyType = null): array\n {\n $sql = '\n SELECT ca.id, ca.file_name, ca.file_path, ca.namespace, cd.dependency_type\n FROM code_dependencies cd\n JOIN code_analysis ca ON cd.analysis_id = ca.id\n WHERE cd.target_fqcn = :fqcn\n ';\n $params = ['fqcn' => $fqcn];\n\n if ($dependencyType !== null) {\n $sql .= ' AND cd.dependency_type = :dep_type';\n $params['dep_type'] = $dependencyType;\n }\n\n $sql .= ' ORDER BY ca.file_name';\n\n $stmt = $this->pdo->prepare($sql);\n $stmt->execute($params);\n\n return $stmt->fetchAll(PDO::FETCH_ASSOC);\n }\n\n \/**\n * Get all dependencies for a given analysis ID.\n *\n * @return array<array{dependency_type: string, target_fqcn: string}>\n *\/\n public function getDependencies(int $analysisId): array\n {\n $stmt = $this->pdo->prepare('\n SELECT dependency_type, target_fqcn\n FROM code_dependencies\n WHERE analysis_id = :id\n ORDER BY dependency_type, target_fqcn\n ');\n $stmt->execute(['id' => $analysisId]);\n\n return $stmt->fetchAll(PDO::FETCH_ASSOC);\n }\n\n \/**\n * Get dependency statistics.\n *\n * @return array{total_dependencies: int, by_type: array<string, int>, most_used: array<array{fqcn: string, count: int}>}\n *\/\n public function getDependencyStatistics(): array\n {\n \/\/ Total count\n $stmt = $this->pdo->query('SELECT COUNT(*) FROM code_dependencies');\n $total = (int) $stmt->fetchColumn();\n\n \/\/ Count by type\n $stmt = $this->pdo->query('\n SELECT dependency_type, COUNT(*) as cnt\n FROM code_dependencies\n GROUP BY dependency_type\n ');\n $byType = [];\n foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {\n $byType[$row['dependency_type']] = (int) $row['cnt'];\n }\n\n \/\/ Most used classes (top 20)\n $stmt = $this->pdo->query('\n SELECT target_fqcn as fqcn, COUNT(*) as count\n FROM code_dependencies\n GROUP BY target_fqcn\n ORDER BY count DESC\n LIMIT 20\n ');\n $mostUsed = $stmt->fetchAll(PDO::FETCH_ASSOC);\n\n return [\n 'total_dependencies' => $total,\n 'by_type' => $byType,\n 'most_used' => $mostUsed,\n ];\n }\n\n \/**\n * Get global graph data for entire project.\n *\n * @return array{nodes: array<array<string, mixed>>, links: array<array<string, mixed>>, stats: array<string, int>}\n *\/\n public function getGlobalGraphData(): array\n {\n $nodes = [];\n $links = [];\n $nodeIndex = [];\n\n \/\/ Get all files with classes\n $stmt = $this->pdo->query('\n SELECT id, file_name, namespace, classes, extends_class, extension\n FROM code_analysis\n WHERE classes IS NOT NULL AND classes != \"[]\"\n ORDER BY namespace, file_name\n ');\n\n foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {\n $classes = json_decode($row['classes'], true);\n if (empty($classes)) {\n continue;\n }\n\n $className = $classes[0]['name'];\n $fqcn = $row['namespace'] ? $row['namespace'] . '\\\\' . $className : $className;\n $namespace = $row['namespace'] ?? 'global';\n\n if (!isset($nodeIndex[$fqcn])) {\n $nodeIndex[$fqcn] = count($nodes);\n $nodes[] = [\n 'id' => $fqcn,\n 'label' => $className,\n 'namespace' => $namespace,\n 'fileId' => $row['id'],\n 'type' => $classes[0]['type'] ?? 'class',\n 'extension' => $row['extension'] ?? 'php',\n ];\n }\n }\n\n \/\/ Get all dependency relationships (internal only)\n $stmt = $this->pdo->query('\n SELECT cd.target_fqcn, cd.dependency_type, ca.namespace, ca.classes\n FROM code_dependencies cd\n JOIN code_analysis ca ON cd.analysis_id = ca.id\n WHERE cd.dependency_type IN (\"extends\", \"implements\", \"constructor\", \"use\", \"trait\")\n ');\n\n foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {\n $classes = json_decode($row['classes'], true);\n if (empty($classes)) {\n continue;\n }\n\n $sourceClass = $classes[0]['name'];\n $sourceFqcn = $row['namespace'] ? $row['namespace'] . '\\\\' . $sourceClass : $sourceClass;\n $targetFqcn = $row['target_fqcn'];\n\n \/\/ Only include if both nodes exist in project\n if (isset($nodeIndex[$sourceFqcn]) && isset($nodeIndex[$targetFqcn])) {\n $links[] = [\n 'source' => $nodeIndex[$sourceFqcn],\n 'target' => $nodeIndex[$targetFqcn],\n 'type' => $row['dependency_type'],\n ];\n }\n }\n\n \/\/ Stats\n $stats = [\n 'nodes' => count($nodes),\n 'links' => count($links),\n 'namespaces' => count(array_unique(array_column($nodes, 'namespace'))),\n ];\n\n return [\n 'nodes' => $nodes,\n 'links' => $links,\n 'stats' => $stats,\n ];\n }\n}\n"
}
}
{
"tool_response": {
"type": "create",
"filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Infrastructure\/Persistence\/DependencyGraphRepository.php",
"content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Infrastructure\\Persistence;\n\n\/\/ @responsibility: Persistenz für Dependency-Graph (code_dependencies Tabelle)\n\nuse Domain\\Repository\\DependencyGraphRepositoryInterface;\nuse PDO;\n\nclass DependencyGraphRepository implements DependencyGraphRepositoryInterface\n{\n private PDO $pdo;\n\n public function __construct(PDO $pdo)\n {\n $this->pdo = $pdo;\n }\n\n \/**\n * Find all files that depend on a given FQCN (reverse lookup for impact analysis).\n *\n * @param string $fqcn The fully qualified class name to search for\n * @param string|null $dependencyType Filter by type (use, extends, implements, trait, constructor)\n * @return array<array{id: int, file_name: string, file_path: string, namespace: string|null, dependency_type: string}>\n *\/\n public function findDependents(string $fqcn, ?string $dependencyType = null): array\n {\n $sql = '\n SELECT ca.id, ca.file_name, ca.file_path, ca.namespace, cd.dependency_type\n FROM code_dependencies cd\n JOIN code_analysis ca ON cd.analysis_id = ca.id\n WHERE cd.target_fqcn = :fqcn\n ';\n $params = ['fqcn' => $fqcn];\n\n if ($dependencyType !== null) {\n $sql .= ' AND cd.dependency_type = :dep_type';\n $params['dep_type'] = $dependencyType;\n }\n\n $sql .= ' ORDER BY ca.file_name';\n\n $stmt = $this->pdo->prepare($sql);\n $stmt->execute($params);\n\n return $stmt->fetchAll(PDO::FETCH_ASSOC);\n }\n\n \/**\n * Get all dependencies for a given analysis ID.\n *\n * @return array<array{dependency_type: string, target_fqcn: string}>\n *\/\n public function getDependencies(int $analysisId): array\n {\n $stmt = $this->pdo->prepare('\n SELECT dependency_type, target_fqcn\n FROM code_dependencies\n WHERE analysis_id = :id\n ORDER BY dependency_type, target_fqcn\n ');\n $stmt->execute(['id' => $analysisId]);\n\n return $stmt->fetchAll(PDO::FETCH_ASSOC);\n }\n\n \/**\n * Get dependency statistics.\n *\n * @return array{total_dependencies: int, by_type: array<string, int>, most_used: array<array{fqcn: string, count: int}>}\n *\/\n public function getDependencyStatistics(): array\n {\n \/\/ Total count\n $stmt = $this->pdo->query('SELECT COUNT(*) FROM code_dependencies');\n $total = (int) $stmt->fetchColumn();\n\n \/\/ Count by type\n $stmt = $this->pdo->query('\n SELECT dependency_type, COUNT(*) as cnt\n FROM code_dependencies\n GROUP BY dependency_type\n ');\n $byType = [];\n foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {\n $byType[$row['dependency_type']] = (int) $row['cnt'];\n }\n\n \/\/ Most used classes (top 20)\n $stmt = $this->pdo->query('\n SELECT target_fqcn as fqcn, COUNT(*) as count\n FROM code_dependencies\n GROUP BY target_fqcn\n ORDER BY count DESC\n LIMIT 20\n ');\n $mostUsed = $stmt->fetchAll(PDO::FETCH_ASSOC);\n\n return [\n 'total_dependencies' => $total,\n 'by_type' => $byType,\n 'most_used' => $mostUsed,\n ];\n }\n\n \/**\n * Get global graph data for entire project.\n *\n * @return array{nodes: array<array<string, mixed>>, links: array<array<string, mixed>>, stats: array<string, int>}\n *\/\n public function getGlobalGraphData(): array\n {\n $nodes = [];\n $links = [];\n $nodeIndex = [];\n\n \/\/ Get all files with classes\n $stmt = $this->pdo->query('\n SELECT id, file_name, namespace, classes, extends_class, extension\n FROM code_analysis\n WHERE classes IS NOT NULL AND classes != \"[]\"\n ORDER BY namespace, file_name\n ');\n\n foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {\n $classes = json_decode($row['classes'], true);\n if (empty($classes)) {\n continue;\n }\n\n $className = $classes[0]['name'];\n $fqcn = $row['namespace'] ? $row['namespace'] . '\\\\' . $className : $className;\n $namespace = $row['namespace'] ?? 'global';\n\n if (!isset($nodeIndex[$fqcn])) {\n $nodeIndex[$fqcn] = count($nodes);\n $nodes[] = [\n 'id' => $fqcn,\n 'label' => $className,\n 'namespace' => $namespace,\n 'fileId' => $row['id'],\n 'type' => $classes[0]['type'] ?? 'class',\n 'extension' => $row['extension'] ?? 'php',\n ];\n }\n }\n\n \/\/ Get all dependency relationships (internal only)\n $stmt = $this->pdo->query('\n SELECT cd.target_fqcn, cd.dependency_type, ca.namespace, ca.classes\n FROM code_dependencies cd\n JOIN code_analysis ca ON cd.analysis_id = ca.id\n WHERE cd.dependency_type IN (\"extends\", \"implements\", \"constructor\", \"use\", \"trait\")\n ');\n\n foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {\n $classes = json_decode($row['classes'], true);\n if (empty($classes)) {\n continue;\n }\n\n $sourceClass = $classes[0]['name'];\n $sourceFqcn = $row['namespace'] ? $row['namespace'] . '\\\\' . $sourceClass : $sourceClass;\n $targetFqcn = $row['target_fqcn'];\n\n \/\/ Only include if both nodes exist in project\n if (isset($nodeIndex[$sourceFqcn]) && isset($nodeIndex[$targetFqcn])) {\n $links[] = [\n 'source' => $nodeIndex[$sourceFqcn],\n 'target' => $nodeIndex[$targetFqcn],\n 'type' => $row['dependency_type'],\n ];\n }\n }\n\n \/\/ Stats\n $stats = [\n 'nodes' => count($nodes),\n 'links' => count($links),\n 'namespaces' => count(array_unique(array_column($nodes, 'namespace'))),\n ];\n\n return [\n 'nodes' => $nodes,\n 'links' => $links,\n 'stats' => $stats,\n ];\n }\n}\n",
"structuredPatch": [],
"originalFile": null
}
}