From 5e7648ba955898ad42fdd839bbf3d21467b4037f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laurent=20L=C3=A9cluse?= <laurent.lecluse@unicaen.fr>
Date: Tue, 15 Oct 2024 16:31:50 +0200
Subject: [PATCH] =?UTF-8?q?nouvelles=20commandes=20et=20am=C3=A9liorations?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 composer.json                                 | 38 ++++----
 config/module.config.php                      | 12 ++-
 src/Command/UpdateBddCommand.php              | 80 ++++++++++++++++
 src/Command/UpdateBddCommandFactory.php       | 31 +++++++
 src/Command/UpdateDdlCommand.php              | 36 ++++++++
 src/Command/UpdateDdlCommandFactory.php       | 32 +++++++
 src/Ddl/Ddl.php                               |  8 +-
 src/Driver/Postgresql/FunctionManager.php     |  6 ++
 .../Postgresql/MaterializedViewManager.php    |  7 +-
 src/Driver/Postgresql/ProcedureManager.php    |  6 ++
 src/Driver/Postgresql/TableManager.php        | 12 +--
 src/Driver/Postgresql/TriggerManager.php      |  6 ++
 src/Driver/Postgresql/ViewManager.php         |  6 ++
 src/Logger/DefaultLogger.php                  |  2 +-
 src/Logger/LoggerAwareTrait.php               |  7 +-
 src/Logger/LoggerInterface.php                |  2 +-
 src/Logger/SymfonyStyleLogger.php             | 92 +++++++++++++++++++
 src/Migration/MigrationManager.php            |  4 +-
 18 files changed, 353 insertions(+), 34 deletions(-)
 create mode 100644 src/Command/UpdateBddCommand.php
 create mode 100644 src/Command/UpdateBddCommandFactory.php
 create mode 100644 src/Command/UpdateDdlCommand.php
 create mode 100644 src/Command/UpdateDdlCommandFactory.php
 create mode 100644 src/Logger/SymfonyStyleLogger.php

