Loading README.md +717 −376 File changed.Preview size limit exceeded, changes collapsed. Show changes config/unicaen-db-import.global.php.dist +100 −53 Original line number Diff line number Diff line Loading @@ -67,24 +67,28 @@ return [ 'source_entity_class' => Source::class, // // Code de la Source par défaut (injectée dans les entités implémentant SourceAwareInterface). // Code de la source par défaut (typiquement, c'est celle correpondant à l'application). // NB : la valeur doit exister dans la table `source` (colonne `code`). // (La source correspondante sera injectée dans les entités implémentant SourceAwareInterface.) // 'default_source_code' => 'app', // // Alias éventuels des noms de colonnes d'historique. // Alias éventuels de noms de colonnes d'historique. // 'histo_columns_aliases' => [ 'created_on' => 'HISTO_CREATION', // date/heure de création de l'enregistrement 'updated_on' => 'HISTO_MODIFICATION', // date/heure de modification 'deleted_on' => 'HISTO_DESTRUCTION', // date/heure de suppression 'created_by' => 'HISTO_CREATEUR_ID', // auteur de la création de l'enregistrement 'updated_by' => 'HISTO_MODIFICATEUR_ID', // auteur de la modification 'deleted_by' => 'HISTO_DESTRUCTEUR_ID', // auteur de la suppression ], //'histo_columns_aliases' => [ // 'created_on' => 'histo_creation', // date/heure de création de l'enregistrement // 'updated_on' => 'histo_modification', // date/heure de modification // 'deleted_on' => 'histo_destruction', // date/heure de suppression // 'created_by' => 'histo_createur_id', // auteur de la création de l'enregistrement // 'updated_by' => 'histo_modificateur_id', // auteur de la modification // 'deleted_by' => 'histo_destructeur_id', // auteur de la suppression //], // // Forçage éventuel de valeur pour les colonnes d'historique. // Valeurs par défaut des colonnes d'historique correspondant aux auteurs. // NB : si vous avez spécifié une valeur par défaut en base de données, commentez la ligne. // Format : 'nom de colonne non aliasée' => entier // 'histo_columns_values' => [ 'created_by' => 1, // auteur de la création de l'enregistrement Loading Loading @@ -113,8 +117,11 @@ return [ // ], //], ], /** * Idem pour les synchros. * Configs communes éventuelles pour des synchros, permettant de factoriser les attributs communs à plusieurs * synchros, héritables à l'aide de la clé 'extends'. * Une telle config peut elle-même étendre une autre config commune. */ 'synchro_base_configs' => [], Loading Loading @@ -142,36 +149,69 @@ return [ 'connection' => 'geo', // URL à concaténer à l'URL de la connexion, permettant l'interrogation des données // Formats d'URL possibles : // - string, ex : '/search/?q=authIdHal_i:123' // - array<string>, ex : ['/search/?q=authIdHal_i:123', '/search/?q=authIdHal_i:789'] // - array<array<string>>, // ex 1 : ``` // Valeurs possibles (exemples pour une source de type API (même idée pour une source de type BDD) : // - `string` : // - fragment d'URL, exemple : // - `/regions?fields=nom,code` // - nom d'un service implémentant "\UnicaenDbImport\Domain\SelectProviderInterface", cette solution permettant de // générer des select "dynamiques" ; // - `array<string>` : // - tableau de fragments d'URL, exemple : // - ``` // [ // ['select' => '/search/?q=authIdHal_i:123'], // ['select' => '/search/?q=authIdHal_i:789'], // ]``` // ex 2 (spécification de colonnes valuées à ajouter aux données obtenues) : ``` // '/regions/1/departements', // '/regions/2/departements', // ] // ``` // - `array<array<string, array>>` : // - tableau de fragments d'URL spécifiés par un tableau avec clé `'select'` obligatoire // et clé `'columns'` facultative (colonnes valuées à ajouter aux données obtenues), exemples : // - ``` // [ // ['select' => '/search/?q=authIdHal_i:123', 'columns' => ['hal_id' => '123']], // ['select' => '/search/?q=authIdHal_i:789', 'columns' => ['hal_id' => '789']], // ]``` // ['select' => '/regions/1/departements'], // ['select' => '/regions/2/departements'], // ] // ``` // - ``` // [ // ['select' => '/regions/1/departements', 'columns' => ['codeRegion' => '1']], // ['select' => '/regions/2/departements', 'columns' => ['codeRegion' => '2']], // ] // ``` 'select' => '/regions?fields=nom,code', // Taille des pages de données demandée à l'API (si supportée par l'API) //'page_size' => 20, // Liste exhaustive éventuelle des seuls attributs/colonnes qui nous intéressent dans la source. // Si la clé est absente, tous les attributs/colonnes sont pris en compte. //'columns' => [], // Taille des pages de données qui seront demandées à l'API. // La pagination n'est activée que si cette valeur est supérieure à 0. // *NB : vérifiez bien que l'API que vous interrogez supporte la pagination. Si vous activez la pagination pour une API // ne la supportant pas, vous rencontrerez une erreur du genre "ERROR: duplicate key value violates unique constraint" // puisqu'à chaque interrogation l'API retournera les mêmes données (toutes).* //'page_size' => 100, // Liste éventuelle des colonnes/attributs dont la valeur est calculée. // Liste exhaustive des seuls attributs/colonnes qui nous intéressent dans la source. // Cela est utile lorsque la table ou le select ramène plus de colonnes/attributs que la destination n'en attend. // // Si la clé est absente, tous les attributs/colonnes seront pris en compte. // // *Dans le cas d'une source de type API ne supportant pas la pagination, il faut renseigner cette liste. // En effet, lorsque cette liste n'est pas fournie, pour des raisons techniques une requête supplémentaire est // systématiquement réalisée à chaque import/synchro pour obtenir la 1ere ligne des données source et ainsi découvrir // la liste des attributs/colonnes source. Sans pagination, cette requête préalable retournerait toutes les données, ce // qui représente un coût non négligeable. // Le problème ne se pose pas dans le cas d'une source de type BDD puisque tous les SGBD supportent le LIMIT.* // 'columns' => ['nom', 'code'], // Liste éventuelle spécifiant des colonnes/attributs supplémentaires dont la valeur est calculée. // // Tableau associatif de services ou de classes implémentant obligatoirement l'interface // {@see \UnicaenDbImport\Filter\ColumnValue\ColumnValueFilterInterface} dont chaque clé est // "\UnicaenDbImport\Filter\ColumnValue\ColumnValueFilterInterface" dont chaque clé est // le nom de la colonne. // Valeurs possibles pour chaque valeur associée à un nom de colonne : // - string : nom de la colonne dont on veut reprendre la valeur (duplication) ; // - tableau associatif spécifiant le filtre et paramètres de génération de la valeur. // // Valeurs possibles pour chaque item : // - string => string : nom de la colonne calculée => nom de la colonne dont on veut reprendre la valeur (duplication) ; // - tableau associatif spécifiant : // - soit un service implémentant obligatoirement "\UnicaenDbImport\Filter\ColumnValue\ColumnValueFilterInterface" // (clé `name`) et ses paramètres de fonctionnement (clé `params`) ; // - soit un callable (clé `name`) et ses paramètres de fonctionnement (clé `params`). //'computed_columns' => [ // 'sourceCode' => 'code', // la colonne calculée 'sourceCode' aura la même valeur que la colonne 'code' // //['name' => SameColumnValueFilter::class, 'params' => ['column' => 'nomUsuel', 'source_column' => 'nomPatronymique']], Loading @@ -179,23 +219,35 @@ return [ // //['name' => ConcatColumnValueFilter::class, 'params' => ['column' => 'libelleLong', 'columns' => ['code',': ','libelleLong'], 'separator' => '']], //], // Filtres à appliquer pour transformer des valeurs de colonnes/attributs issues de la source // Transformations à appliquer pour transformer des valeurs de colonnes/attributs issues de la source // en nouvelles valeurs de colonnes destination. // Tableau ordonné de services ou de classes implémentant obligatoirement l'interface // {@see \UnicaenDbImport\Filter\ColumnValue\ColumnValueFilterInterface}. // // Valeurs possibles pour chaque item : // - tableau associatif spécifiant : // - soit un service implémentant obligatoirement "\UnicaenDbImport\Filter\ColumnValue\ColumnValueFilterInterface" // (clé `name`) et ses paramètres de fonctionnement (clé `params`) ; // - soit un callable (clé `name`) et ses paramètres de fonctionnement (clé `params`). 'column_value_filter' => [ // ['name' => ToLowercaseColumnValueFilter::class, 'params' => ['column' => 'code']], // ['name' => SameColumnValueFilter::class, 'params' => ['column' => 'nomUsuel', 'source_column' => 'nomPatronymique']], ], // Spécification du filtre à utiliser pour transformer chaque nom de colonne/attribut source en // le nom de la colonne dans la table destination. Valeurs possibles : // - array : un filtre de type `\UnicaenDbImport\Filter\ColumnName\ArrayMapColumnNameFilter` // sera instancié avec ce tableau de correspondance comme mapping ; // - string : service ou classe de filtre à instancier, // implémentant {@see \UnicaenDbImport\Filter\ColumnName\ColumnNameFilterInterface} ; // NB : par défaut, `\UnicaenDbImport\Filter\ColumnName\DefaultColumnNameFilter::class` // est utilisé. // Transformations à appliquer sur les noms de colonnes/attributs source pour produire les noms de colonnes destination. // // Valeurs possibles : // - array : tableau de correspondance 'ancien nom' => 'nouveau nom' qui utilisé pour instancier un filtre de type // "\UnicaenDbImport\Filter\ColumnName\ArrayMapColumnNameFilter", exemple : // - ```php // 'column_name_filter' => [ // 'civilite' => 'civ', // 'nomUsuel' => 'lib_nom_usu_ind', // 'nomPatronymique' => 'lib_nom_pat_ind', // 'prenom1' => 'lib_pr1_ind', // ], // ``` // - string : service de filtre à instancier, implémentant "\UnicaenDbImport\Filter\ColumnName\ColumnNameFilterInterface" // // La valeur par défaut est `\UnicaenDbImport\Filter\ColumnName\DefaultColumnNameFilter::class`. 'column_name_filter' => \UnicaenDbImport\Filter\ColumnName\DefaultColumnNameFilter::class, // Nom de l'attribut ou colonne discriminant·e dans les données source, contenant l'identifiant unique. Loading @@ -219,11 +271,6 @@ return [ // Nom de la colonne dans la table destination contenant l'identifiant unique 'source_code_column' => 'CODE', // Filtres de transformation à appliquer aux valeurs des colonnes destination. // Tableau ordonné de services ou de classes implémentant obligatoirement l'interface // {@see \UnicaenDbImport\Filter\ColumnValue\ColumnValueFilterInterface}. 'column_value_filter' => [], // Stratégie à utiliser pour générer les valeurs dans la colonne ID (clé primaire). // Si la stratégie est `'SEQUENCE'`, la séquence spécifiée par 'id_sequence' sera utilisée. // Si la stratégie est `'IDENTITY'`, la valeur de l'ID des données sources sera utilisée. Loading Loading @@ -263,7 +310,7 @@ return [ 'connection' => 'apogee', // Select SQL de mise en forme des données source à importer (NB: antinomique avec 'table') 'select' => "SELECT COD_PAY, LIB_PAY, LIB_NAT FROM PAYS", 'select' => "select cod_pay, lib_pay, lib_nat from pays", // Nom de la table source contenant les données à importer //'table' => 'PAYS', // Équivalent de SELECT * FROM PAYS; Loading Loading @@ -437,7 +484,7 @@ return [ // Forçage éventuel du nom de la table intermédiaire utilisée lorsque source et destination // ne partagent pas la même connexion. NB: cette table intermédiaire est créée/peuplée/supprimée // dans la base de données destination à chaque import. // dans la base de données destination à chaque synchro. // En l'absence de ce forçage, le nom de la table intermédiaire sera celui de la table destination // préfixé par "tmp_". 'intermediate_table' => 'tmp_pays', Loading config/unicaen-db-import.local.php.dist +5 −12 Original line number Diff line number Diff line Loading @@ -27,26 +27,19 @@ return [ 'user' => null, 'password' => null, // Implémentés, mais pas gérés par Guzzle : // Pagination (si supportée par l'API) : // - 'page_param' : nom du paramètre à utiliser pour demander une page de données précise // - 'page_size_param' : nom du paramètre à utiliser pour demander une taille de page précise /* 'page_param' => 'page', 'page_size_param' => 'page_size', */ //'page_param' => 'page', //'page_size_param' => 'page_size', ], ], ], /** * Configuration Doctrine minimum requise. * * NB: vous pouvez placer cette configuration autre part dans votre projet. */ 'doctrine' => [ 'connection' => [ 'orm_default' => [ 'driverClass' => 'Doctrine\\DBAL\\Driver\\PDOPgSql\\Driver', 'driverClass' => \Doctrine\DBAL\Driver\PDO\PgSQL\Driver::class, 'params' => [ 'host' => 'host.domain.fr', 'port' => '5432', Loading @@ -57,7 +50,7 @@ return [ ], ], 'orm_apogee' => [ 'driverClass' => 'Doctrine\\DBAL\\Driver\\OCI8\\Driver', 'driverClass' => \Doctrine\DBAL\Driver\OCI8\Driver::class, 'params' => [ 'host' => 'host.domain.fr', 'port' => '1527', Loading documentation/fonctionnement_synchro_locale.drawio +53 −1 Original line number Diff line number Diff line <mxfile modified="2019-05-15T13:45:14.165Z" host="www.draw.io" agent="Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0" etag="7NW-oTR3yv0mZXlWeKPD" version="10.6.7" type="device"><diagram id="mWMuuFM-TLnJckGO8D4I" name="Page-1">3Vhbj5s4FP41PM4IcMjlMSVNq2pXu1Kk2faRgkPcOpg1zm1//R5jGwwmk0TNNFUz0sQ+vn/nnO+z46F4e/zAk3LzJ8sw9UI/O3po4YXhCI3gvzSclGHqI2XIOcmUKWgNK/If1kZfW3ckw1Wno2CMClJ2jSkrCpyKji3hnB263daMdlctkxw7hlWaUNf6D8nERp8i8lv7R0zyjVk58HXLNjGdtaHaJBk7WCb03kMxZ0yo0vYYYyqxM7ioccszrc3GOC7ENQNCNWCf0J0+24rteIr17sTJHBk2WspieqKkyDD30LvDhgi8KpNU2g/gaLBtxJZCLYDiV7aDjtkfXxtDkn7PubT+tRMwC9b2NaE0ZpTxeiUUx8tlHIPdPYs+3h5zgY+WSZ/tA2ZbLPgJuhxNYKkROs4CA/uh9dpYmzaWw8ywRMdJ3kzcQgkFjeYwsshB1gvHFBZ4l5E9FHNRH1GZ1gyOaOM9/nfHTMNTVSfAHDoEYXlsG80sC1wJ8mTmgo2p6bpLgHlg4bvvpUgEYcWNe3lYrC2XM/jcJ9bQA4MtGgi2Hqi4yOaS+iSuNKkqknYhxEciPlvlL1D2nyNdW8hD+qZyMpUC9vnZrlijZLUdVtfMuLNoV4p8bGoSCc+x7vXyInjxiUTkQMJpddp/Yqv1U6DFBGcd0nb9ZPkhGvCDsXFMIYb3Xaofco5e4W9G6oTRYTDpRkE067lXHVEPspm5N08jESacxr2JFDDORHWoNKe+KnomTvQsvBh585kA8ayz2c/Ieq1s4LMUtLcfXpAkohtQleDsOzbJVjCZg53806aEkryQUQnBUOe6TDkCYjvXDVuSZXKZQRLgKvMvBdb1aRzMph3cZ5GTxcFoIHzCO6Tx1HHEQB5TCtecc3BY+CdVqe4+a3KUAPW5bx3JP8dR0DKuP5rxLbv63AnmyeQSzOEboRy4l57b2dIwX3Ab8xmW9W9g2bNo9xjvBq60eRY9ki0R6rJcc8W/lS5R0KPLvqzejy5fRfb1+94LtF17H5OU+/z4G1POk4zgDg80N3TTtiBcKwUsBZfRixcthycG4vssdYBgPUcPu2oNe9+V0JgrvdT3YX+/cx9Vv5Jo/qBLxj2XTFyPTNHb8PmwR1wtDRz8fx0t/TH0/fEl7N9KSwexNxPf4znSiuMXq+XSc6TV4WbUxeeIo6YX3ievyoCtxI8V2N4zIuw/I64W2MkFpT4jsODl5GR1K2WH6vyGw6h7/w6R/+q+Rj3h7/WHgtrBXdW+ue+2ET4vSwoEbN5L8onkb0lVf9dKANvyvwHYv7EKzMKuK0YuEQXBz1QBQzyWn9BvqwKToKfBQ/jfSQig2v5IrbKq/aUfvf8f</diagram></mxfile> No newline at end of file <mxfile modified="2026-06-25T08:15:14.866Z" host="drawio-plugin" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" etag="dh87ylEmqqxMijT0wglW" version="22.1.22" type="embed"> <diagram id="mWMuuFM-TLnJckGO8D4I" name="Page-1"> <mxGraphModel dx="600" dy="548" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="2" value="Source" style="shape=cylinder;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;fillColor=#CCFFCC;rounded=0;" parent="1" vertex="1"> <mxGeometry x="80" y="120" width="60" height="80" as="geometry" /> </mxCell> <mxCell id="3" value="<div><font style="font-size: 12px">Desti-</font></div><div><font style="font-size: 12px">nation</font></div>" style="shape=cylinder;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;fillColor=#FF9999;rounded=0;" parent="1" vertex="1"> <mxGeometry x="380" y="120" width="60" height="80" as="geometry" /> </mxCell> <mxCell id="5" value="" style="endArrow=classic;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;dashed=1;strokeColor=default;shape=link;rounded=0;" parent="1" source="2" target="VVtrnJi5iwi28syvJoSf-14" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="70" y="590" as="sourcePoint" /> <mxPoint x="220" y="160" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="8" value="2" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;rounded=0;" parent="1" vertex="1"> <mxGeometry x="247.5" y="240" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="12" value="" style="endArrow=classic;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;dashed=1;strokeColor=default;shape=link;rounded=0;" parent="1" source="3" target="VVtrnJi5iwi28syvJoSf-14" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="330" y="210" as="sourcePoint" /> <mxPoint x="310" y="180" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="VVtrnJi5iwi28syvJoSf-14" value="<div>Vue</div><div>diff.</div>" style="shape=cylinder;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;gradientColor=#CCFFCC;gradientDirection=west;fillColor=#FF9999;rounded=0;" parent="1" vertex="1"> <mxGeometry x="227.5" y="120" width="60" height="80" as="geometry" /> </mxCell> <mxCell id="VVtrnJi5iwi28syvJoSf-17" value="Création vue différentielle V_DIFF_xxx" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="226.5" y="76" width="93.5" height="20" as="geometry" /> </mxCell> <mxCell id="VVtrnJi5iwi28syvJoSf-18" value="1" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;rounded=0;" parent="1" vertex="1"> <mxGeometry x="206" y="76" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="VVtrnJi5iwi28syvJoSf-20" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;rounded=0;" parent="1" source="VVtrnJi5iwi28syvJoSf-14" target="3" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="320" y="260" as="sourcePoint" /> <mxPoint x="370" y="210" as="targetPoint" /> <Array as="points"> <mxPoint x="258" y="230" /> <mxPoint x="410" y="230" /> </Array> </mxGeometry> </mxCell> <mxCell id="VVtrnJi5iwi28syvJoSf-21" value="Application des mises à jour" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="270" y="240" width="170" height="20" as="geometry" /> </mxCell> </root> </mxGraphModel> </diagram> </mxfile> documentation/fonctionnement_synchro_locale.drawio.png 0 → 100644 +19.6 KiB Loading image diff... Loading
config/unicaen-db-import.global.php.dist +100 −53 Original line number Diff line number Diff line Loading @@ -67,24 +67,28 @@ return [ 'source_entity_class' => Source::class, // // Code de la Source par défaut (injectée dans les entités implémentant SourceAwareInterface). // Code de la source par défaut (typiquement, c'est celle correpondant à l'application). // NB : la valeur doit exister dans la table `source` (colonne `code`). // (La source correspondante sera injectée dans les entités implémentant SourceAwareInterface.) // 'default_source_code' => 'app', // // Alias éventuels des noms de colonnes d'historique. // Alias éventuels de noms de colonnes d'historique. // 'histo_columns_aliases' => [ 'created_on' => 'HISTO_CREATION', // date/heure de création de l'enregistrement 'updated_on' => 'HISTO_MODIFICATION', // date/heure de modification 'deleted_on' => 'HISTO_DESTRUCTION', // date/heure de suppression 'created_by' => 'HISTO_CREATEUR_ID', // auteur de la création de l'enregistrement 'updated_by' => 'HISTO_MODIFICATEUR_ID', // auteur de la modification 'deleted_by' => 'HISTO_DESTRUCTEUR_ID', // auteur de la suppression ], //'histo_columns_aliases' => [ // 'created_on' => 'histo_creation', // date/heure de création de l'enregistrement // 'updated_on' => 'histo_modification', // date/heure de modification // 'deleted_on' => 'histo_destruction', // date/heure de suppression // 'created_by' => 'histo_createur_id', // auteur de la création de l'enregistrement // 'updated_by' => 'histo_modificateur_id', // auteur de la modification // 'deleted_by' => 'histo_destructeur_id', // auteur de la suppression //], // // Forçage éventuel de valeur pour les colonnes d'historique. // Valeurs par défaut des colonnes d'historique correspondant aux auteurs. // NB : si vous avez spécifié une valeur par défaut en base de données, commentez la ligne. // Format : 'nom de colonne non aliasée' => entier // 'histo_columns_values' => [ 'created_by' => 1, // auteur de la création de l'enregistrement Loading Loading @@ -113,8 +117,11 @@ return [ // ], //], ], /** * Idem pour les synchros. * Configs communes éventuelles pour des synchros, permettant de factoriser les attributs communs à plusieurs * synchros, héritables à l'aide de la clé 'extends'. * Une telle config peut elle-même étendre une autre config commune. */ 'synchro_base_configs' => [], Loading Loading @@ -142,36 +149,69 @@ return [ 'connection' => 'geo', // URL à concaténer à l'URL de la connexion, permettant l'interrogation des données // Formats d'URL possibles : // - string, ex : '/search/?q=authIdHal_i:123' // - array<string>, ex : ['/search/?q=authIdHal_i:123', '/search/?q=authIdHal_i:789'] // - array<array<string>>, // ex 1 : ``` // Valeurs possibles (exemples pour une source de type API (même idée pour une source de type BDD) : // - `string` : // - fragment d'URL, exemple : // - `/regions?fields=nom,code` // - nom d'un service implémentant "\UnicaenDbImport\Domain\SelectProviderInterface", cette solution permettant de // générer des select "dynamiques" ; // - `array<string>` : // - tableau de fragments d'URL, exemple : // - ``` // [ // ['select' => '/search/?q=authIdHal_i:123'], // ['select' => '/search/?q=authIdHal_i:789'], // ]``` // ex 2 (spécification de colonnes valuées à ajouter aux données obtenues) : ``` // '/regions/1/departements', // '/regions/2/departements', // ] // ``` // - `array<array<string, array>>` : // - tableau de fragments d'URL spécifiés par un tableau avec clé `'select'` obligatoire // et clé `'columns'` facultative (colonnes valuées à ajouter aux données obtenues), exemples : // - ``` // [ // ['select' => '/search/?q=authIdHal_i:123', 'columns' => ['hal_id' => '123']], // ['select' => '/search/?q=authIdHal_i:789', 'columns' => ['hal_id' => '789']], // ]``` // ['select' => '/regions/1/departements'], // ['select' => '/regions/2/departements'], // ] // ``` // - ``` // [ // ['select' => '/regions/1/departements', 'columns' => ['codeRegion' => '1']], // ['select' => '/regions/2/departements', 'columns' => ['codeRegion' => '2']], // ] // ``` 'select' => '/regions?fields=nom,code', // Taille des pages de données demandée à l'API (si supportée par l'API) //'page_size' => 20, // Liste exhaustive éventuelle des seuls attributs/colonnes qui nous intéressent dans la source. // Si la clé est absente, tous les attributs/colonnes sont pris en compte. //'columns' => [], // Taille des pages de données qui seront demandées à l'API. // La pagination n'est activée que si cette valeur est supérieure à 0. // *NB : vérifiez bien que l'API que vous interrogez supporte la pagination. Si vous activez la pagination pour une API // ne la supportant pas, vous rencontrerez une erreur du genre "ERROR: duplicate key value violates unique constraint" // puisqu'à chaque interrogation l'API retournera les mêmes données (toutes).* //'page_size' => 100, // Liste éventuelle des colonnes/attributs dont la valeur est calculée. // Liste exhaustive des seuls attributs/colonnes qui nous intéressent dans la source. // Cela est utile lorsque la table ou le select ramène plus de colonnes/attributs que la destination n'en attend. // // Si la clé est absente, tous les attributs/colonnes seront pris en compte. // // *Dans le cas d'une source de type API ne supportant pas la pagination, il faut renseigner cette liste. // En effet, lorsque cette liste n'est pas fournie, pour des raisons techniques une requête supplémentaire est // systématiquement réalisée à chaque import/synchro pour obtenir la 1ere ligne des données source et ainsi découvrir // la liste des attributs/colonnes source. Sans pagination, cette requête préalable retournerait toutes les données, ce // qui représente un coût non négligeable. // Le problème ne se pose pas dans le cas d'une source de type BDD puisque tous les SGBD supportent le LIMIT.* // 'columns' => ['nom', 'code'], // Liste éventuelle spécifiant des colonnes/attributs supplémentaires dont la valeur est calculée. // // Tableau associatif de services ou de classes implémentant obligatoirement l'interface // {@see \UnicaenDbImport\Filter\ColumnValue\ColumnValueFilterInterface} dont chaque clé est // "\UnicaenDbImport\Filter\ColumnValue\ColumnValueFilterInterface" dont chaque clé est // le nom de la colonne. // Valeurs possibles pour chaque valeur associée à un nom de colonne : // - string : nom de la colonne dont on veut reprendre la valeur (duplication) ; // - tableau associatif spécifiant le filtre et paramètres de génération de la valeur. // // Valeurs possibles pour chaque item : // - string => string : nom de la colonne calculée => nom de la colonne dont on veut reprendre la valeur (duplication) ; // - tableau associatif spécifiant : // - soit un service implémentant obligatoirement "\UnicaenDbImport\Filter\ColumnValue\ColumnValueFilterInterface" // (clé `name`) et ses paramètres de fonctionnement (clé `params`) ; // - soit un callable (clé `name`) et ses paramètres de fonctionnement (clé `params`). //'computed_columns' => [ // 'sourceCode' => 'code', // la colonne calculée 'sourceCode' aura la même valeur que la colonne 'code' // //['name' => SameColumnValueFilter::class, 'params' => ['column' => 'nomUsuel', 'source_column' => 'nomPatronymique']], Loading @@ -179,23 +219,35 @@ return [ // //['name' => ConcatColumnValueFilter::class, 'params' => ['column' => 'libelleLong', 'columns' => ['code',': ','libelleLong'], 'separator' => '']], //], // Filtres à appliquer pour transformer des valeurs de colonnes/attributs issues de la source // Transformations à appliquer pour transformer des valeurs de colonnes/attributs issues de la source // en nouvelles valeurs de colonnes destination. // Tableau ordonné de services ou de classes implémentant obligatoirement l'interface // {@see \UnicaenDbImport\Filter\ColumnValue\ColumnValueFilterInterface}. // // Valeurs possibles pour chaque item : // - tableau associatif spécifiant : // - soit un service implémentant obligatoirement "\UnicaenDbImport\Filter\ColumnValue\ColumnValueFilterInterface" // (clé `name`) et ses paramètres de fonctionnement (clé `params`) ; // - soit un callable (clé `name`) et ses paramètres de fonctionnement (clé `params`). 'column_value_filter' => [ // ['name' => ToLowercaseColumnValueFilter::class, 'params' => ['column' => 'code']], // ['name' => SameColumnValueFilter::class, 'params' => ['column' => 'nomUsuel', 'source_column' => 'nomPatronymique']], ], // Spécification du filtre à utiliser pour transformer chaque nom de colonne/attribut source en // le nom de la colonne dans la table destination. Valeurs possibles : // - array : un filtre de type `\UnicaenDbImport\Filter\ColumnName\ArrayMapColumnNameFilter` // sera instancié avec ce tableau de correspondance comme mapping ; // - string : service ou classe de filtre à instancier, // implémentant {@see \UnicaenDbImport\Filter\ColumnName\ColumnNameFilterInterface} ; // NB : par défaut, `\UnicaenDbImport\Filter\ColumnName\DefaultColumnNameFilter::class` // est utilisé. // Transformations à appliquer sur les noms de colonnes/attributs source pour produire les noms de colonnes destination. // // Valeurs possibles : // - array : tableau de correspondance 'ancien nom' => 'nouveau nom' qui utilisé pour instancier un filtre de type // "\UnicaenDbImport\Filter\ColumnName\ArrayMapColumnNameFilter", exemple : // - ```php // 'column_name_filter' => [ // 'civilite' => 'civ', // 'nomUsuel' => 'lib_nom_usu_ind', // 'nomPatronymique' => 'lib_nom_pat_ind', // 'prenom1' => 'lib_pr1_ind', // ], // ``` // - string : service de filtre à instancier, implémentant "\UnicaenDbImport\Filter\ColumnName\ColumnNameFilterInterface" // // La valeur par défaut est `\UnicaenDbImport\Filter\ColumnName\DefaultColumnNameFilter::class`. 'column_name_filter' => \UnicaenDbImport\Filter\ColumnName\DefaultColumnNameFilter::class, // Nom de l'attribut ou colonne discriminant·e dans les données source, contenant l'identifiant unique. Loading @@ -219,11 +271,6 @@ return [ // Nom de la colonne dans la table destination contenant l'identifiant unique 'source_code_column' => 'CODE', // Filtres de transformation à appliquer aux valeurs des colonnes destination. // Tableau ordonné de services ou de classes implémentant obligatoirement l'interface // {@see \UnicaenDbImport\Filter\ColumnValue\ColumnValueFilterInterface}. 'column_value_filter' => [], // Stratégie à utiliser pour générer les valeurs dans la colonne ID (clé primaire). // Si la stratégie est `'SEQUENCE'`, la séquence spécifiée par 'id_sequence' sera utilisée. // Si la stratégie est `'IDENTITY'`, la valeur de l'ID des données sources sera utilisée. Loading Loading @@ -263,7 +310,7 @@ return [ 'connection' => 'apogee', // Select SQL de mise en forme des données source à importer (NB: antinomique avec 'table') 'select' => "SELECT COD_PAY, LIB_PAY, LIB_NAT FROM PAYS", 'select' => "select cod_pay, lib_pay, lib_nat from pays", // Nom de la table source contenant les données à importer //'table' => 'PAYS', // Équivalent de SELECT * FROM PAYS; Loading Loading @@ -437,7 +484,7 @@ return [ // Forçage éventuel du nom de la table intermédiaire utilisée lorsque source et destination // ne partagent pas la même connexion. NB: cette table intermédiaire est créée/peuplée/supprimée // dans la base de données destination à chaque import. // dans la base de données destination à chaque synchro. // En l'absence de ce forçage, le nom de la table intermédiaire sera celui de la table destination // préfixé par "tmp_". 'intermediate_table' => 'tmp_pays', Loading
config/unicaen-db-import.local.php.dist +5 −12 Original line number Diff line number Diff line Loading @@ -27,26 +27,19 @@ return [ 'user' => null, 'password' => null, // Implémentés, mais pas gérés par Guzzle : // Pagination (si supportée par l'API) : // - 'page_param' : nom du paramètre à utiliser pour demander une page de données précise // - 'page_size_param' : nom du paramètre à utiliser pour demander une taille de page précise /* 'page_param' => 'page', 'page_size_param' => 'page_size', */ //'page_param' => 'page', //'page_size_param' => 'page_size', ], ], ], /** * Configuration Doctrine minimum requise. * * NB: vous pouvez placer cette configuration autre part dans votre projet. */ 'doctrine' => [ 'connection' => [ 'orm_default' => [ 'driverClass' => 'Doctrine\\DBAL\\Driver\\PDOPgSql\\Driver', 'driverClass' => \Doctrine\DBAL\Driver\PDO\PgSQL\Driver::class, 'params' => [ 'host' => 'host.domain.fr', 'port' => '5432', Loading @@ -57,7 +50,7 @@ return [ ], ], 'orm_apogee' => [ 'driverClass' => 'Doctrine\\DBAL\\Driver\\OCI8\\Driver', 'driverClass' => \Doctrine\DBAL\Driver\OCI8\Driver::class, 'params' => [ 'host' => 'host.domain.fr', 'port' => '1527', Loading
documentation/fonctionnement_synchro_locale.drawio +53 −1 Original line number Diff line number Diff line <mxfile modified="2019-05-15T13:45:14.165Z" host="www.draw.io" agent="Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0" etag="7NW-oTR3yv0mZXlWeKPD" version="10.6.7" type="device"><diagram id="mWMuuFM-TLnJckGO8D4I" name="Page-1">3Vhbj5s4FP41PM4IcMjlMSVNq2pXu1Kk2faRgkPcOpg1zm1//R5jGwwmk0TNNFUz0sQ+vn/nnO+z46F4e/zAk3LzJ8sw9UI/O3po4YXhCI3gvzSclGHqI2XIOcmUKWgNK/If1kZfW3ckw1Wno2CMClJ2jSkrCpyKji3hnB263daMdlctkxw7hlWaUNf6D8nERp8i8lv7R0zyjVk58HXLNjGdtaHaJBk7WCb03kMxZ0yo0vYYYyqxM7ioccszrc3GOC7ENQNCNWCf0J0+24rteIr17sTJHBk2WspieqKkyDD30LvDhgi8KpNU2g/gaLBtxJZCLYDiV7aDjtkfXxtDkn7PubT+tRMwC9b2NaE0ZpTxeiUUx8tlHIPdPYs+3h5zgY+WSZ/tA2ZbLPgJuhxNYKkROs4CA/uh9dpYmzaWw8ywRMdJ3kzcQgkFjeYwsshB1gvHFBZ4l5E9FHNRH1GZ1gyOaOM9/nfHTMNTVSfAHDoEYXlsG80sC1wJ8mTmgo2p6bpLgHlg4bvvpUgEYcWNe3lYrC2XM/jcJ9bQA4MtGgi2Hqi4yOaS+iSuNKkqknYhxEciPlvlL1D2nyNdW8hD+qZyMpUC9vnZrlijZLUdVtfMuLNoV4p8bGoSCc+x7vXyInjxiUTkQMJpddp/Yqv1U6DFBGcd0nb9ZPkhGvCDsXFMIYb3Xaofco5e4W9G6oTRYTDpRkE067lXHVEPspm5N08jESacxr2JFDDORHWoNKe+KnomTvQsvBh585kA8ayz2c/Ieq1s4LMUtLcfXpAkohtQleDsOzbJVjCZg53806aEkryQUQnBUOe6TDkCYjvXDVuSZXKZQRLgKvMvBdb1aRzMph3cZ5GTxcFoIHzCO6Tx1HHEQB5TCtecc3BY+CdVqe4+a3KUAPW5bx3JP8dR0DKuP5rxLbv63AnmyeQSzOEboRy4l57b2dIwX3Ab8xmW9W9g2bNo9xjvBq60eRY9ki0R6rJcc8W/lS5R0KPLvqzejy5fRfb1+94LtF17H5OU+/z4G1POk4zgDg80N3TTtiBcKwUsBZfRixcthycG4vssdYBgPUcPu2oNe9+V0JgrvdT3YX+/cx9Vv5Jo/qBLxj2XTFyPTNHb8PmwR1wtDRz8fx0t/TH0/fEl7N9KSwexNxPf4znSiuMXq+XSc6TV4WbUxeeIo6YX3ievyoCtxI8V2N4zIuw/I64W2MkFpT4jsODl5GR1K2WH6vyGw6h7/w6R/+q+Rj3h7/WHgtrBXdW+ue+2ET4vSwoEbN5L8onkb0lVf9dKANvyvwHYv7EKzMKuK0YuEQXBz1QBQzyWn9BvqwKToKfBQ/jfSQig2v5IrbKq/aUfvf8f</diagram></mxfile> No newline at end of file <mxfile modified="2026-06-25T08:15:14.866Z" host="drawio-plugin" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" etag="dh87ylEmqqxMijT0wglW" version="22.1.22" type="embed"> <diagram id="mWMuuFM-TLnJckGO8D4I" name="Page-1"> <mxGraphModel dx="600" dy="548" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="2" value="Source" style="shape=cylinder;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;fillColor=#CCFFCC;rounded=0;" parent="1" vertex="1"> <mxGeometry x="80" y="120" width="60" height="80" as="geometry" /> </mxCell> <mxCell id="3" value="<div><font style="font-size: 12px">Desti-</font></div><div><font style="font-size: 12px">nation</font></div>" style="shape=cylinder;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;fillColor=#FF9999;rounded=0;" parent="1" vertex="1"> <mxGeometry x="380" y="120" width="60" height="80" as="geometry" /> </mxCell> <mxCell id="5" value="" style="endArrow=classic;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;dashed=1;strokeColor=default;shape=link;rounded=0;" parent="1" source="2" target="VVtrnJi5iwi28syvJoSf-14" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="70" y="590" as="sourcePoint" /> <mxPoint x="220" y="160" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="8" value="2" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;rounded=0;" parent="1" vertex="1"> <mxGeometry x="247.5" y="240" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="12" value="" style="endArrow=classic;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;dashed=1;strokeColor=default;shape=link;rounded=0;" parent="1" source="3" target="VVtrnJi5iwi28syvJoSf-14" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="330" y="210" as="sourcePoint" /> <mxPoint x="310" y="180" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="VVtrnJi5iwi28syvJoSf-14" value="<div>Vue</div><div>diff.</div>" style="shape=cylinder;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;gradientColor=#CCFFCC;gradientDirection=west;fillColor=#FF9999;rounded=0;" parent="1" vertex="1"> <mxGeometry x="227.5" y="120" width="60" height="80" as="geometry" /> </mxCell> <mxCell id="VVtrnJi5iwi28syvJoSf-17" value="Création vue différentielle V_DIFF_xxx" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="226.5" y="76" width="93.5" height="20" as="geometry" /> </mxCell> <mxCell id="VVtrnJi5iwi28syvJoSf-18" value="1" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;rounded=0;" parent="1" vertex="1"> <mxGeometry x="206" y="76" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="VVtrnJi5iwi28syvJoSf-20" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;rounded=0;" parent="1" source="VVtrnJi5iwi28syvJoSf-14" target="3" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="320" y="260" as="sourcePoint" /> <mxPoint x="370" y="210" as="targetPoint" /> <Array as="points"> <mxPoint x="258" y="230" /> <mxPoint x="410" y="230" /> </Array> </mxGeometry> </mxCell> <mxCell id="VVtrnJi5iwi28syvJoSf-21" value="Application des mises à jour" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="270" y="240" width="170" height="20" as="geometry" /> </mxCell> </root> </mxGraphModel> </diagram> </mxfile>