Add enhanced data synchronization functions and improve user feedback
This commit introduces new functions for confirming database names during synchronization, enhancing safety when syncing to production environments. The `fxStaticSyncResolveDataStatus` and `fxStaticSyncFormatDataDiffSummary` functions are added to provide clearer status reporting and summaries of data discrepancies. Additionally, the synchronization process is refined to improve user feedback, including clearer messages regarding data alignment and discrepancies. These changes aim to enhance the robustness and clarity of the database synchronization workflow.
This commit is contained in:
@ -249,6 +249,52 @@ function fxStaticSyncWriteAllowed($strEnvId) {
|
||||
return $strEnvId !== 'client_prod';
|
||||
}
|
||||
|
||||
/** Environnements client : confirmation renforcée (nom de BD à retaper). */
|
||||
function fxStaticSyncRequiresDbConfirm($strEnvId) {
|
||||
return ($strEnvId === 'client_preprod' || $strEnvId === 'client_prod');
|
||||
}
|
||||
|
||||
function fxStaticSyncResolveDataStatus(array $tabCmp) {
|
||||
$intMissing = count($tabCmp['missing_keys']);
|
||||
$intExtra = count($tabCmp['extra_keys']);
|
||||
$intDiff = count($tabCmp['diff_keys']);
|
||||
|
||||
if ($intMissing === 0 && $intExtra === 0 && $intDiff === 0) {
|
||||
return 'ok';
|
||||
}
|
||||
if ($intMissing > 0 && $intExtra > 0) {
|
||||
return 'mixed';
|
||||
}
|
||||
if ($intExtra > 0) {
|
||||
return 'extra_rows';
|
||||
}
|
||||
if ($intMissing > 0) {
|
||||
return 'missing_rows';
|
||||
}
|
||||
|
||||
return 'content_diff';
|
||||
}
|
||||
|
||||
/** Résumé lisible des écarts de données (toujours affiché quand status !== ok). */
|
||||
function fxStaticSyncFormatDataDiffSummary(array $tabCmp) {
|
||||
$tabParts = array();
|
||||
$intMissing = count($tabCmp['missing_keys']);
|
||||
$intExtra = count($tabCmp['extra_keys']);
|
||||
$intDiff = count($tabCmp['diff_keys']);
|
||||
|
||||
if ($intMissing > 0) {
|
||||
$tabParts[] = 'manque ' . $intMissing;
|
||||
}
|
||||
if ($intExtra > 0) {
|
||||
$tabParts[] = 'en trop ' . $intExtra;
|
||||
}
|
||||
if ($intDiff > 0) {
|
||||
$tabParts[] = 'contenu diff. ' . $intDiff;
|
||||
}
|
||||
|
||||
return implode(' · ', $tabParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array status: ok|content_diff|missing_rows|extra_rows|missing_table|schema_mismatch|no_connection|error
|
||||
*/
|
||||
@ -287,14 +333,11 @@ function fxStaticSyncCompareTable(array $tabSourceData, array $tabTargetData, ar
|
||||
}
|
||||
}
|
||||
|
||||
$strStatus = 'ok';
|
||||
if (count($tabExtra) > 0) {
|
||||
$strStatus = 'extra_rows';
|
||||
} elseif (count($tabMissing) > 0) {
|
||||
$strStatus = 'missing_rows';
|
||||
} elseif (count($tabDiff) > 0) {
|
||||
$strStatus = 'content_diff';
|
||||
}
|
||||
$strStatus = fxStaticSyncResolveDataStatus(array(
|
||||
'missing_keys' => $tabMissing,
|
||||
'extra_keys' => $tabExtra,
|
||||
'diff_keys' => $tabDiff,
|
||||
));
|
||||
|
||||
return array(
|
||||
'status' => $strStatus,
|
||||
@ -514,16 +557,26 @@ function fxStaticSyncCopyTable($objSource, $objTarget, $strTable, array $tabDef
|
||||
);
|
||||
}
|
||||
|
||||
function fxStaticSyncStatusLabel($strStatus) {
|
||||
function fxStaticSyncStatusLabel($strStatus, array $tabCmp = null) {
|
||||
if ($strStatus === 'ok') {
|
||||
return 'Données alignées';
|
||||
}
|
||||
if (is_array($tabCmp) && $strStatus !== 'schema_mismatch' && $strStatus !== 'missing_table' && $strStatus !== 'no_connection') {
|
||||
$strSummary = fxStaticSyncFormatDataDiffSummary($tabCmp);
|
||||
if ($strSummary !== '') {
|
||||
return 'Écarts : ' . $strSummary;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($strStatus) {
|
||||
case 'ok':
|
||||
return 'Données alignées';
|
||||
case 'content_diff':
|
||||
return 'Contenu différent';
|
||||
case 'missing_rows':
|
||||
return 'Lignes manquantes';
|
||||
case 'extra_rows':
|
||||
return 'Lignes en trop';
|
||||
case 'mixed':
|
||||
return 'Manque et en trop';
|
||||
case 'missing_table':
|
||||
return 'Table absente';
|
||||
case 'schema_mismatch':
|
||||
@ -541,6 +594,7 @@ function fxStaticSyncStatusClass($strStatus) {
|
||||
return 'ss-ok';
|
||||
case 'content_diff':
|
||||
case 'missing_rows':
|
||||
case 'mixed':
|
||||
return 'ss-warn';
|
||||
case 'extra_rows':
|
||||
return 'ss-bad';
|
||||
|
||||
@ -42,7 +42,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
|
||||
} elseif (!fxStaticSyncWriteAllowed($strEnvId)) {
|
||||
$strFlash = 'Sync désactivée vers client prod — comparaison seule.';
|
||||
$strFlashType = 'bad';
|
||||
} elseif (fxStaticSyncRequiresDbConfirm($strEnvId)) {
|
||||
$strConfirmDb = trim($_POST['confirm_db'] ?? '');
|
||||
$strExpectedDb = $tabEnv['config']['db'] ?? '';
|
||||
if ($strConfirmDb !== $strExpectedDb) {
|
||||
$strFlash = 'Sync annulée — nom de base incorrect (attendu : ' . $strExpectedDb . ').';
|
||||
$strFlashType = 'bad';
|
||||
} else {
|
||||
$blnDbConfirmed = true;
|
||||
}
|
||||
} else {
|
||||
$blnDbConfirmed = true;
|
||||
}
|
||||
|
||||
if (!empty($blnDbConfirmed)) {
|
||||
$tabConnections = fxStaticSyncOpenConnections();
|
||||
$objSource = $tabConnections['dev_preprod'];
|
||||
$objTarget = $tabConnections[$strEnvId];
|
||||
@ -59,8 +72,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
|
||||
$strFlash .= ' Vérification : aligné.';
|
||||
$strFlashType = 'ok';
|
||||
} else {
|
||||
$strFlash .= ' ATTENTION — après sync, statut : '
|
||||
. fxStaticSyncStatusLabel($tabVerify['status']) . '.';
|
||||
$strFlash .= ' ATTENTION — après sync : '
|
||||
. fxStaticSyncStatusLabel($tabVerify['status'], $tabVerify) . '.';
|
||||
$strFlashType = 'bad';
|
||||
}
|
||||
} else {
|
||||
@ -108,8 +121,9 @@ if ($strDetailTable !== '' && $strDetailEnv !== '') {
|
||||
.ss-bad { background: #f8d7da; color: #721c24; }
|
||||
.ss-muted { background: #e9ecef; color: #495057; }
|
||||
.ss-schema { background: #fde2e2; color: #7f1d1d; border: 1px solid #f5c2c2; }
|
||||
.badge { display: inline-block; padding: 3px 8px; border-radius: 4px; font-size: 0.82rem; font-weight: 600; }
|
||||
.badge { display: inline-block; padding: 3px 8px; border-radius: 4px; font-size: 0.82rem; font-weight: 600; max-width: 100%; line-height: 1.35; }
|
||||
.count { font-size: 0.82rem; color: #444; margin-top: 4px; }
|
||||
.count-hint { font-size: 0.78rem; color: #666; }
|
||||
.btn { display: inline-block; margin-top: 6px; padding: 4px 10px; font-size: 0.82rem; border: 1px solid #888; background: #fff; border-radius: 4px; cursor: pointer; text-decoration: none; color: #222; }
|
||||
.btn:hover { background: #f0f0f0; }
|
||||
.btn-sync { border-color: #2e6da4; color: #1a4f7a; }
|
||||
@ -128,8 +142,9 @@ if ($strDetailTable !== '' && $strDetailEnv !== '') {
|
||||
<div class="warn-box">
|
||||
<strong>Attention — opération destructive.</strong>
|
||||
Sync = <code>DELETE</code> puis recopie complète depuis dev préprod.
|
||||
Ne pas utiliser vers client prod (bouton désactivé).
|
||||
Si le statut indique <em>Structures non alignées</em>, aligner les structures via Navicat — aucune sync possible tant que ce n'est pas réglé.
|
||||
<strong>Client prod : lecture seule</strong> (aucun bouton Sync).
|
||||
Client préprod : confirmation avec le nom exact de la base.
|
||||
Structures non alignées : corriger via Navicat avant toute sync.
|
||||
</div>
|
||||
<p class="sub">
|
||||
Source de vérité : <strong>dev préprod</strong> (<code><?php echo ssEsc($arrConDatabasePreProd['db'] ?? ''); ?></code>).
|
||||
@ -142,11 +157,14 @@ if ($strDetailTable !== '' && $strDetailEnv !== '') {
|
||||
|
||||
<div class="legend">
|
||||
<span class="ss-ok">Données alignées</span>
|
||||
<span class="ss-warn">Manque ou contenu différent</span>
|
||||
<span class="ss-bad">Lignes en trop</span>
|
||||
<span class="ss-warn">Écarts (manque / en trop / contenu)</span>
|
||||
<span class="ss-schema">Structures non alignées</span>
|
||||
<span class="ss-muted">Table absente / connexion</span>
|
||||
</div>
|
||||
<p class="count-hint" style="margin:-8px 0 16px;">
|
||||
Compteur : <strong>cible · source</strong> (lignes dans l'environnement comparé · lignes en dev préprod).
|
||||
Les écarts détaillent combien de clés manquent, sont en trop ou diffèrent — indépendamment du total.
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
@ -181,12 +199,13 @@ if ($strDetailTable !== '' && $strDetailEnv !== '') {
|
||||
continue;
|
||||
}
|
||||
$strClass = fxStaticSyncStatusClass($tabCmp['status']);
|
||||
$strLabel = fxStaticSyncStatusLabel($tabCmp['status']);
|
||||
$strLabel = fxStaticSyncStatusLabel($tabCmp['status'], $tabCmp);
|
||||
$strDbName = $tabEnv['config']['db'] ?? '';
|
||||
?>
|
||||
<td class="<?php echo ssEsc($strClass); ?>">
|
||||
<span class="badge"><?php echo ssEsc($strLabel); ?></span>
|
||||
<div class="count">
|
||||
<?php echo (int)$tabCmp['target_count']; ?> / <?php echo (int)$tabCmp['source_count']; ?> ligne(s)
|
||||
Cible <?php echo (int)$tabCmp['target_count']; ?> · Source <?php echo (int)$tabCmp['source_count']; ?>
|
||||
</div>
|
||||
<?php if ($tabCmp['status'] === 'schema_mismatch') {
|
||||
$tabSchemaDiff = array(
|
||||
@ -201,13 +220,20 @@ if ($strDetailTable !== '' && $strDetailEnv !== '') {
|
||||
<a class="btn" href="?detail_table=<?php echo urlencode($tabDef['table']); ?>&detail_env=<?php echo urlencode($strEnvId); ?>">Détails</a>
|
||||
<?php } ?>
|
||||
<?php if ($tabCmp['status'] !== 'no_connection' && $tabCmp['status'] !== 'schema_mismatch' && fxStaticSyncWriteAllowed($strEnvId)) { ?>
|
||||
<form method="post" style="display:inline;" onsubmit="return confirm('DESTRUCTIF : effacer <?php echo ssEsc($tabDef['table']); ?> sur <?php echo ssEsc($tabEnv['label']); ?> (<?php echo ssEsc($tabEnv['config']['db'] ?? ''); ?>) et recopier depuis dev préprod ?');">
|
||||
<form method="post" style="display:inline;" class="ss-sync-form"
|
||||
data-table="<?php echo ssEsc($tabDef['table']); ?>"
|
||||
data-label="<?php echo ssEsc($tabEnv['label']); ?>"
|
||||
data-db="<?php echo ssEsc($strDbName); ?>"
|
||||
data-env="<?php echo ssEsc($strEnvId); ?>"
|
||||
data-confirm-db="<?php echo fxStaticSyncRequiresDbConfirm($strEnvId) ? '1' : '0'; ?>">
|
||||
<input type="hidden" name="action" value="sync">
|
||||
<input type="hidden" name="csrf" value="<?php echo ssEsc($strCsrf); ?>">
|
||||
<input type="hidden" name="table" value="<?php echo ssEsc($tabDef['table']); ?>">
|
||||
<input type="hidden" name="env" value="<?php echo ssEsc($strEnvId); ?>">
|
||||
<button type="submit" class="btn btn-sync">Sync →</button>
|
||||
</form>
|
||||
<?php } elseif (!fxStaticSyncWriteAllowed($strEnvId)) { ?>
|
||||
<div class="count-hint">Lecture seule</div>
|
||||
<?php } ?>
|
||||
</td>
|
||||
<?php } ?>
|
||||
@ -243,7 +269,10 @@ if ($strDetailTable !== '' && $strDetailEnv !== '') {
|
||||
<?php foreach ($tabDetail['samples'] as $tabSample) { ?>
|
||||
<li>
|
||||
<strong><?php echo ssEsc(fxStaticSyncFormatKeyPreview($tabSample['key'])); ?></strong>
|
||||
(<?php echo ssEsc($tabSample['type']); ?>)
|
||||
(<?php
|
||||
echo ssEsc($tabSample['type'] === 'extra' ? 'en trop'
|
||||
: ($tabSample['type'] === 'missing' ? 'manque' : 'contenu diff.'));
|
||||
?>)
|
||||
<?php if ($tabDef['table'] === 'info' && $tabSample['type'] === 'diff') { ?>
|
||||
<br>Source : <em><?php echo ssEsc($tabSample['source_texte']); ?></em>
|
||||
<br>Cible : <em><?php echo ssEsc($tabSample['target_texte']); ?></em>
|
||||
@ -258,4 +287,43 @@ if ($strDetailTable !== '' && $strDetailEnv !== '') {
|
||||
<?php } ?>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
(function () {
|
||||
document.querySelectorAll('.ss-sync-form').forEach(function (form) {
|
||||
form.addEventListener('submit', function (e) {
|
||||
var table = form.getAttribute('data-table');
|
||||
var label = form.getAttribute('data-label');
|
||||
var db = form.getAttribute('data-db');
|
||||
var env = form.getAttribute('data-env');
|
||||
var needDb = form.getAttribute('data-confirm-db') === '1';
|
||||
|
||||
var msg = 'DESTRUCTIF : effacer la table « ' + table + ' » sur '
|
||||
+ label + ' (' + db + ') et recopier depuis dev préprod ?';
|
||||
if (!window.confirm(msg)) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
if (needDb) {
|
||||
var typed = window.prompt(
|
||||
'Environnement client — tapez le nom EXACT de la base cible pour confirmer :',
|
||||
''
|
||||
);
|
||||
if (typed !== db) {
|
||||
window.alert('Nom de base incorrect. Sync annulée.');
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
var input = form.querySelector('input[name="confirm_db"]');
|
||||
if (!input) {
|
||||
input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'confirm_db';
|
||||
form.appendChild(input);
|
||||
}
|
||||
input.value = typed;
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</html>
|
||||
|
||||
19
sql/MSIN-eve-acces-v2-drop-foreign-keys.sql
Normal file
19
sql/MSIN-eve-acces-v2-drop-foreign-keys.sql
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
MSIN — Retrait FK acces v2
|
||||
Base : devinscription_preprod UNIQUEMENT
|
||||
Aucune donnee touchee.
|
||||
*/
|
||||
|
||||
USE devinscription_preprod;
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
ALTER TABLE `inscriptions_eve_role_permissions`
|
||||
DROP FOREIGN KEY `fk_erp_role`,
|
||||
DROP FOREIGN KEY `fk_erp_perm`;
|
||||
|
||||
ALTER TABLE `inscriptions_eve_acces`
|
||||
DROP FOREIGN KEY `fk_ea_role`;
|
||||
|
||||
ALTER TABLE `inscriptions_eve_acces_extra`
|
||||
DROP FOREIGN KEY `fk_ae_perm`;
|
||||
Reference in New Issue
Block a user