diff --git a/composer.json b/composer.json
index 8f9ad1f..6f1db68 100644
--- a/composer.json
+++ b/composer.json
@@ -1,22 +1,22 @@
 {
-    "name"        : "unicaen/bddadmin",
-    "description" : "Module pour administrer des bases de données Postgresql et Oracle",
-    "repositories": [
-        {
-            "type": "composer",
-            "url" : "https://dev.unicaen.fr/packagist"
-        }
-    ],
-    "require": {
-        "php": ">=8.2"
-    },
-    "autoload"    : {
-        "psr-4"   : {
-            "Unicaen\\BddAdmin\\"    : "src/",
-            "Unicaen\\BddAdmin\\Tests\\"    : "tests/"
-        },
-        "classmap": [
-            "./Module.php"
-        ]
+  "name": "unicaen/bddadmin",
+  "description": "Module pour administrer des bases de données Postgresql et Oracle",
+  "repositories": [
+    {
+      "type": "composer",
+      "url": "https://dev.unicaen.fr/packagist"
     }
+  ],
+  "require": {
+    "php": ">=8.2"
+  },
+  "autoload": {
+    "psr-4": {
+      "Unicaen\\BddAdmin\\": "src/",
+      "Unicaen\\BddAdmin\\Tests\\": "tests/"
+    },
+    "classmap": [
+      "./Module.php"
+    ]
+  }
 }
diff --git a/config/module.config.php b/config/module.config.php
index 7d4bc09..bb7f738 100644
--- a/config/module.config.php
+++ b/config/module.config.php
@@ -54,7 +54,17 @@ return [
 
     'service_manager' => [
         'factories' => [
-            Bdd::class => BddFactory::class,
+            Bdd::class                      => BddFactory::class,
+            Command\UpdateBddCommand::class => Command\UpdateBddCommandFactory::class,
+            Command\UpdateDdlCommand::class => Command\UpdateDdlCommandFactory::class,
+        ],
+    ],
+
+
+    'laminas-cli' => [
+        'commands' => [
+            'bddadmin:update-bdd' => Command\UpdateBddCommand::class,
+            'bddadmin:update-ddl' => Command\UpdateDdlCommand::class,
         ],
     ],
 ];
\ No newline at end of file
diff --git a/src/Command/UpdateBddCommand.php b/src/Command/UpdateBddCommand.php
new file mode 100644
index 0000000..7e75b26
--- /dev/null
+++ b/src/Command/UpdateBddCommand.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Unicaen\BddAdmin\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+use Unicaen\BddAdmin\BddAwareTrait;
+use Unicaen\BddAdmin\Ddl\Ddl;
+
+/**
+ * Description of UpdateBddCommand
+ *
+ * @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
+ */
+class UpdateBddCommand extends Command
+{
+    use BddAwareTrait;
+    
+    protected function execute(InputInterface $input, OutputInterface $output): int
+    {
+        $io = new SymfonyStyle($input, $output);
+        $bdd = $this->getBdd()->setLogger($io);
+
+        $io->title('Mise à jour de la base de données');
+        $io->info('Mise à jour des définitions de la base de données. Merci de patienter ...');
+
+        $ref = $bdd->getRefDdl();
+
+        // Construction de la config de DDL pour filtrer
+        $filters = [];
+        foreach ($ref as $ddlClass => $objects) {
+            foreach ($objects as $object => $objectDdl) {
+                $filters[$ddlClass]['includes'][] = $object;
+            }
+        }
+
+        $tablesDep = [
+            Ddl::INDEX,
+            Ddl::PRIMARY_CONSTRAINT,
+            Ddl::REF_CONSTRAINT,
+            Ddl::UNIQUE_CONSTRAINT,
+        ];
+
+        foreach ($tablesDep as $tableDep) {
+            $objects = $bdd->manager($tableDep)->get();
+            foreach ($objects as $obj) {
+                if (in_array($obj['table'], $filters['table']['includes'])) {
+                    $filters[$tableDep]['includes'][] = $obj['name'];
+                }
+            }
+        }
+
+        // Initialisation et lancement de la pré-migration
+        //$mm = new MigrationManager($oa, $ref, $filters);
+        //$mm->migration('before');
+
+
+        // Mise à jour de la BDD (structures)
+        $bdd->alter($ref, $filters, true);
+
+
+        // Mise à jour des séquences
+        $bdd->majSequences($ref);
+
+
+        // Mise à jour des données
+        //$bdd->logBegin('Contrôle et mise à jour des données');
+        //$bdd->dataUpdater()->run('update');
+        //$bdd->logEnd('Données à jour');
+
+
+
+        // Post-migration
+        //$mm->migration('after');
+
+        return Command::SUCCESS;
+    }
+}
\ No newline at end of file
diff --git a/src/Command/UpdateBddCommandFactory.php b/src/Command/UpdateBddCommandFactory.php
new file mode 100644
index 0000000..4b5ef7d
--- /dev/null
+++ b/src/Command/UpdateBddCommandFactory.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Unicaen\BddAdmin\Command;
+
+use Psr\Container\ContainerInterface;
+use Unicaen\BddAdmin\Bdd;
+
+
+/**
+ * Description of UpdateBddCommandFactory
+ *
+ * @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
+ */
+class UpdateBddCommandFactory
+{
+
+    /**
+     * @param ContainerInterface $container
+     * @param string             $requestedName
+     * @param array|null         $options
+     *
+     * @return UpdateBddCommand
+     */
+    public function __invoke(ContainerInterface $container, $requestedName, $options = null): UpdateBddCommand
+    {
+        $command = new UpdateBddCommand;
+        $command->setBdd($container->get(Bdd::class));
+
+        return $command;
+    }
+}
\ No newline at end of file
diff --git a/src/Command/UpdateDdlCommand.php b/src/Command/UpdateDdlCommand.php
new file mode 100644
index 0000000..9fdeecf
--- /dev/null
+++ b/src/Command/UpdateDdlCommand.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Unicaen\BddAdmin\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+use Unicaen\BddAdmin\BddAwareTrait;
+
+/**
+ * Description of UpdateDdlCommand
+ *
+ * @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
+ */
+class UpdateDdlCommand extends Command
+{
+    use BddAwareTrait;
+
+    protected function execute(InputInterface $input, OutputInterface $output): int
+    {
+        $io = new SymfonyStyle($input, $output);
+        $bdd = $this->getBdd()->setLogger($io);
+
+        $filters = [
+        ];
+
+        $io->title('Génération de la DDL à partir de la base de données');
+
+        $ddl = $bdd->getDdl($filters);
+
+        $ddl->saveToDir();
+
+        return Command::SUCCESS;
+    }
+}
\ No newline at end of file
diff --git a/src/Command/UpdateDdlCommandFactory.php b/src/Command/UpdateDdlCommandFactory.php
new file mode 100644
index 0000000..c6402da
--- /dev/null
+++ b/src/Command/UpdateDdlCommandFactory.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Unicaen\BddAdmin\Command;
+
+use Psr\Container\ContainerInterface;
+use Unicaen\BddAdmin\Bdd;
+
+
+/**
+ * Description of UpdateDdlCommandFactory
+ *
+ * @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
+ */
+class UpdateDdlCommandFactory
+{
+    
+    
+    /**
+     * @param ContainerInterface $container
+     * @param string             $requestedName
+     * @param array|null         $options
+     *
+     * @return UpdateBddCommand
+     */
+    public function __invoke(ContainerInterface $container, $requestedName, $options = null): UpdateDdlCommand
+    {
+        $command = new UpdateDdlCommand;
+        $command->setBdd($container->get(Bdd::class));
+
+        return $command;
+    }
+}
\ No newline at end of file
diff --git a/src/Ddl/Ddl.php b/src/Ddl/Ddl.php
index c35c2b8..582da1e 100644
--- a/src/Ddl/Ddl.php
+++ b/src/Ddl/Ddl.php
@@ -488,7 +488,13 @@ class Ddl implements Iterator, ArrayAccess
                     if ($name != '.' && $name != '..') {
                         $def                      = file_get_contents($dir . $type . '/' . $name);
                         $name                     = substr($name, 0, -4);
-                        $this->data[$type][$name] = ['name' => $name, 'definition' => $def];
+                        if (str_contains($name, '.')) {
+                            [$schema, $vmname] = Util::explodedFullObjectName($name);
+                            $this->data[$type][$name] = ['schema' => $schema, 'name' => $vmname, 'definition' => $def];
+                        }else{
+                            $this->data[$type][$name] = ['name' => $name, 'definition' => $def];
+                        }
+
                     }
                 }
             }
