瀏覽代碼

Merge branch 'V8-6941_complete_entities' into feature/V8-6941-complter-les-entits-avec-les-pro

Olivier Massot 11 月之前
父節點
當前提交
eb8de51b39

+ 9 - 62
src/Commands/Doctrine/SchemaValidateCommand.php

@@ -4,7 +4,6 @@
 
 namespace App\Commands\Doctrine;
 
-use App\Service\Doctrine\SchemaValidation\Difference;
 use App\Service\Doctrine\SchemaValidation\DiffTypeEnum;
 use App\Service\Doctrine\SchemaValidation\SchemaSnippetsMaker;
 use App\Service\Doctrine\SchemaValidation\SchemaValidationService;
@@ -27,7 +26,7 @@ class SchemaValidateCommand extends Command
 {
     public function __construct(
         private readonly SchemaValidationService $schemaValidationService,
-        private readonly SchemaSnippetsMaker $schemaSnippetsMaker
+        private readonly SchemaSnippetsMaker $schemaSnippetsMaker,
     ) {
         parent::__construct();
     }
@@ -43,12 +42,6 @@ class SchemaValidateCommand extends Command
             InputOption::VALUE_OPTIONAL,
             "Filter the type of difference to display (ex: 'MISSING_PROPERTY')."
         );
