diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..0e61a8f9c152c209311f0ae33e8b5f438e5327a2 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,14 @@ +image: registre.unicaen.fr:5000/unicaen-dev-php7.3-apache + +stages: +- publish + +cache: + key: ${CI_COMMIT_REF_SLUG} + paths: + - vendor/ + +update-satis: + stage: publish + script: + - curl https://gest.unicaen.fr/packagist/update diff --git a/Module.php b/Module.php index 8d902d7672106a33aff26dda14c83888e78ed54a..213858199b1be65c68d2ba77a62344ec19e9cb77 100644 --- a/Module.php +++ b/Module.php @@ -39,16 +39,16 @@ class Module implements ConsoleUsageProviderInterface public function getConsoleUsage(AdapterInterface $console) { return [ - 'run import --name=' => 'Lance un import par son nom (name)', - ['--name', "Nom unique de l'import (clé 'name')"], + 'run import [--name=<name>]' => 'Lance un import par son nom', + ['<name>', "Nom unique de l'import (clé de config 'name')"], - 'run import --all' => 'Lance tous les imports', + 'run import' => 'Lance tous les imports', [], - 'run synchro --name=' => 'Lance une synchronisation par son nom (name)', - ['--name', "Nom unique de la synchronisation (clé 'name')"], + 'run synchro [--name=<name>]' => 'Lance une synchronisation par son nom', + ['<name>', "Nom unique de la synchronisation (clé de config 'name')"], - 'run synchro --all' => 'Lance toutes les synchronisations', + 'run synchro' => 'Lance toutes les synchronisations', [], ]; } diff --git a/config/module.config.php b/config/module.config.php index 171f74264f04fbf17216405de2eac0766402a686..2a6d1d7212106eebe735013378d2855825c9ba00 100644 --- a/config/module.config.php +++ b/config/module.config.php @@ -16,6 +16,11 @@ use UnicaenDbImport\Entity\Db\Service\ImportObserv\ImportObservService; use UnicaenDbImport\Entity\Db\Service\ImportObserv\ImportObservServiceFactory; use UnicaenDbImport\Entity\Db\Service\ImportObservResult\ImportObservResultService; use UnicaenDbImport\Entity\Db\Service\ImportObservResult\ImportObservResultServiceFactory; +use UnicaenDbImport\Entity\Db\Service\Source\SourceService; +use UnicaenDbImport\Entity\Db\Service\Source\SourceServiceFactory; +use UnicaenDbImport\Entity\Db\Source; +use UnicaenDbImport\ORM\Event\Listeners\SourceListener; +use UnicaenDbImport\ORM\Event\Listeners\SourceListenerFactory; use UnicaenDbImport\Service\ApiService; use UnicaenDbImport\Service\ApiServiceFactory; use UnicaenDbImport\Service\CodeGeneratorPluginManager; @@ -31,6 +36,9 @@ use UnicaenDbImport\Service\SynchroServiceFactory; return [ 'import' => [ + 'source_entity_class' => Source::class, + 'default_source_code' => 'app', + 'import_observ_entity_class' => ImportObserv::class, 'import_observ_result_entity_class' => ImportObservResult::class, @@ -41,6 +49,9 @@ return [ 'synchros' => [], ], + 'UnicaenDbImport.Entity.Db.Source.dcm.xml', + 'UnicaenDbImport.Entity.Db.Source.dcm.xml', + 'doctrine' => [ 'driver' => [ 'orm_default' => [ @@ -57,6 +68,13 @@ return [ ], ], ], + 'eventmanager' => [ + 'orm_default' => [ + 'subscribers' => [ + SourceListener::class, + ], + ], + ], ], 'console' => [ @@ -65,7 +83,7 @@ return [ 'execute_imports' => [ 'type' => 'simple', 'options' => [ - 'route' => 'run import [--all|-a] [--name=]', + 'route' => 'run import [--name=]', 'defaults' => [ 'controller' => 'UnicaenDbImport\Controller\Console', 'action' => 'runImport', @@ -75,7 +93,7 @@ return [ 'execute_synchro' => [ 'type' => 'simple', 'options' => [ - 'route' => 'run synchro [--all|-a] [--name=]', + 'route' => 'run synchro [--name=]', 'defaults' => [ 'controller' => 'UnicaenDbImport\Controller\Console', 'action' => 'runSynchro', @@ -95,16 +113,21 @@ return [ 'service_manager' => [ 'factories' => [ Config::class => ConfigFactory::class, + ImportService::class => ImportServiceFactory::class, SynchroService::class => SynchroServiceFactory::class, FacadeService::class => FacadeServiceFactory::class, DatabaseService::class => DatabaseServiceFactory::class, ApiService::class => ApiServiceFactory::class, + CodeGeneratorPluginManager::class => CodeGeneratorPluginManagerFactory::class, + SourceService::class => SourceServiceFactory::class, ImportLogService::class => ImportLogServiceFactory::class, ImportObservService::class => ImportObservServiceFactory::class, ImportObservResultService::class => ImportObservResultServiceFactory::class, + + SourceListener::class => SourceListenerFactory::class, ], 'abstract_factories' => [ HelperAbstractFactory::class, diff --git a/config/unicaen-db-import.global.php.dist b/config/unicaen-db-import.global.php.dist index c95a071b3c14081a279dfa65574d678279c9f7dc..eaf88a7a6fcde1d907e02cae08933d07fce70e4b 100644 --- a/config/unicaen-db-import.global.php.dist +++ b/config/unicaen-db-import.global.php.dist @@ -5,12 +5,24 @@ namespace Application; +use \UnicaenDbImport\Entity\Db\Source; + return [ 'import' => [ 'connections' => [ // Cf. `./unicaen-db-import.local.php.dist` ], + // + // Classe de l'entité Doctrine représentant une "Source". + // + 'source_entity_class' => Source::class, + + // + // Code de la Source par défaut (injectée dans les entités implémentant SoucreAwareInterface). + // + 'default_source_code' => 'app', + // // Alias éventuels des noms de colonnes d'historique. // @@ -105,6 +117,7 @@ return [ // - 'table' : nom de la table source contenant les données à importer // - 'select' : select SQL de mise en forme des données source à importer (NB: antinomique avec 'table') // - 'source_code_column' : nom de la colonne dans la table/vue source contenant l'identifiant unique + // - 'where' : filtre SQL éventuel à appliquer aux données sources. // 'source' => [ 'name' => 'TABLE PAYS DE APOGÉE', @@ -112,6 +125,7 @@ return [ //'table' => 'PAYS', // Équivalent de SELECT * FROM PAYS; 'connection' => 'apogee', 'source_code_column' => 'COD_PAY', + //'where' => "TEM_OUV_DRT_SSO_PAY = 'N'", ], // // Configuration de la destination des données importées : @@ -157,12 +171,14 @@ return [ // - 'select' : select SQL de mise en forme des données source à importer (NB: antinomique avec 'table') // - 'connection' : identifiant de la connexion à la bdd source (cf. clé 'connections' plus haut) // - 'source_code_column' : nom de la colonne dans la table/vue source contenant l'identifiant unique + // - 'where' : filtre éventuel à apliquer aux données sources // 'source' => [ 'name' => 'TABLE MES_PAYS_TEMPORAIRES', 'table' => 'MES_PAYS_TEMPORAIRES', 'connection' => 'default', 'source_code_column' => 'COD_PAY', + //'where' => "TEM_OUV_DRT_SSO_PAY = 'N'", ], // @@ -171,12 +187,15 @@ return [ // - 'connection' : identifiant de la connexion à la bdd destination (cf. clé 'connections' plus haut) // - 'table' : nom de la table destination vers laquelle les données sont synchronisées // - 'source_code_column' : nom de la colonne dans la table destination contenant l'identifiant unique + // - 'where' : filtre SQL éventuel permettant de restreindre les données destinations concernées + // (les noms de colonnes doivent être préfixés par l'alias de la table destination 'd') // 'destination' => [ 'name' => 'TABLE PAYS', 'table' => 'PAYS', 'connection' => 'default', 'source_code_column' => 'COD_PAY', + //'where' => "d.TEM_OUV_DRT_SSO_PAY = 'N'", // // Forçage éventuel du nom de la table où seront inscrits les logs (créée automatiquement si nécessaire). // En l'absence de ce forçage, le nom de la table sera 'import_log'. diff --git a/src/UnicaenDbImport/CodeGenerator/CodeGenerator.php b/src/UnicaenDbImport/CodeGenerator/CodeGenerator.php index 56b52d8208291f6b5c829532bac718c07d743e32..719fad04e2f776468e83455aadbae7be06738344 100644 --- a/src/UnicaenDbImport/CodeGenerator/CodeGenerator.php +++ b/src/UnicaenDbImport/CodeGenerator/CodeGenerator.php @@ -198,12 +198,17 @@ abstract class CodeGenerator implements CodeGeneratorInterface public function generateSQLForSelectFromSource(SourceInterface $source) { if ($source->getSelect()) { - return $source->getSelect(); + $query = $source->getSelect(); + } else { + $sourceTable = $source->getTable(); + $query = $this->tableHelper->generateSQLForSelectFromTable($sourceTable); } - $sourceTable = $source->getTable(); + if ($where = $source->getWhere()) { + $query = sprintf('SELECT * FROM (%s) tmp WHERE (%s)', $query, $where); + } - return $this->tableHelper->generateSQLForSelectFromTable($sourceTable); + return $query; } /** @@ -313,6 +318,7 @@ abstract class CodeGenerator implements CodeGeneratorInterface { $destinationTable = $destination->getTable(); $idColumnSequence = $destination->getIdColumnSequence(); + $where = $destination->getWhere(); $sourceCodeColumn = $source->getSourceCodeColumn(); $columns = $source->getColumns(); @@ -331,19 +337,22 @@ abstract class CodeGenerator implements CodeGeneratorInterface $sql = $this->tableHelper->generateSQLForUpdateOperationInDestinationTable( $destinationTable, $sourceCodeColumn, - $columns); + $columns, + $where); break; case Operation::OPERATION_DELETE; $sql = $this->tableHelper->generateSQLForDeleteOperationInDestinationTable( $destinationTable, $sourceCodeColumn, - $columns); + $columns, + $where); break; case Operation::OPERATION_UNDELETE; $sql = $this->tableHelper->generateSQLForUndeleteOperationInDestinationTable( $destinationTable, $sourceCodeColumn, - $columns); + $columns, + $where); break; default: throw new RuntimeException("Opération inattendue"); @@ -386,9 +395,10 @@ EOS; /** * @param array $importObservRow + * @param string $where * @return string */ - protected function generateSQLForSelectingDiffViewFromImportObservRow(array $importObservRow) + protected function generateSQLForSelectingDiffViewFromImportObservRow(array $importObservRow, $where = null) { $id = $importObservRow['id']; $tableName = $importObservRow['tableName']; @@ -408,8 +418,17 @@ EOS; // Ex: "v.COLONNE = 'VALEUR'" ou "v.COLONNE IS NULL". $toValueCond = 'v.' . $sColName . ($toValue === null ? ' is null' : (" = '" . $toValue . "'")); - // Eventuel filtre. - $andWhere = $filter ? 'and ' . $filter : ''; + // Eventuels filtres : celui de la table IMPORT_OBSERV + celui spécifié dans la config de la destination. + $wheres = []; + if ($filter) $wheres[] = sprintf('(%s)', $filter); + if ($where) $wheres[] = sprintf('(%s)', str_ireplace('d.', 't.', $where)); + $andWhere = $wheres ? 'and ' . implode(' and ', $wheres) : ''; + + // Colonne 'detail'. Ex: "coalesce(t.RESULTAT::varchar, '') || '>' || coalesce(v.RESULTAT::varchar, '')" + $detail = + 'coalesce(' . $this->tableHelper->generateSQLForCastToString("t.$columnName") . ", '')" . + " || '>' || " . + 'coalesce(' . $this->tableHelper->generateSQLForCastToString("v.$sColName") . ", '')"; // Construction de la requête recherchant dans la vue V_DIFF_X les lignes correspondant à : // une prise de valeur particulière spécifiée par IMPORT_OBSERV.TO_VALUE, @@ -417,7 +436,7 @@ EOS; // dans la table spécifiée par IMPORT_OBSERV.TABLE_NAME. return <<<EOS select $id import_observ_id, v.source_code, -t.$columnName || '>' || v.$sColName detail +$detail detail from V_DIFF_$tableName v join $tableName t on t.source_code = v.source_code where $uColName = 1 and $toValueCond @@ -426,25 +445,29 @@ EOS; } /** + * @param DestinationInterface $destination * @param array $importObservRows * @return string */ - public function generateSQLForInsertionIntoImportObservResult(array $importObservRows) + public function generateSQLForInsertionIntoImportObservResult(DestinationInterface $destination, array $importObservRows) { + $where = $destination->getWhere(); $now = $this->platform->getNowExpression(); $selects = []; foreach ($importObservRows as $importObservRow) { - $selects[] = $this->generateSQLForSelectingDiffViewFromImportObservRow($importObservRow); + $selects[] = $this->generateSQLForSelectingDiffViewFromImportObservRow($importObservRow, $where); } $selects = implode(PHP_EOL . 'UNION ALL' . PHP_EOL, $selects); + $nextval = $this->tableHelper->generateSQLForSequenceNextVal('import_observ_result_id_seq'); + return <<<EOS insert into import_observ_result (id, date_creation, import_observ_id, source_code, resultat) with tmp as ( $selects ) -select import_observ_result_id_seq.nextval, $now, import_observ_id, source_code, detail +select $nextval, $now, import_observ_id, source_code, detail from tmp EOS; } diff --git a/src/UnicaenDbImport/CodeGenerator/Helper/TableHelper.php b/src/UnicaenDbImport/CodeGenerator/Helper/TableHelper.php index e2a89e64fb9b60e2b9928495950d696604627dfc..7948f7e0c000065b1d67c202b3634de02f21fab0 100644 --- a/src/UnicaenDbImport/CodeGenerator/Helper/TableHelper.php +++ b/src/UnicaenDbImport/CodeGenerator/Helper/TableHelper.php @@ -512,6 +512,12 @@ EOT; return $tableName . '_ID_SEQ'; } + /** + * @param string $columnName + * @return string + */ + abstract public function generateSQLForCastToString(string $columnName); + /** * @param string $sequenceName * @return string @@ -522,23 +528,26 @@ EOT; * @param string $destinationTable * @param string $sourceCodeColumn * @param array $columns + * @param string $where * @return string */ - abstract public function generateSQLForUpdateOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns); + abstract public function generateSQLForUpdateOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns, $where = null); /** * @param string $destinationTable * @param string $sourceCodeColumn * @param array $columns + * @param string $where * @return string */ - abstract public function generateSQLForUndeleteOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns); + abstract public function generateSQLForUndeleteOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns, $where = null); /** * @param string $destinationTable * @param string $sourceCodeColumn * @param array $columns + * @param string $where * @return string */ - abstract public function generateSQLForDeleteOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns); + abstract public function generateSQLForDeleteOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns, $where = null); } \ No newline at end of file diff --git a/src/UnicaenDbImport/CodeGenerator/Oracle/Helper/TableHelper.php b/src/UnicaenDbImport/CodeGenerator/Oracle/Helper/TableHelper.php index dbb8e6a7b13873a60ab4ae326fd49bbdb08befa7..3b88a50ba61060033512ee1f6438f89bbb389797 100644 --- a/src/UnicaenDbImport/CodeGenerator/Oracle/Helper/TableHelper.php +++ b/src/UnicaenDbImport/CodeGenerator/Oracle/Helper/TableHelper.php @@ -80,25 +80,25 @@ class TableHelper extends \UnicaenDbImport\CodeGenerator\Helper\TableHelper /** * {@inheritDoc} */ - public function generateSQLForUpdateOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns) + public function generateSQLForUpdateOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns, $where = null) { - return $this->generateSQLUpdate(Operation::OPERATION_UPDATE, $destinationTable, $sourceCodeColumn, $columns); + return $this->generateSQLUpdate(Operation::OPERATION_UPDATE, $destinationTable, $sourceCodeColumn, $columns, $where); } /** * {@inheritDoc} */ - public function generateSQLForUndeleteOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns) + public function generateSQLForUndeleteOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns, $where = null) { - return $this->generateSQLUpdate(Operation::OPERATION_UNDELETE, $destinationTable, $sourceCodeColumn, $columns); + return $this->generateSQLUpdate(Operation::OPERATION_UNDELETE, $destinationTable, $sourceCodeColumn, $columns, $where); } /** * {@inheritDoc} */ - public function generateSQLForDeleteOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns) + public function generateSQLForDeleteOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns, $where = null) { - return $this->generateSQLUpdate(Operation::OPERATION_DELETE, $destinationTable, $sourceCodeColumn, $columns); + return $this->generateSQLUpdate(Operation::OPERATION_DELETE, $destinationTable, $sourceCodeColumn, $columns, $where); } /** @@ -106,9 +106,10 @@ class TableHelper extends \UnicaenDbImport\CodeGenerator\Helper\TableHelper * @param string $destinationTable * @param string $sourceCodeColumn * @param array $columns + * @param string $where * @return string */ - protected function generateSQLUpdate($operation, $destinationTable, $sourceCodeColumn, array $columns) + protected function generateSQLUpdate($operation, $destinationTable, $sourceCodeColumn, array $columns, $where = null) { $diffViewName = $this->generateDiffViewName($destinationTable); $setters = $this->generateSQLUpdateSetters($operation, $columns); @@ -132,4 +133,12 @@ class TableHelper extends \UnicaenDbImport\CodeGenerator\Helper\TableHelper { return $sequenceName . '.nextval'; } + + /** + * @inheritDoc + */ + public function generateSQLForCastToString(string $columnName) + { + return $columnName . "||''"; + } } \ No newline at end of file diff --git a/src/UnicaenDbImport/CodeGenerator/PostgreSQL/Helper/TableHelper.php b/src/UnicaenDbImport/CodeGenerator/PostgreSQL/Helper/TableHelper.php index dbe18c0290e36b5e3f1ee7652d3ae565d5e83389..28cb3b9b1ddc1b5c8ad3d9b2d3c575a2de063c22 100644 --- a/src/UnicaenDbImport/CodeGenerator/PostgreSQL/Helper/TableHelper.php +++ b/src/UnicaenDbImport/CodeGenerator/PostgreSQL/Helper/TableHelper.php @@ -35,25 +35,25 @@ class TableHelper extends \UnicaenDbImport\CodeGenerator\Helper\TableHelper /** * {@inheritDoc} */ - public function generateSQLForUpdateOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns) + public function generateSQLForUpdateOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns, $where = null) { - return $this->generateSQLUpdate(Operation::OPERATION_UPDATE, $destinationTable, $sourceCodeColumn, $columns); + return $this->generateSQLUpdate(Operation::OPERATION_UPDATE, $destinationTable, $sourceCodeColumn, $columns, $where); } /** * {@inheritDoc} */ - public function generateSQLForUndeleteOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns) + public function generateSQLForUndeleteOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns, $where = null) { - return $this->generateSQLUpdate(Operation::OPERATION_UNDELETE, $destinationTable, $sourceCodeColumn, $columns); + return $this->generateSQLUpdate(Operation::OPERATION_UNDELETE, $destinationTable, $sourceCodeColumn, $columns, $where); } /** * {@inheritDoc} */ - public function generateSQLForDeleteOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns) + public function generateSQLForDeleteOperationInDestinationTable($destinationTable, $sourceCodeColumn, array $columns, $where = null) { - return $this->generateSQLUpdate(Operation::OPERATION_DELETE, $destinationTable, $sourceCodeColumn, $columns); + return $this->generateSQLUpdate(Operation::OPERATION_DELETE, $destinationTable, $sourceCodeColumn, $columns, $where); } /** @@ -61,20 +61,24 @@ class TableHelper extends \UnicaenDbImport\CodeGenerator\Helper\TableHelper * @param string $destinationTable * @param string $sourceCodeColumn * @param array $columns + * @param string $where * @return string */ - protected function generateSQLUpdate($operation, $destinationTable, $sourceCodeColumn, array $columns) + protected function generateSQLUpdate($operation, $destinationTable, $sourceCodeColumn, array $columns, $where = null) { $diffViewName = $this->generateDiffViewName($destinationTable); $setters = $this->generateSQLUpdateSetters($operation, $columns); - $where = "diff.operation = '$operation' AND d.$sourceCodeColumn = diff.$sourceCodeColumn"; + $wheres = "diff.operation = '$operation' AND d.$sourceCodeColumn = diff.$sourceCodeColumn"; + if ($where) { + $wheres .= sprintf(" AND (%s)", $where); + } // instruction de mise à jour de la table destination $markup = ''; $markup .= "UPDATE $destinationTable d SET " . PHP_EOL; $markup .= $setters . PHP_EOL; $markup .= "FROM (SELECT * FROM $diffViewName) AS diff " . PHP_EOL; - $markup .= "WHERE $where"; + $markup .= "WHERE $wheres"; return $markup; } @@ -87,4 +91,12 @@ class TableHelper extends \UnicaenDbImport\CodeGenerator\Helper\TableHelper { return "nextval('" . $sequenceName . "')"; } + + /** + * @inheritDoc + */ + public function generateSQLForCastToString(string $columnName) + { + return $columnName . '::varchar'; + } } \ No newline at end of file diff --git a/src/UnicaenDbImport/Config/Config.php b/src/UnicaenDbImport/Config/Config.php index a6c4a0536cadcc6d9b272b213922029e1c1d9792..4628c2c42f9f74238225d9c86a6898fce24c1e65 100644 --- a/src/UnicaenDbImport/Config/Config.php +++ b/src/UnicaenDbImport/Config/Config.php @@ -10,6 +10,9 @@ use UnicaenDbImport\Domain\SynchroInterface; */ class Config { + const KEY_default_source_code = 'default_source_code'; + const KEY_source_entity_class = 'source_entity_class'; + const COLUMN_CREATED_ON = 'created_on'; const COLUMN_UPDATED_ON = 'updated_on'; const COLUMN_DELETED_ON = 'deleted_on'; @@ -17,6 +20,16 @@ class Config const COLUMN_UPDATED_BY = 'updated_by'; const COLUMN_DELETED_BY = 'deleted_by'; + /** + * @var string + */ + protected $sourceEntityClass; + + /** + * @var string + */ + protected $defaultSourceCode; + /** * @var string */ @@ -80,6 +93,42 @@ class Config */ protected $synchros = []; + /** + * @param string $sourceEntityClass + * @return self + */ + public function setSourceEntityClass(string $sourceEntityClass): self + { + $this->sourceEntityClass = $sourceEntityClass; + return $this; + } + + /** + * @return string + */ + public function getSourceEntityClass(): string + { + return $this->sourceEntityClass; + } + + /** + * @return string + */ + public function getDefaultSourceCode(): string + { + return $this->defaultSourceCode; + } + + /** + * @param string $defaultSourceCode + * @return Config + */ + public function setDefaultSourceCode(string $defaultSourceCode): Config + { + $this->defaultSourceCode = $defaultSourceCode; + return $this; + } + /** * @return string */ diff --git a/src/UnicaenDbImport/Config/ConfigException.php b/src/UnicaenDbImport/Config/ConfigException.php index 74244c4e01214b1435be9825b6ba57022dcc5576..cc2d817eb334b3a398a7bb9c52e1692b071690ca 100644 --- a/src/UnicaenDbImport/Config/ConfigException.php +++ b/src/UnicaenDbImport/Config/ConfigException.php @@ -6,9 +6,28 @@ use UnicaenApp\Exception\RuntimeException; class ConfigException extends RuntimeException { + public static function missingKeyInImport($key, $name = null) + { + return $name ? + new static("Le tableau de config de l'import '$name' doit contenir une clé '$key'") : + new static("Le tableau de config d'un import ne contient pas la clé '$key' attendue"); + } + + public static function missingKeyInSynchro($key, $name = null) + { + return $name ? + new static("Le tableau de config de la synchro '$name' doit contenir une clé '$key'") : + new static("Le tableau de config d'une synchro ne contient pas la clé '$key' attendue"); + } + + public static function missingRootKey($key) + { + return new static("La config de l'appli doit contenir la clé '$key' contenant la config du module"); + } + public static function missingKey($key) { - return new static("Clé de config '$key' introuvable"); + return new static("Le tableau de config du module doit contenir la clé '$key'"); } public static function missingKeyInConnections($key) diff --git a/src/UnicaenDbImport/Config/ConfigFactory.php b/src/UnicaenDbImport/Config/ConfigFactory.php index beb8d0bc58fd04b33a68e1ad4b71ad37b53c8545..21e8dd351badfeefdd43ad6b288df9a0bec76b8f 100644 --- a/src/UnicaenDbImport/Config/ConfigFactory.php +++ b/src/UnicaenDbImport/Config/ConfigFactory.php @@ -17,7 +17,7 @@ use Zend\ServiceManager\ServiceLocatorInterface; class ConfigFactory { - const CONFIG_KEY = 'import'; + const CONFIG_ROOT_KEY = 'import'; /** * @var ServiceLocatorInterface @@ -43,6 +43,7 @@ class ConfigFactory $this->container = $container; $this->moduleConfig = $this->getModuleConfig(); + $defaultSourceCode = $this->getDefaultSourceCode(); $importObservEntityClass = $this->getImportObservEntityClass(); $importObservResultEntityClass = $this->getImportObservResultEntityClass(); $codeGeneratorsConfig = $this->getCodeGeneratorsConfig(); @@ -54,6 +55,7 @@ class ConfigFactory $synchros = $this->createSynchros(); $config = new Config(); + $config->setDefaultSourceCode($defaultSourceCode); $config->setImportObservEntityClass($importObservEntityClass); $config->setImportObservResultEntityClass($importObservResultEntityClass); $config->setCodeGeneratorsMappingConfig($codeGeneratorsConfig); @@ -74,13 +76,27 @@ class ConfigFactory { $appConfig = $this->container->get('Config'); - if (!array_key_exists($key = self::CONFIG_KEY, $appConfig)) { - throw ConfigException::missingKey($key); + if (!array_key_exists($key = self::CONFIG_ROOT_KEY, $appConfig)) { + throw ConfigException::missingRootKey($key); } return $appConfig[$key]; } + /** + * @return string + */ + private function getDefaultSourceCode() + { + try { + Assertion::keyExists($this->moduleConfig, $key = 'default_source_code'); + } catch (AssertionFailedException $e) { + throw ConfigException::missingKey($key); + } + + return $this->moduleConfig[$key]; + } + /** * @return string */ @@ -185,11 +201,16 @@ class ConfigFactory */ private function createImport(array $importConfig) { + if (!array_key_exists($key = 'name', $importConfig)) { + throw ConfigException::missingKeyInImport($key); + } + $name = $importConfig['name']; + if (!array_key_exists($key = 'source', $importConfig)) { - throw ConfigException::missingKey($key); + throw ConfigException::missingKeyInImport($key, $name); } if (!array_key_exists($key = 'destination', $importConfig)) { - throw ConfigException::missingKey($key); + throw ConfigException::missingKeyInImport($key, $name); } $source = $this->createSource($importConfig['source']); @@ -222,11 +243,16 @@ class ConfigFactory */ private function createSynchro(array $synchroConfig) { + if (!array_key_exists($key = 'name', $synchroConfig)) { + throw ConfigException::missingKeyInImport($key); + } + $name = $synchroConfig['name']; + if (!array_key_exists($key = 'source', $synchroConfig)) { - throw ConfigException::missingKey($key); + throw ConfigException::missingKeyInSynchro($key, $name); } if (!array_key_exists($key = 'destination', $synchroConfig)) { - throw ConfigException::missingKey($key); + throw ConfigException::missingKeyInSynchro($key, $name); } $source = $this->createSource($synchroConfig['source']); diff --git a/src/UnicaenDbImport/Controller/ConsoleController.php b/src/UnicaenDbImport/Controller/ConsoleController.php index 85c1b887166336bb9233b61312c018a0a0841f6b..d9b4599f3f9eaf5bb5393ffffe672181261ade96 100644 --- a/src/UnicaenDbImport/Controller/ConsoleController.php +++ b/src/UnicaenDbImport/Controller/ConsoleController.php @@ -135,25 +135,14 @@ class ConsoleController extends AbstractConsoleController { /** @var Request $request */ $request = $this->getRequest(); - - //Passage de code à probablement concaténer en un (redonant au moins 3 fois) - $all = $request->getParam('all') || $request->getParam('a'); $name = $request->getParam('name'); - if ($all && $name !== null) { - $this->getConsole()->writeLine("Les paramètres 'all' et 'name' sont antinomiques", ColorInterface::RED); - } - - if (!$all && $name === null) { - $all = TRUE; - } - $this->getConsole()->writeLine(); $this->getConsole()->writeLine("######################## IMPORTS ########################", ColorInterface::BLUE); $this->getConsole()->writeLine(); try { - if ($all) { + if ($name === null) { $imports = $this->importService->getImports(); } else { try { @@ -183,25 +172,14 @@ class ConsoleController extends AbstractConsoleController { /** @var Request $request */ $request = $this->getRequest(); - - //Passage de code à probablement concaténer en un (redonant au moins 3 fois) - $all = $request->getParam('all') || $request->getParam('a'); $name = $request->getParam('name'); - if ($all && $name !== null) { - $this->getConsole()->writeLine("Les paramètres 'all' et 'name' sont antinomiques", ColorInterface::RED); - } - - if (!$all && $name === null) { - $all = TRUE; - } - $this->getConsole()->writeLine(); $this->getConsole()->writeLine("######################## SYNCHROS ########################", ColorInterface::BLUE); $this->getConsole()->writeLine(); try { - if ($all) { + if ($name === null) { $synchros = $this->synchroService->getSynchros(); } else { try { diff --git a/src/UnicaenDbImport/Domain/Destination.php b/src/UnicaenDbImport/Domain/Destination.php index efa9c8edb7122a67f851e9643a2a18a08fb58b4e..beb54c836171863bc9409fd8f2e42d0f4c1c8e58 100644 --- a/src/UnicaenDbImport/Domain/Destination.php +++ b/src/UnicaenDbImport/Domain/Destination.php @@ -62,6 +62,11 @@ class Destination implements DestinationInterface throw ConfigException::illegalKey('select'); } + if ($where = $this->config->get($key = 'where')) + (new AssertionChain($where, "Une string non vide est requise pour la clé suivante: $key")) + ->notEmpty() + ->string(); + (new AssertionChain($this->config->get($key = 'connection'), "Une instance de " . Connection::class . " est requise pour la clé suivante: $key")) ->notEmpty() ->isInstanceOf(Connection::class); @@ -82,6 +87,11 @@ class Destination implements DestinationInterface return $this->config->get('table'); } + public function getWhere() + { + return $this->config->get('where'); + } + public function getConnection() { return $this->config->get('connection'); diff --git a/src/UnicaenDbImport/Domain/DestinationInterface.php b/src/UnicaenDbImport/Domain/DestinationInterface.php index c4213c6ce50dd4b11f5ad48e0bce25a034312a70..1bfdd897966aa5e81dc0d9f5fafffaa507513f58 100644 --- a/src/UnicaenDbImport/Domain/DestinationInterface.php +++ b/src/UnicaenDbImport/Domain/DestinationInterface.php @@ -24,6 +24,13 @@ interface DestinationInterface */ public function getTable(); + /** + * Retourne le filtre SQL éventuel permettant de restreindre les données destinations concernées. + * + * @return string + */ + public function getWhere(); + /** * Retourne l'instance de la connexion Doctrine vers cette destination. * diff --git a/src/UnicaenDbImport/Domain/Source.php b/src/UnicaenDbImport/Domain/Source.php index 7e3b9e0ea225b1312872c165da8a768009722e27..b79d54bce34018c667bbdf07f17f95d0349fed47 100644 --- a/src/UnicaenDbImport/Domain/Source.php +++ b/src/UnicaenDbImport/Domain/Source.php @@ -80,6 +80,14 @@ class Source implements SourceInterface return $this->config->get('select'); } + /** + * {@inheritDoc} + */ + public function getWhere() + { + return $this->config->get('where'); + } + /** * {@inheritDoc} */ @@ -167,6 +175,11 @@ class Source implements SourceInterface throw ConfigException::atLeastOneKey(['table', 'select']); } + if ($where = $this->config->get($key = 'where')) + (new AssertionChain($where, "Une string non vide est requise pour la clé suivante: $key")) + ->notEmpty() + ->string(); + $conn = $this->config->get($key = 'connection'); if (! $conn instanceof DbConnection && ! $conn instanceof ApiConnection) { throw new InvalidArgumentException( diff --git a/src/UnicaenDbImport/Domain/SourceInterface.php b/src/UnicaenDbImport/Domain/SourceInterface.php index 9dc69759195cc11212f2551def33d996aeeccb94..b1de74313775d8b5fd2da19a5c8fe39683a719c0 100644 --- a/src/UnicaenDbImport/Domain/SourceInterface.php +++ b/src/UnicaenDbImport/Domain/SourceInterface.php @@ -28,6 +28,13 @@ interface SourceInterface */ public function getSelect(); + /** + * Retourne le filtre SQL éventuel à appliquer aux données sources. + * + * @return string + */ + public function getWhere(); + /** * Retourne l'instance de la connexion Doctrine vers cette source. * diff --git a/src/UnicaenDbImport/Entity/Db/Interfaces/SourceAwareInterface.php b/src/UnicaenDbImport/Entity/Db/Interfaces/SourceAwareInterface.php index bcedb15745ef2ef2db3d6824f4152cdda98beaa4..e40cf54ba486c643eaeeee02a0148fdce321c437 100644 --- a/src/UnicaenDbImport/Entity/Db/Interfaces/SourceAwareInterface.php +++ b/src/UnicaenDbImport/Entity/Db/Interfaces/SourceAwareInterface.php @@ -12,13 +12,12 @@ use UnicaenDbImport\Entity\Db\Source; interface SourceAwareInterface { /** - * @param Source $source - * @return self + * @param SourceInterface|null $source */ - public function setSource(Source $source = null); + public function setSource(SourceInterface $source = null); /** - * @return Source + * @return SourceInterface|null */ - public function getSource(); + public function getSource(): ?SourceInterface; } \ No newline at end of file diff --git a/src/UnicaenDbImport/Entity/Db/Interfaces/SourceInterface.php b/src/UnicaenDbImport/Entity/Db/Interfaces/SourceInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..247d40f1a5138977d4a85a59e5de338d94707741 --- /dev/null +++ b/src/UnicaenDbImport/Entity/Db/Interfaces/SourceInterface.php @@ -0,0 +1,48 @@ +<?php + +namespace UnicaenDbImport\Entity\Db\Interfaces; + +use UnicaenDbImport\Entity\Db\Source; + +/** + * Interface décrivant une "source". + * + * @see Source + */ +interface SourceInterface +{ + /** + * Get id + * + * @return int + */ + public function getId(): int; + + /** + * Get code + * + * @return string + */ + public function getCode(): string; + + /** + * Get importable + * + * @return boolean + */ + public function getImportable(): bool; + + /** + * Get libelle + * + * @return string + */ + public function getLibelle(): string; + + /** + * Retourne la représentation littérale de cette source. + * + * @return string + */ + public function __toString(): string; +} \ No newline at end of file diff --git a/src/UnicaenDbImport/Entity/Db/Mapping/UnicaenDbImport.Entity.Db.Source.dcm.xml b/src/UnicaenDbImport/Entity/Db/Mapping/UnicaenDbImport.Entity.Db.Source.dcm.xml index a732cc546a77c2230b92953945515bcaf96b86d4..84dd784ec08993e5f98fdad9748fb234595d3b2d 100644 --- a/src/UnicaenDbImport/Entity/Db/Mapping/UnicaenDbImport.Entity.Db.Source.dcm.xml +++ b/src/UnicaenDbImport/Entity/Db/Mapping/UnicaenDbImport.Entity.Db.Source.dcm.xml @@ -2,7 +2,6 @@ <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> - <entity name="UnicaenDbImport\Entity\Db\Source" table="SOURCE"> <id name="id" type="integer" column="ID"> @@ -12,5 +11,6 @@ <field name="code" column="CODE"/> <field name="importable" type="boolean" column="IMPORTABLE"/> <field name="libelle" column="LIBELLE"/> + </entity> </doctrine-mapping> diff --git a/src/UnicaenDbImport/Entity/Db/Service/ServiceException.php b/src/UnicaenDbImport/Entity/Db/Service/ServiceException.php new file mode 100644 index 0000000000000000000000000000000000000000..9da4a43275ac96c706ec7f0f227e86642c874e31 --- /dev/null +++ b/src/UnicaenDbImport/Entity/Db/Service/ServiceException.php @@ -0,0 +1,8 @@ +<?php + +namespace UnicaenDbImport\Entity\Db\Service; + +class ServiceException extends \Exception +{ + +} \ No newline at end of file diff --git a/src/UnicaenDbImport/Entity/Db/Service/Source/SourceService.php b/src/UnicaenDbImport/Entity/Db/Service/Source/SourceService.php new file mode 100644 index 0000000000000000000000000000000000000000..c7657fedb3f300e88dd403f2583fbc7232626360 --- /dev/null +++ b/src/UnicaenDbImport/Entity/Db/Service/Source/SourceService.php @@ -0,0 +1,51 @@ +<?php + +namespace UnicaenDbImport\Entity\Db\Service\Source; + +use UnicaenDbImport\Entity\Db\Service\ServiceException; +use UnicaenDbImport\Entity\Db\Source; +use UnicaenDbImport\Entity\Db\Service\AbstractService; + +class SourceService extends AbstractService +{ + /** + * @var string + */ + protected $defaultSourceCode; + + /** + * @param string $defaultSourceCode + * @return SourceService + */ + public function setDefaultSourceCode(string $defaultSourceCode): SourceService + { + $this->defaultSourceCode = $defaultSourceCode; + return $this; + } + + /** + * @inheritDoc + */ + public function getRepository() + { + return $this->getEntityManager()->getRepository(Source::class); + } + + /** + * Retourne la Source par défaut (celle dont le code est spécifié dans la config du module). + * + * @return Source + * @throws ServiceException Source introuvable + */ + public function fetchDefaultSource(): Source + { + /** @var Source|null $source */ + $source = $this->getRepository()->findOneBy(['code' => $this->defaultSourceCode]); + + if ($source === null) { + throw new ServiceException("Source introuvable avec ce code : " . $this->defaultSourceCode); + } + + return $source; + } +} \ No newline at end of file diff --git a/src/UnicaenDbImport/Entity/Db/Service/Source/SourceServiceAwareTrait.php b/src/UnicaenDbImport/Entity/Db/Service/Source/SourceServiceAwareTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..293edffaf211d1902e7302ee784573ec15033ccf --- /dev/null +++ b/src/UnicaenDbImport/Entity/Db/Service/Source/SourceServiceAwareTrait.php @@ -0,0 +1,19 @@ +<?php + +namespace UnicaenDbImport\Entity\Db\Service\Source; + +trait SourceServiceAwareTrait +{ + /** + * @var SourceService + */ + protected $sourceService; + + /** + * @param SourceService $serviceSource + */ + public function setSourceService(SourceService $serviceSource) + { + $this->sourceService = $serviceSource; + } +} \ No newline at end of file diff --git a/src/UnicaenDbImport/Entity/Db/Service/Source/SourceServiceFactory.php b/src/UnicaenDbImport/Entity/Db/Service/Source/SourceServiceFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..2c76fbc860c3a5273186fa9a8b7d577c9aad04b6 --- /dev/null +++ b/src/UnicaenDbImport/Entity/Db/Service/Source/SourceServiceFactory.php @@ -0,0 +1,20 @@ +<?php + +namespace UnicaenDbImport\Entity\Db\Service\Source; + +use Interop\Container\ContainerInterface; +use UnicaenDbImport\Config\Config; + +class SourceServiceFactory +{ + public function __invoke(ContainerInterface $container): SourceService + { + /** @var Config $config */ + $config = $container->get(Config::class); + + $service = new SourceService(); + $service->setDefaultSourceCode($config->getDefaultSourceCode()); + + return $service; + } +} \ No newline at end of file diff --git a/src/UnicaenDbImport/Entity/Db/Source.php b/src/UnicaenDbImport/Entity/Db/Source.php index 4266fb13bc3cbfc22d331236026008803346e624..0d3af25798ad3d0f37ce467bc70cdab71cb48e7e 100644 --- a/src/UnicaenDbImport/Entity/Db/Source.php +++ b/src/UnicaenDbImport/Entity/Db/Source.php @@ -2,10 +2,12 @@ namespace UnicaenDbImport\Entity\Db; +use UnicaenDbImport\Entity\Db\Interfaces\SourceInterface; + /** * Source */ -class Source +class Source implements SourceInterface { /** * @var int @@ -30,9 +32,9 @@ class Source /** * Get id * - * @return integer + * @return int */ - public function getId() + public function getId(): int { return $this->id; } @@ -41,14 +43,10 @@ class Source * Set code * * @param string $code - * - * @return Source */ - public function setCode($code) + public function setCode(string $code) { $this->code = $code; - - return $this; } /** @@ -56,7 +54,7 @@ class Source * * @return string */ - public function getCode() + public function getCode(): string { return $this->code; } @@ -65,14 +63,10 @@ class Source * Set importable * * @param boolean $importable - * - * @return Source */ - public function setImportable($importable) + public function setImportable(bool $importable) { $this->importable = $importable; - - return $this; } /** @@ -80,7 +74,7 @@ class Source * * @return boolean */ - public function getImportable() + public function getImportable(): bool { return $this->importable; } @@ -89,14 +83,10 @@ class Source * Set libelle * * @param string $libelle - * - * @return Source */ - public function setLibelle($libelle) + public function setLibelle(string $libelle) { $this->libelle = $libelle; - - return $this; } /** @@ -104,7 +94,7 @@ class Source * * @return string */ - public function getLibelle() + public function getLibelle(): string { return $this->libelle; } @@ -114,23 +104,8 @@ class Source * * @return string */ - public function __toString() + public function __toString(): string { return $this->getLibelle(); } - - /** - * @return array - * @since PHP 5.6.0 - * This method is called by var_dump() when dumping an object to get the properties that should be shown. - * If the method isn't defined on an object, then all public, protected and private properties will be shown. - * - * @link http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo - */ - function __debugInfo() - { - return [ - 'libelle' => $this->libelle, - ]; - } } \ No newline at end of file diff --git a/src/UnicaenDbImport/Entity/Db/Traits/SourceAwareTrait.php b/src/UnicaenDbImport/Entity/Db/Traits/SourceAwareTrait.php index bf54f7797ae5d67e7a0a4a7b1e670b78972cde70..6f5dc3451c0cb42bd989eed4585780ea40684ab4 100644 --- a/src/UnicaenDbImport/Entity/Db/Traits/SourceAwareTrait.php +++ b/src/UnicaenDbImport/Entity/Db/Traits/SourceAwareTrait.php @@ -2,30 +2,27 @@ namespace UnicaenDbImport\Entity\Db\Traits; -use UnicaenDbImport\Entity\Db\Source; +use UnicaenDbImport\Entity\Db\Interfaces\SourceInterface; trait SourceAwareTrait { /** - * @var Source + * @var SourceInterface */ protected $source; /** - * @param Source $source - * @return self + * @param SourceInterface|null $source */ - public function setSource(Source $source = null): self + public function setSource(SourceInterface $source = null) { $this->source = $source; - - return $this; } /** - * @return Source + * @return SourceInterface|null */ - public function getSource(): Source + public function getSource(): ?SourceInterface { return $this->source; } diff --git a/src/UnicaenDbImport/ORM/Event/Listeners/SourceListener.php b/src/UnicaenDbImport/ORM/Event/Listeners/SourceListener.php new file mode 100644 index 0000000000000000000000000000000000000000..d33727e106c0227eb6b8b68a4093086c35507414 --- /dev/null +++ b/src/UnicaenDbImport/ORM/Event/Listeners/SourceListener.php @@ -0,0 +1,120 @@ +<?php + +namespace UnicaenDbImport\ORM\Event\Listeners; + +use Doctrine\Common\EventSubscriber; +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Event\LifecycleEventArgs; +use Doctrine\ORM\Event\PreUpdateEventArgs; +use Doctrine\ORM\Events; +use RuntimeException; +use UnicaenDbImport\Entity\Db\Interfaces\SourceAwareInterface; +use UnicaenDbImport\Entity\Db\Source; + +/** + * Listener Doctrine chargé d'injecter si besoin une Source par défaut dans les entités + * implémentant {@see SourceAwareInterface}. + * + * Déclenchement : avant que l'entité ne soit persistée (création) ou mis à jour (update). + * + * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr> + * @see SourceAwareInterface + */ +class SourceListener implements EventSubscriber +{ + /** + * @var string + */ + protected $sourceEntityClass; + + /** + * @param string $sourceEntityClass + * @return self + */ + public function setSourceEntityClass(string $sourceEntityClass): self + { + $this->sourceEntityClass = $sourceEntityClass; + return $this; + } + + /** + * @var string + */ + protected $defaultSourceCode; + + /** + * @param string $defaultSourceCode + * @return self + */ + public function setDefaultSourceCode(string $defaultSourceCode): self + { + $this->defaultSourceCode = $defaultSourceCode; + return $this; + } + + /** + * @param LifecycleEventArgs $args + * @throws RuntimeException Aucun utilisateur disponible pour en faire l'auteur de la création/modification + */ + protected function updateSource(LifecycleEventArgs $args) + { + $entity = $args->getEntity(); + + if (! $entity instanceof SourceAwareInterface) { + return; + } + if ($entity->getSource() !== null) { + return; + } + + $defaultSource = $this->fetchDefaultSource($args->getEntityManager()); + + $entity->setSource($defaultSource); + } + + /** + * @param EntityManager $entityManager + * @return Source + */ + private function fetchDefaultSource(EntityManager $entityManager): Source + { + if ($this->sourceEntityClass === null) { + throw new RuntimeException("La classe d'entité Source n'a pas été spécifiée"); + } + if ($this->defaultSourceCode === null) { + throw new RuntimeException("Aucun code n'a été spécifié pour la Source par défaut"); + } + + /** @var Source $defaultSource */ + $defaultSource = $entityManager->getRepository($this->sourceEntityClass)->findOneBy(['code' => $this->defaultSourceCode]); + if ($defaultSource === null) { + throw new RuntimeException("Source par défaut introuvable avec ce code : " . $this->defaultSourceCode); + } + + return $defaultSource; + } + + /** + * @param LifecycleEventArgs $args + */ + public function prePersist(LifecycleEventArgs $args) + { + $this->updateSource($args); + } + + /** + * @param PreUpdateEventArgs $args + */ + public function preUpdate(PreUpdateEventArgs $args) + { + $this->updateSource($args); + } + + /** + * {@inheritdoc} + */ + public function getSubscribedEvents() + { + return [Events::prePersist, Events::preUpdate]; + } +} \ No newline at end of file diff --git a/src/UnicaenDbImport/ORM/Event/Listeners/SourceListenerFactory.php b/src/UnicaenDbImport/ORM/Event/Listeners/SourceListenerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..e144ec312d3e354050297444ac5e148873aeb187 --- /dev/null +++ b/src/UnicaenDbImport/ORM/Event/Listeners/SourceListenerFactory.php @@ -0,0 +1,37 @@ +<?php + +namespace UnicaenDbImport\ORM\Event\Listeners; + +use Interop\Container\ContainerInterface; +use UnicaenDbImport\Config\Config; +use UnicaenDbImport\Config\ConfigException; + +class SourceListenerFactory +{ + /** + * @param ContainerInterface $container + * @return SourceListener + */ + public function __invoke(ContainerInterface $container): SourceListener + { + // + // ATTENTION aux boucles de dépendance (ex: SourceListener => SourceService => EntityManager => SourceListener), + // donc maheureusement mieux vaut n'injecter aucun service dans un listener Doctrine. + // + + /** @var array $config */ + $config = $container->get('Config')['import']; + if (!isset($config[$key = Config::KEY_source_entity_class])) { + throw ConfigException::missingKey($key); + } + if (!isset($config[$key = Config::KEY_default_source_code])) { + throw ConfigException::missingKey($key); + } + + $listener = new SourceListener(); + $listener->setSourceEntityClass($config[Config::KEY_source_entity_class]); + $listener->setDefaultSourceCode($config[Config::KEY_default_source_code]); + + return $listener; + } +} diff --git a/src/UnicaenDbImport/QueryExecutor.php b/src/UnicaenDbImport/QueryExecutor.php index 1e190d2ff232596b9956f2f07738fd27163c222c..20c76d458a43a430ddc7b92cf7048f283170958a 100644 --- a/src/UnicaenDbImport/QueryExecutor.php +++ b/src/UnicaenDbImport/QueryExecutor.php @@ -60,7 +60,7 @@ class QueryExecutor $statement = $this->executeQuery($sql, $connection); $rows = []; - while ($row = $statement->fetch(\PDO::FETCH_ASSOC)) { + while ($row = $statement->fetchAssociative()) { $rows[] = $row; yield $row; } @@ -83,12 +83,16 @@ class QueryExecutor /** * @param string $sql * @param Connection $connection - * @return array + * @return array|null */ public function fetch($sql, Connection $connection) { $statement = $this->executeQuery($sql, $connection); + if ($statement->rowCount() === 0) { + return null; + } + return $statement->fetch(\PDO::FETCH_ASSOC); } diff --git a/src/UnicaenDbImport/Service/DatabaseService.php b/src/UnicaenDbImport/Service/DatabaseService.php index 65de97b3596349f24c0b935ee7ab3acbf742d5be..2e0b75d9f5b58ec90ba9d65f9c1cffe2c3940dac 100644 --- a/src/UnicaenDbImport/Service/DatabaseService.php +++ b/src/UnicaenDbImport/Service/DatabaseService.php @@ -400,7 +400,7 @@ class DatabaseService $importObservRows = $qb->getQuery()->getResult(Query::HYDRATE_ARRAY); if (!empty($importObservRows)) { - $sql = $this->codeGenerator->generateSQLForInsertionIntoImportObservResult($importObservRows); + $sql = $this->codeGenerator->generateSQLForInsertionIntoImportObservResult($this->destination, $importObservRows); try { $this->queryExecutor->exec($sql, $this->destination->getConnection()); } catch (DBALException $e) { @@ -482,7 +482,7 @@ class DatabaseService /** * Requête la Source pour obtenir le 1er enregistrements des données sources. * - * @return array + * @return array|null */ public function fetchSourceFirstRow() { diff --git a/src/UnicaenDbImport/Service/FacadeService.php b/src/UnicaenDbImport/Service/FacadeService.php index 960c52aeebe91d9688fa04c1ad1609b6d7dcd49f..645c2b3209b36add5b79d876e68c126ca3b26ff5 100644 --- a/src/UnicaenDbImport/Service/FacadeService.php +++ b/src/UnicaenDbImport/Service/FacadeService.php @@ -275,12 +275,16 @@ class FacadeService break; case $connection instanceof DbConnection: - $data = [ $this->databaseService->fetchSourceFirstRow() ]; + $firstRow = $this->databaseService->fetchSourceFirstRow(); + if ($firstRow === null) { + throw new RuntimeException( + "Impossible de découvrir la liste des colonnes à partir des données sources car celles-ci sont vides"); + } + $data = [ $firstRow ]; break; default: throw new RuntimeException("Type de connexion source inattendue"); - break; } } @@ -295,11 +299,6 @@ class FacadeService */ private function extractColumnsFromSourceFetchResults(array $rows) { - if (empty($rows)) { - throw new RuntimeException( - "Impossible de déterminer la liste des colonnes à partir des données sources car celles-ci sont vides"); - } - $first = current($rows); if ($first instanceof stdClass) { @@ -311,9 +310,7 @@ class FacadeService "Impossible de déterminer la liste des colonnes à partir des données sources car leur type est inconnu (ni array ni stdClass)"); } - $columns = array_diff($columns, ['source_id', 'SOURCE_ID']); - - return $columns; + return array_diff($columns, ['source_id', 'SOURCE_ID']); } /**