diff --git a/src/Driver/Postgresql/FunctionManager.php b/src/Driver/Postgresql/FunctionManager.php
index 413b862..06161dc 100644
--- a/src/Driver/Postgresql/FunctionManager.php
+++ b/src/Driver/Postgresql/FunctionManager.php
@@ -106,6 +106,12 @@ class FunctionManager extends AbstractManager implements FunctionManagerInteface
 
     public function alter(array $old, array $new): void
     {
+        if (!isset($old['schema'])){
+            $old['schema'] = 'public';
+        }
+        if (!isset($new['schema'])){
+            $new['schema'] = 'public';
+        }
         if ($old != $new) {
             if ($this->sendEvent()->getReturn('no-exec')) return;
 
diff --git a/src/Driver/Postgresql/MaterializedViewManager.php b/src/Driver/Postgresql/MaterializedViewManager.php
index 3bbf40a..cafef3b 100644
--- a/src/Driver/Postgresql/MaterializedViewManager.php
+++ b/src/Driver/Postgresql/MaterializedViewManager.php
@@ -78,7 +78,12 @@ class MaterializedViewManager extends AbstractManager implements MaterializedVie
     public function alter(array $old, array $new): void
     {
         if ($this->sendEvent()->getReturn('no-exec')) return;
-
+        if (!isset($old['schema'])){
+            $old['schema'] = 'public';
+        }
+        if (!isset($new['schema'])){
+            $new['schema'] = 'public';
+        }
         if ($old != $new) {
             $this->drop($old);
             $this->create($new);
diff --git a/src/Driver/Postgresql/ProcedureManager.php b/src/Driver/Postgresql/ProcedureManager.php
index 3405c33..e37d230 100644
--- a/src/Driver/Postgresql/ProcedureManager.php
+++ b/src/Driver/Postgresql/ProcedureManager.php
@@ -79,6 +79,12 @@ class ProcedureManager extends AbstractManager implements ProcedureManagerIntefa
 
     public function alter(array $old, array $new): void
     {
+        if (!isset($old['schema'])){
+            $old['schema'] = 'public';
+        }
+        if (!isset($new['schema'])){
+            $new['schema'] = 'public';
+        }
         if ($old != $new) {
             if ($this->sendEvent()->getReturn('no-exec')) return;
 
diff --git a/src/Driver/Postgresql/TableManager.php b/src/Driver/Postgresql/TableManager.php
index 8b30de0..9208502 100644
--- a/src/Driver/Postgresql/TableManager.php
+++ b/src/Driver/Postgresql/TableManager.php
@@ -80,8 +80,7 @@ class TableManager extends AbstractManager implements TableManagerInterface
         $q = "SELECT
             ns.nspname                 \"schema\",
             t.relname                  \"name\",
-            'N'                        \"temporary\",
-            'NO'                       logging,
+            t.relpersistence           relpersistence,
             c.column_name              cname,
             c.data_type                \"type\",
             c.character_maximum_length length,
@@ -113,8 +112,8 @@ class TableManager extends AbstractManager implements TableManagerInterface
                 $data[$name] = [
                     'schema'      => $paq['schema'],
                     'name'        => $paq['name'],
-                    'temporary'   => $paq['temporary'] == 'YES',
-                    'logging'     => $paq['logging'] == 'YES',
+                    'temporary'   => $paq['relpersistence'] == 't',
+                    'logging'     => $paq['relpersistence'] == 'p',
                     'commentaire' => $paq['commentaire'],
                     'sequence'    => $paq['sequence'],
                     'columns'     => [],
@@ -578,9 +577,8 @@ class TableManager extends AbstractManager implements TableManagerInterface
             if ($this->sendEvent()->getReturn('no-exec')) return;
 
             if ($old['logging'] !== $new['logging']) {
-                throw new BddException("Il n'est pas possible actuellement de supprimer le logging d'une table ou de faire l'opération inverse");
-                //$log = $new['logging'] ? 'LOGGING' : 'NOLOGGING';
-                //$this->addQuery("ALTER TABLE $name $log", 'Modification du logging de la table ' . $name);
+                $sql = "ALTER TABLE $name SET ".($new['logging'] ? 'LOGGED' : 'UNLOGGED');
+                $this->addQuery($sql, 'Modification du logging de la table ' . $name);
             }
 
             $newCols = array_diff(array_keys($new['columns']), array_keys($old['columns']));
diff --git a/src/Driver/Postgresql/TriggerManager.php b/src/Driver/Postgresql/TriggerManager.php
index e420783..20a681c 100644
--- a/src/Driver/Postgresql/TriggerManager.php
+++ b/src/Driver/Postgresql/TriggerManager.php
@@ -153,6 +153,12 @@ class TriggerManager extends AbstractManager implements TriggerManagerInterface
 
     public function alter(array $old, array $new): void
     {
+        if (!isset($old['schema'])){
+            $old['schema'] = 'public';
+        }
+        if (!isset($new['schema'])){
+            $new['schema'] = 'public';
+        }
         if ($old != $new) {
             if ($this->sendEvent()->getReturn('no-exec')) return;
 
diff --git a/src/Driver/Postgresql/ViewManager.php b/src/Driver/Postgresql/ViewManager.php
index a69159d..d8917d8 100644
--- a/src/Driver/Postgresql/ViewManager.php
+++ b/src/Driver/Postgresql/ViewManager.php
@@ -75,6 +75,12 @@ class ViewManager extends AbstractManager implements ViewManagerInterface
 
     public function alter(array $old, array $new): void
     {
+        if (!isset($old['schema'])){
+            $old['schema'] = 'public';
+        }
+        if (!isset($new['schema'])){
+            $new['schema'] = 'public';
+        }
         if ($old != $new) {
             if ($this->sendEvent()->getReturn('no-exec')) return;
 
diff --git a/src/Logger/DefaultLogger.php b/src/Logger/DefaultLogger.php
index cb82ff1..4846a49 100644
--- a/src/Logger/DefaultLogger.php
+++ b/src/Logger/DefaultLogger.php
@@ -54,7 +54,7 @@ class DefaultLogger implements LoggerInterface
 
 
 
-    public function error($e)
+    public function error(Throwable|string $e)
     {
         if ($e instanceof \Throwable) {
             $e = $e->getMessage();
diff --git a/src/Logger/LoggerAwareTrait.php b/src/Logger/LoggerAwareTrait.php
index 6c33541..399a64e 100644
--- a/src/Logger/LoggerAwareTrait.php
+++ b/src/Logger/LoggerAwareTrait.php
@@ -2,6 +2,8 @@
 
 namespace Unicaen\BddAdmin\Logger;
 
+use Symfony\Component\Console\Style\SymfonyStyle;
+
 trait LoggerAwareTrait
 {
 
@@ -24,8 +26,11 @@ trait LoggerAwareTrait
      *
      * @return self
      */
-    public function setLogger(?LoggerInterface $logger): self
+    public function setLogger(null|LoggerInterface|SymfonyStyle $logger): self
     {
+        if ($logger instanceof SymfonyStyle){
+            $logger = new SymfonyStyleLogger($logger);
+        }
         $this->logger = $logger;
 
         return $this;
diff --git a/src/Logger/LoggerInterface.php b/src/Logger/LoggerInterface.php
index 51b7b43..0d0aa4b 100644
--- a/src/Logger/LoggerInterface.php
+++ b/src/Logger/LoggerInterface.php
@@ -4,7 +4,7 @@ namespace Unicaen\BddAdmin\Logger;
 
 interface LoggerInterface
 {
-    public function error($e);
+    public function error(Throwable|string $e);
 
 
 
diff --git a/src/Logger/SymfonyStyleLogger.php b/src/Logger/SymfonyStyleLogger.php
new file mode 100644
index 0000000..15307cf
--- /dev/null
+++ b/src/Logger/SymfonyStyleLogger.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Unicaen\BddAdmin\Logger;
+
+
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+class SymfonyStyleLogger implements LoggerInterface
+{
+    protected ?string $lastMessage = null;
+
+    protected bool $lastRewrite = false;
+    
+    protected SymfonyStyle $symfonyStyle;
+
+
+
+    public function __construct(SymfonyStyle $symfonyStyle)
+    {
+        $this->symfonyStyle = $symfonyStyle;
+    }
+
+
+
+    public function print($text, $color = null, $bgColor = null)
+    {
+        $this->symfonyStyle->write($text);
+    }
+
+
+
+    public function println($text, $color = null, $bgColor = null)
+    {
+        $this->symfonyStyle->writeln($text);
+    }
+
+
+
+    public function msg($message, bool $rewrite = false)
+    {
+        if ($rewrite) {
+            if ($this->lastMessage) {
+                $m = $message . str_pad('', strlen($this->lastMessage) - strlen($message) + 2) . "\r";
+            } else {
+                $m = $message . "\r";
+            }
+            $this->print($m);
+        } else {
+            $this->println($message);
+        }
+        $this->lastMessage = $message;
+        $this->lastRewrite = $rewrite;
+    }
+
+
+
+    public function error(Throwable|string $e)
+    {
+        if ($e instanceof \Throwable) {
+            $e = $e->getMessage();
+        }
+        if ($this->lastRewrite) $this->println('');
+        $this->symfonyStyle->error($e);
+    }
+
+
+
+    public function begin(string $title)
+    {
+        if ($this->lastMessage) {
+            $title .= str_pad('', strlen($this->lastMessage) - strlen($title) + 2);
+        }
+        $this->lastRewrite = false;
+        $this->lastMessage = null;
+        $this->symfonyStyle->title($title);
+    }
+
+
+
+    public function end(?string $msg = null): void
+    {
+        if ($this->lastMessage && $this->lastRewrite) {
+            $msg .= str_pad('', strlen($this->lastMessage) - strlen($msg ?? '') + 2);
+        }
+        if (trim($msg)) {
+            $this->symfonyStyle->comment($msg);
+        } else {
+            $this->println('');
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/Migration/MigrationManager.php b/src/Migration/MigrationManager.php
index 9edc1b0..f0b5569 100644
--- a/src/Migration/MigrationManager.php
+++ b/src/Migration/MigrationManager.php
@@ -11,7 +11,7 @@ class MigrationManager
 {
     use OptionsTrait;
 
-    const string OPTION_DIR = 'dir';
+    const OPTION_DIR = 'dir';
 
     protected Ddl $ref;
 
@@ -174,7 +174,7 @@ class MigrationManager
 
     protected function getMigrationDir(): ?string
     {
-        $migrationDir = $this->getBdd()->getOption(Bdd::OPTION_MIGRATION_DIR);
+        $migrationDir = $this->getBdd()->getOption(self::OPTION_DIR);
 
         return $migrationDir;
     }
-- 
GitLab