From a195fa218696ebdb1e35611ff2659f78b00e9204 Mon Sep 17 00:00:00 2001 From: Hugo Posnic Date: Fri, 19 Jul 2024 16:18:59 +0200 Subject: [PATCH 1/3] Add new graph format option and start to better organize code --- README.md | 13 ++--- src/Command/AnalyseCommand.php | 83 +++++++---------------------- src/Enum/GraphFormat.php | 11 ++++ src/Resources/config/services.php | 8 ++- src/Service/RelationshipService.php | 83 +++++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 71 deletions(-) create mode 100644 src/Enum/GraphFormat.php create mode 100644 src/Service/RelationshipService.php diff --git a/README.md b/README.md index b6b31a1..faf6448 100644 --- a/README.md +++ b/README.md @@ -76,12 +76,13 @@ To check deletion relations of two entities in a graph: #### Command-line Arguments -- --entities: Optional. Comma-separated list of entities to analyze. -- -m, --mode: Optional. Analysis mode: all, deletions [default: "all"] -- -o, --output: Optional. Output path for reports generated. -- -g, --graph: Optional. Generate Graphviz graph. -- -V, --version: Optional. Display help for the given command. When no command is given display help for the list command. -- -h, --help: Optional. Display this application version. +- --entities: Optional. Comma-separated list of entities to analyze +- -m, --mode: Optional. Analysis mode (all, deletions) [default: "all"] +- -o, --output: Optional. Output path for reports generated +- -g, --graph: Optional. Generate Graphviz graph +- --graph-format: Optional. Graph image format (png, svg) [default: "png"] +- -V, --version: Optional. Display help for the given command. When no command is given display help for the list command +- -h, --help: Optional. Display this application version ## Limitations diff --git a/src/Command/AnalyseCommand.php b/src/Command/AnalyseCommand.php index 4f8129e..e40d228 100644 --- a/src/Command/AnalyseCommand.php +++ b/src/Command/AnalyseCommand.php @@ -4,11 +4,12 @@ namespace DoctrineRelationsAnalyserBundle\Command; -use Doctrine\ORM\EntityManagerInterface; use DoctrineRelationsAnalyserBundle\Enum\AnalysisMode; use DoctrineRelationsAnalyserBundle\Enum\DeletionType; +use DoctrineRelationsAnalyserBundle\Enum\GraphFormat; use DoctrineRelationsAnalyserBundle\Enum\Level; use DoctrineRelationsAnalyserBundle\Service\HelperService; +use DoctrineRelationsAnalyserBundle\Service\RelationshipService; use Graphp\Graph\Graph; use Graphp\GraphViz\GraphViz; use Symfony\Component\Console\Attribute\AsCommand; @@ -28,7 +29,7 @@ class AnalyseCommand extends Command { public function __construct( - private readonly EntityManagerInterface $entityManager, + private readonly RelationshipService $relationshipService, private readonly Filesystem $filesystem ) { parent::__construct(); @@ -38,9 +39,10 @@ protected function configure(): void { $this ->addOption('entities', null, InputOption::VALUE_REQUIRED, 'Comma-separated list of entities to analyze') - ->addOption('mode', 'm', InputOption::VALUE_REQUIRED, 'Analysis mode: all, deletions', AnalysisMode::ALL->value, array_column(AnalysisMode::cases(), 'name')) + ->addOption('mode', 'm', InputOption::VALUE_REQUIRED, 'Analysis mode', AnalysisMode::ALL->value, array_column(AnalysisMode::cases(), 'name')) ->addOption('output', 'o', InputOption::VALUE_REQUIRED, 'Output path for reports generated') ->addOption('graph', 'g', InputOption::VALUE_NONE, 'Generate Graphviz graph') + ->addOption('graph-format', null, InputOption::VALUE_REQUIRED, 'Graph image format', GraphFormat::PNG->value, array_column(GraphFormat::cases(), 'name')) ; } @@ -56,7 +58,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int try { $modeOption = AnalysisMode::from($input->getOption('mode')); } catch (ValueError $e) { - $io->error('Invalid mode. Allowed values are: all, deletions.'); + $io->error('Invalid mode. Allowed values are: ' . implode(array_column(AnalysisMode::cases(), 'name', ','))); + + return Command::FAILURE; + } + + try { + $graphFormatOption = GraphFormat::from($input->getOption('graph-format')); + } catch (ValueError $e) { + $io->error('Invalid graph format. Allowed values are: ' . implode(array_column(GraphFormat::cases(), 'name', ','))); return Command::FAILURE; } @@ -92,61 +102,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $entitiesToAnalyze = $entitiesOption ? explode(',', $entitiesOption) : []; - $restrictedEntities = !empty($entitiesToAnalyze); - $metaData = $this->entityManager->getMetadataFactory()->getAllMetadata(); - - $relationships = []; - - foreach ($metaData as $meta) { - $className = $meta->getName(); - if ($restrictedEntities && !in_array($className, $entitiesToAnalyze, true)) { - continue; // Skip entities not in the list - } - - $relationships[$className] = []; - foreach ($meta->associationMappings as $fieldName => $association) { - $relationDetails = [ - 'field' => $fieldName, - 'targetEntity' => $association['targetEntity'], - 'type' => $association['type'], - ]; - - if (AnalysisMode::DELETIONS === $modeOption) { - $deletions = []; - - if (isset($association['orphanRemoval']) && $association['orphanRemoval']) { - $deletions[] = [ - 'type' => DeletionType::ORPHAN_REMOVAL, - 'level' => Level::ORM, - 'value' => 'true', - ]; - } - - if (isset($association['cascade']) && in_array('remove', $association['cascade'], true)) { - $deletions[] = [ - 'type' => DeletionType::CASCADE, - 'level' => Level::ORM, - 'value' => '["remove"]', - ]; - } - - if (!empty($association['joinColumns'])) { - if (!empty($association['joinColumns'][0]['onDelete'])) { - $deletions[] = [ - 'type' => DeletionType::ON_DELETE, - 'level' => Level::DATABASE, - 'value' => $association['joinColumns'][0]['onDelete'], - ]; - } - } - - $relationDetails['deletions'] = $deletions; - } - - $relationships[$className][] = $relationDetails; - } - } - + $relationships = $this->relationshipService->fetch($entitiesToAnalyze, $modeOption); if (empty($relationships)) { $io->error('No relationships detected'); @@ -156,7 +112,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->outputRelationships($relationships, $io, $modeOption); if ($graphOption) { - if ($this->generateGraph($relationships, $outputPathOption, $modeOption)) { + if ($this->generateGraph($relationships, $outputPathOption, $modeOption, $graphFormatOption)) { $io->success("Graph image generated in: $outputPathOption"); } else { $io->error("Can't save graph image"); @@ -204,7 +160,7 @@ private function outputRelationships(array $relationships, SymfonyStyle $io, Ana /** * @param array $relationships */ - private function generateGraph(array $relationships, string $outputPath, AnalysisMode $mode): bool + private function generateGraph(array $relationships, string $outputPath, AnalysisMode $mode, GraphFormat $format): bool { $graph = new Graph(); $graph->setAttribute('graphviz.graph.rankdir', 'LR'); @@ -248,12 +204,11 @@ private function generateGraph(array $relationships, string $outputPath, Analysi } } - $format = 'png'; $graphviz = new GraphViz(); - $graphviz->setFormat($format); + $graphviz->setFormat($format->value); $imageData = $graphviz->createImageData($graph); - $fullPath = $outputPath . '/' . $mode->value . '.' . $format; + $fullPath = $outputPath . '/' . $mode->value . '.' . $format->value; try { $this->filesystem->dumpFile($fullPath, $imageData); } catch (IOExceptionInterface) { diff --git a/src/Enum/GraphFormat.php b/src/Enum/GraphFormat.php new file mode 100644 index 0000000..3504b86 --- /dev/null +++ b/src/Enum/GraphFormat.php @@ -0,0 +1,11 @@ +set(HelperService::class); $services - ->set(AnalyseCommand::class) + ->set(RelationshipService::class) ->arg(0, service(EntityManagerInterface::class)) + ; + + $services + ->set(AnalyseCommand::class) + ->arg(0, service(RelationshipService::class)) ->arg(1, service(Filesystem::class)) ->tag('console.command'); }; diff --git a/src/Service/RelationshipService.php b/src/Service/RelationshipService.php new file mode 100644 index 0000000..dec8ce5 --- /dev/null +++ b/src/Service/RelationshipService.php @@ -0,0 +1,83 @@ + $entities + * + * @return array + */ + public function fetch(array $entities, AnalysisMode $mode): array + { + $restrictedEntities = !empty($entities); + $metaData = $this->entityManager->getMetadataFactory()->getAllMetadata(); + + $relationships = []; + + foreach ($metaData as $meta) { + $className = $meta->getName(); + if ($restrictedEntities && !in_array($className, $entities, true)) { + continue; // Skip entities not in the list + } + + $relationships[$className] = []; + foreach ($meta->associationMappings as $fieldName => $association) { + $relationDetails = [ + 'field' => $fieldName, + 'targetEntity' => $association['targetEntity'], + 'type' => $association['type'], + ]; + + if (AnalysisMode::DELETIONS === $mode) { + $deletions = []; + + if (isset($association['orphanRemoval']) && $association['orphanRemoval']) { + $deletions[] = [ + 'type' => DeletionType::ORPHAN_REMOVAL, + 'level' => Level::ORM, + 'value' => 'true', + ]; + } + + if (isset($association['cascade']) && in_array('remove', $association['cascade'], true)) { + $deletions[] = [ + 'type' => DeletionType::CASCADE, + 'level' => Level::ORM, + 'value' => '["remove"]', + ]; + } + + if (!empty($association['joinColumns'])) { + if (!empty($association['joinColumns'][0]['onDelete'])) { + $deletions[] = [ + 'type' => DeletionType::ON_DELETE, + 'level' => Level::DATABASE, + 'value' => $association['joinColumns'][0]['onDelete'], + ]; + } + } + + $relationDetails['deletions'] = $deletions; + } + + $relationships[$className][] = $relationDetails; + } + } + + return $relationships; + } +} From 06de4c7e951c6d49a3c78b219e208715ce5aed82 Mon Sep 17 00:00:00 2001 From: Hugo Posnic Date: Fri, 19 Jul 2024 16:25:46 +0200 Subject: [PATCH 2/3] Fixes for allowed values --- CHANGELOG.md | 8 ++++++++ README.md | 2 +- src/Command/AnalyseCommand.php | 4 ++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f74ff6..191d323 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Add new graph-format option +- Add shortcuts for some parameters +- Add initial Unit Tests and CI + +### Fixed +- Fix output path trimming + ## [0.3.0] - 2024-07-19 ### Changed diff --git a/README.md b/README.md index faf6448..7f2656d 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ return [ To check deletion relations of two entities in a graph: - php bin/console doctrine-relations-analyser:analyse --output data/ --graph --entities="App\\Entity\\User,App\\Entity\\Workspace" --mode="deletions" + php bin/console doctrine-relations-analyser:analyse -o data/ -g --entities="App\\Entity\\User,App\\Entity\\Workspace" -m="deletions" #### Command-line Arguments diff --git a/src/Command/AnalyseCommand.php b/src/Command/AnalyseCommand.php index e40d228..8be8f8f 100644 --- a/src/Command/AnalyseCommand.php +++ b/src/Command/AnalyseCommand.php @@ -58,7 +58,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int try { $modeOption = AnalysisMode::from($input->getOption('mode')); } catch (ValueError $e) { - $io->error('Invalid mode. Allowed values are: ' . implode(array_column(AnalysisMode::cases(), 'name', ','))); + $io->error('Invalid mode. Allowed values are: ' . implode(',', array_column(AnalysisMode::cases(), 'value'))); return Command::FAILURE; } @@ -66,7 +66,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int try { $graphFormatOption = GraphFormat::from($input->getOption('graph-format')); } catch (ValueError $e) { - $io->error('Invalid graph format. Allowed values are: ' . implode(array_column(GraphFormat::cases(), 'name', ','))); + $io->error('Invalid graph format. Allowed values are: ' . implode(',', array_column(GraphFormat::cases(), 'value'))); return Command::FAILURE; } From 4f2bdbc66bd28934d87c67451d876e19fe79383a Mon Sep 17 00:00:00 2001 From: Hugo Posnic Date: Fri, 19 Jul 2024 16:27:36 +0200 Subject: [PATCH 3/3] Doc fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7f2656d..5699efa 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ return [ To check deletion relations of two entities in a graph: - php bin/console doctrine-relations-analyser:analyse -o data/ -g --entities="App\\Entity\\User,App\\Entity\\Workspace" -m="deletions" + php bin/console doctrine-relations-analyser:analyse -o data/ -g --entities="App\\Entity\\User,App\\Entity\\Workspace" -m deletions #### Command-line Arguments