-        $this->addOption(
-            'csv',
-            null,
-            InputOption::VALUE_NONE,
-            "Print the result in CSV format."
-        );
         $this->addOption(
             'snippets',
             null,
@@ -63,29 +56,19 @@ class SchemaValidateCommand extends Command
 
         $diff = $this->schemaValidationService->validateSchema($filter);
 
-        foreach ($diff as $entity => $value) {
-            if (empty($value)) {
-                continue;
-            }
+        $csv = $this->schemaValidationService->formatToCsv($diff);
 
-            if ($input->getOption('csv')) {
-                $this->printCsv($output, $entity, $value);
-            } else {
-                $this->printVerbose($output, $entity, $value);
-            }
+        if (empty($csv)) {
+            $output->writeln("No difference found");
+            return 0;
         }
 
-        if ($diff) {
-            $count = 0;
-            foreach ($diff as $entity => $value) {
-                $count += (is_array($value) ? count($value) : 1);
-            }
-
-            $output->writeln($count . " differences found");
-        } else {
-            $output->writeln("No difference found");
+        foreach ($csv as $line) {
+            $output->writeln($line);
         }
 
+        $output->writeln(count($csv) . " differences found");
+
         if ($input->getOption('snippets')) {
             $this->schemaSnippetsMaker->makeSnippets($diff);
             $output->writeln("Snippets generated");
@@ -93,40 +76,4 @@ class SchemaValidateCommand extends Command
 
         return 0;
     }
-
-    /**
-     * @param OutputInterface $output
-     * @param string $entity
-     * @param Difference|array<Difference> $differences
-     * @return void
-     */
-    protected function printVerbose(OutputInterface $output, string $entity, Difference | array $differences): void {
-        $output->writeln($entity);
-
-        if (!is_array($differences)) {
-            $output->writeln($differences->getType()->value . " : " . $differences->getMessage());
-        } else {
-            foreach ($differences as $field => $difference) {
-                $output->writeln(" * " . $field . " - " . $difference->getType()->value . " : " . $difference->getMessage());
-            }
-        }
-
-        $output->writeln("\n");
-    }
-
-    /**
-     * @param OutputInterface $output
-     * @param string $entity
-     * @param Difference|array<Difference> $differences
-     * @return void
-     */
-    protected function printCsv(OutputInterface $output, string $entity, Difference | array $differences): void {
-        if (!is_array($differences)) {
-            $output->writeln(implode(';', [$entity, '', $differences->getType()->value]));
-        } else {
-            foreach ($differences as $field => $difference) {
-                $output->writeln(implode(';', [$entity, $field, $difference->getType()->value]));
-            }
-        }
-    }
 }

+ 72 - 0
src/Service/Cron/Job/SchemaValidation.php

@@ -0,0 +1,72 @@
+<?php
+
+namespace App\Service\Cron\Job;
+
+use App\Service\Cron\BaseCronJob;
+use App\Service\Doctrine\SchemaValidation\DiffTypeEnum;
+use App\Service\Doctrine\SchemaValidation\SchemaValidationService;
+use JetBrains\PhpStorm\Pure;
+
+/**
+ * Valide le schéma Doctrine en le comparant à la V1.
+ *
+ * >>> ot:cron run schema-validation --preview
+ * >>> ot:cron run schema-validation
+ */
+class SchemaValidation extends BaseCronJob
+{
+    const VALIDATION_FILTER = DiffTypeEnum::MISSING_RELATION;
+
+    #[Pure]
+    public function __construct(
+        private readonly SchemaValidationService $schemaValidationService,
+    ) {
+        parent::__construct();
+    }
+
+    /**
+     * @return array<string>
+     */
+    protected function getDiffAsCsv(): array
+    {
+        $diff = $this->schemaValidationService->validateSchema(self::VALIDATION_FILTER);
+
+        return $this->schemaValidationService->formatToCsv($diff);
+    }
+
+
+    /**
+     * Validate the doctrine schema (without reporting)
+     */
+    public function preview(): void
+    {
+        $csv = $this->getDiffAsCsv();
+
+        if (empty($csv)) {
+            $this->ui->print('No difference found');
+        } else {
+            foreach ($csv as $line) {
+                $this->ui->print($line);
+            }
+            $this->ui->print("");
+            $this->ui->print("> " . count($csv) . " differences found");
+        }
+    }
+
+    /**
+     * Validate the doctrine schema and report
+     */
+    public function execute(): void
+    {
+        $csv = $this->getDiffAsCsv();
+
+        if (empty($csv)) {
+            $this->logger->info('No difference found');
+        } else {
+            foreach ($csv as $line) {
+                $this->logger->warning($line);
+            }
+            $this->logger->warning(count($csv) . " differences found");
+        }
+    }
+}

+ 0 - 2
src/Service/Doctrine/SchemaValidation/Difference.php

@@ -7,8 +7,6 @@ class Difference
 {
     protected DiffTypeEnum $type;
     protected ?string $message;
-
-
     protected string $entity;
     protected string $property;
 

+ 54 - 16
src/Service/Doctrine/SchemaValidation/SchemaSnippetsMaker.php

@@ -387,6 +387,7 @@ class SchemaSnippetsMaker
         $prop = new Property($name);
         $prop->setProtected();
         $prop->setType($php_type);
+        $prop->setComment("-- Warning : auto-generated property, checkup the attribute options --");
 
         if ($type === 'text') {
             $prop->addAttribute(Column::class, ['length' => 255, 'options' => ['nullable' => true]]);
@@ -466,8 +467,6 @@ class SchemaSnippetsMaker
 
         if (isset($type['cascade'])) {
             $options['cascade'] = $type['cascade'];
-        } else {
-            $options['cascade'] = ['persist'];
         }
 
         if (isset($type['inversedBy'])) {
@@ -500,7 +499,7 @@ class SchemaSnippetsMaker
         }
 
         $options = [];
-        if ($type['joinTable']['name']) {
+        if (isset($type['joinTable']['name']) && $type['joinTable']['name']) {
             $options['name'] = $type['joinTable']['name'];
         }
 
@@ -649,6 +648,41 @@ class SchemaSnippetsMaker
         return null;
     }
 
+    protected function getInverseSetterCallFromCollectionProp(Property $prop, bool $isRemoving = false): ?string
+    {
+        if (
+            $prop->getType() !== Collection::class
+        ) {
+            throw new \LogicException('The property must be a collection');
+        }
+
+        $relationAttr = null;
+        foreach ($prop->getAttributes() as $attribute) {
+            if ($attribute->getName() === OneToMany::class || $attribute->getName() === ManyToMany::class) {
+                $relationAttr = $attribute;
+            }
+        }
+
+        if (!$relationAttr) {
+            throw new \LogicException('Missing relation attribute for collection property ' . $prop->getName());
+        }
+
+        $inversedBy = $relationAttr->getArguments()['inversedBy'] ?? $relationAttr->getArguments()['mappedBy'] ?? null;
+        if (!$inversedBy) {
+            var_dump('Could not determine the inverse prop for collection property ' . $prop->getName());
+            $inversedBy = 'XXXX';
+        }
+
+        $attr = $prop->getAttributes()[0];
+
+        $prefix = ($attr->getName() === OneToMany::class ? 'set' : ($isRemoving ? 'remove' : 'add'));
+
+        return
+            $prefix .
+            ucfirst($inversedBy) .
+            (($prefix === 'set' && $isRemoving) ? '(null)' : '($this)');
+    }
+
     /**
      * Make an 'adder' method for the given property
      *
@@ -667,19 +701,19 @@ class SchemaSnippetsMaker
         $parameter->setType($targetEntityName ?? 'mixed');
         $method->setParameters([$parameter]);
 
+        $inverseSetterCall = $this->getInverseSetterCallFromCollectionProp($prop);
+
         $method->setReturnType('self');
-        $method->setBody(
-            implode(
-                "\n",
-                [
-                    'if (!$this->' . $prop->getName() . '->contains($' . $singularPropName . ')) {',
-                    '    $this->' . $prop->getName() . '[] = $' . $singularPropName . ';',
-                    '}',
-                    '',
-                    'return $this;',
-                ]
-            )
-        );
+        $method->setBody(implode(
+            "\n",
+            [
+                'if (!$this->' . $prop->getName() . '->contains($' . $singularPropName . ')) {',
+                '    $this->' . $prop->getName() . '[] = $' . $singularPropName . ';',
+                '    $' . $singularPropName . '->' . $inverseSetterCall . ';',
+                '}',
+                '',
+                'return $this;',
+            ]));
         return $method;
     }
 
@@ -700,12 +734,16 @@ class SchemaSnippetsMaker
         $parameter->setType($targetEntityName ?? 'mixed');
         $method->setParameters([$parameter]);
 
+        $inverseSetterCall = $this->getInverseSetterCallFromCollectionProp($prop, true);
+
         $method->setReturnType('self');
         $method->setBody(
             implode(
                 "\n",
                 [
-                    '$this->' . $prop->getName() . '->removeElement($' . $singularPropName . ');',
+                    'if ($this->' . $prop->getName() . '->removeElement($' . $singularPropName . ')) {',
+                    '    $' . $singularPropName . '->' . $inverseSetterCall . ';',
+                    '}',
                     '',
                     'return $this;',
                 ]

+ 19 - 0
src/Service/Doctrine/SchemaValidation/SchemaValidationService.php

@@ -292,4 +292,23 @@ class SchemaValidationService
             ClassMetadataInfo::MANY_TO_MANY => 'ManyToMany',
         ][$relation['type']] ?? 'Unknown';
     }
+
+    /**
+     * @param array<Difference|array<Difference>> $diff
+     * @return array<string>
+     */
+    public function formatToCsv(array $diff): array
+    {
+        $csv = [];
+        foreach ($diff as $entity => $differences) {
+            if (!is_array($differences)) {
+                $csv[] = implode(';', [$entity, '', $differences->getType()->value]);
+            } else {
+                foreach ($differences as $field => $difference) {
+                    $csv[] = implode(';', [$entity, $field, $difference->getType()->value]);
+                }
+            }
+        }
+        return $csv;
+    }
 }