Files
HouseHub/migrate.php
T
FeFeClochette f5c5320189
Deploy HouseHub / deploy (push) Successful in 2s
auto increment
2026-06-19 11:58:43 +02:00

182 lines
8.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Script de migration global HouseHub OS (Multi-tenant)
* Auto-Sync dynamique basé sur schema_family.sql + Migration des données
* À exécuter via le navigateur : http://localhost:8083/migrate.php
*/
require_once __DIR__ . '/includes/meta_db.php';
$db_host = getenv('DB_HOST') ?: '127.0.0.1';
$db_user = getenv('DB_USER') ?: 'househub';
$db_pass = getenv('DB_PASS') ?: 'househub_dev';
echo "<h1>🚀 Début de la migration Multi-Tenant (Auto-Sync)</h1>";
// ---------------------------------------------------------
// 1. LECTURE ET PARSING DU FICHIER SCHEMA_FAMILY.SQL
// ---------------------------------------------------------
$schemaPath = __DIR__ . '/schema_family.sql'; // Modifie si rangé dans /docker/
if (!file_exists($schemaPath)) {
$schemaPath = __DIR__ . '/docker/schema_family.sql';
}
if (!file_exists($schemaPath)) {
die("❌ Impossible de trouver le fichier schema_family.sql");
}
$sqlContent = file_get_contents($schemaPath);
/**
* Analyse le code SQL pour en extraire la structure [Table => [Colonnes]]
*/
function parseExpectedSchema($sql) {
$schema = [];
// Récupère tout ce qui se trouve entre CREATE TABLE (...) ENGINE
preg_match_all('/CREATE TABLE (?:IF NOT EXISTS )?`?([a-zA-Z0-9_]+)`?\s*\((.*?)\)\s*ENGINE/si', $sql, $tableMatches, PREG_SET_ORDER);
foreach($tableMatches as $match) {
$tableName = $match[1];
$body = $match[2];
// Sépare les lignes par virgule, en ignorant les virgules entre parenthèses (ex: DECIMAL(10,2) ou ENUM('a','b'))
$lines = preg_split('/,(?![^\(]*\))/', $body);
$columns = [];
foreach($lines as $line) {
$line = trim($line);
if (empty($line)) continue;
// On ignore la déclaration des clés, contraintes et index
if (preg_match('/^(PRIMARY KEY|UNIQUE KEY|FOREIGN KEY|KEY|INDEX|FULLTEXT KEY|CONSTRAINT)\b/i', $line)) {
continue;
}
// Extrait le nom de la colonne et sa définition SQL
if (preg_match('/^`?([a-zA-Z0-9_]+)`?\s+(.*)$/i', $line, $colMatch)) {
$colName = $colMatch[1];
$colDef = rtrim($colMatch[2], ',');
$columns[$colName] = $colDef;
}
}
$schema[$tableName] = $columns;
}
return $schema;
}
$expectedSchema = parseExpectedSchema($sqlContent);
echo "️ Modèle SQL chargé et analysé : <b>" . count($expectedSchema) . " tables</b> détectées.<hr>";
try {
$stmt = $meta_pdo->query("SELECT db_name, name FROM families WHERE is_active = 1");
$families = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($families as $family) {
$dbName = $family['db_name'];
echo "<h2>🏡 Famille : {$family['name']} ($dbName)</h2>";
try {
$pdo = new PDO(
"mysql:host=$db_host;dbname=$dbName;charset=utf8mb4",
$db_user, $db_pass,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
// ---------------------------------------------------------
// 2. EXÉCUTION DU SQL (CRÉATION TABLES MANQUANTES + DONNÉES PAR DÉFAUT)
// ---------------------------------------------------------
try {
$pdo->exec($sqlContent);
echo "✅ Structure de base validée (les tables manquantes ont été créées).<br>";
} catch (PDOException $e) {
echo "⚠️ Avertissement SQL brut : " . $e->getMessage() . "<br>";
}
// ---------------------------------------------------------
// 3. DIFFING (AJOUT DYNAMIQUE DES COLONNES MANQUANTES)
// ---------------------------------------------------------
$colsAdded = 0;
foreach ($expectedSchema as $table => $expectedCols) {
$stmtCol = $pdo->query("SHOW COLUMNS FROM `$table`");
$existingCols = $stmtCol->fetchAll(PDO::FETCH_COLUMN);
foreach ($expectedCols as $colName => $colDef) {
if (!in_array($colName, $existingCols)) {
$pdo->exec("ALTER TABLE `$table` ADD COLUMN `$colName` $colDef");
echo " Nouvelle colonne ajoutée : <b>$table.$colName</b><br>";
$colsAdded++;
}
}
}
if ($colsAdded === 0) {
echo "✅ Toutes les colonnes de toutes les tables sont déjà à jour.<br>";
}
// ---------------------------------------------------------
// 4. MIGRATION DES DONNÉES (TRANSFORMATIONS HISTORIQUES)
// ---------------------------------------------------------
echo "<i>🔄 Exécution des transformations de données héritées...</i><br>";
$pdo->exec("ALTER TABLE pf_events MODIFY id INT NOT NULL AUTO_INCREMENT");
$pdo->exec("ALTER TABLE pf_leaves MODIFY id INT NOT NULL AUTO_INCREMENT");
// A. Changement de types spécifiques (Car le parseur n'ajoute que les colonnes manquantes)
$pdo->exec("ALTER TABLE pf_budget_items MODIFY category VARCHAR(100) DEFAULT NULL");
// B. Migration de l'historique calendrier (Enfants & Helpers)
$helperId = $pdo->query("SELECT id FROM pf_people WHERE role = 'helper' LIMIT 1")->fetchColumn();
if ($helperId) {
$pdo->exec("UPDATE pf_events SET event_type = 'HELPER_OFF', person_id = $helperId WHERE event_type = 'OFF_CAROLE'");
$pdo->exec("UPDATE pf_events SET event_type = 'HELPER_EXTRA', person_id = $helperId WHERE event_type = 'EXTRA_OFF_CAROLE'");
}
$kidId = $pdo->query("SELECT id FROM pf_people WHERE role IN ('child', 'enfant') ORDER BY id ASC LIMIT 1")->fetchColumn();
if ($kidId) {
$pdo->exec("UPDATE pf_events SET event_type = 'CHILD_SICK', person_id = $kidId WHERE event_type = 'PEP_SICK'");
}
// C. Mapping Intelligent du Budget (is_estimate)
$itemsMigres = 0;
$itemsMigres += $pdo->exec("UPDATE pf_budget_items SET category = 'FIXED' WHERE category = 'expense' AND is_estimate = 0");
$itemsMigres += $pdo->exec("UPDATE pf_budget_items SET category = 'FUEL' WHERE category = 'expense' AND is_estimate = 1 AND (mapping_keywords LIKE '%ESSENCE%' OR name LIKE '%gasolina%')");
$itemsMigres += $pdo->exec("UPDATE pf_budget_items SET category = 'SCHOOL' WHERE category = 'expense' AND is_estimate = 1 AND (mapping_keywords LIKE '%ESCOLA%' OR mapping_keywords LIKE '%PARASCOL%' OR name LIKE '%escola%')");
$itemsMigres += $pdo->exec("UPDATE pf_budget_items SET category = 'FMCG' WHERE category = 'expense' AND is_estimate = 1 AND (mapping_keywords LIKE '%FMCG%' OR name LIKE '%F&B%')");
$itemsMigres += $pdo->exec("UPDATE pf_budget_items SET category = 'AUTRES' WHERE category = 'expense'");
$itemsMigres += $pdo->exec("UPDATE pf_budget_items SET category = 'INCOME' WHERE category = 'income'");
// D. Nettoyage des virgules orphelines dans mapping_keywords
$budgetCodes = ['INCOME', 'FMCG', 'FUEL', 'SCHOOL', 'HEALTH', 'FIXED', 'SAVINGS', 'AUTRES'];
foreach ($budgetCodes as $code) {
$pdo->exec("UPDATE pf_budget_items SET mapping_keywords = REPLACE(mapping_keywords, '$code', '') WHERE mapping_keywords LIKE '%$code%'");
}
$pdo->exec("UPDATE pf_budget_items SET mapping_keywords = TRIM(BOTH ',' FROM REPLACE(REPLACE(mapping_keywords, ' ', ''), ',,', ','))");
// E. Migration de l'historique des dépenses et des règles d'import
$budgetMapping = [
'Income' => 'INCOME',
'FMCG' => 'FMCG',
'Essence' => 'FUEL',
'School' => 'SCHOOL',
'Frais' => 'FIXED',
'LivretA' => 'SAVINGS',
'Autres' => 'AUTRES'
];
$stmtExp = $pdo->prepare("UPDATE pf_expenses SET category = ? WHERE category = ?");
$stmtRules = $pdo->prepare("UPDATE pf_import_rules SET category = ? WHERE category = ?");
foreach ($budgetMapping as $old => $newCode) {
$stmtExp->execute([$newCode, $old]);
$stmtRules->execute([$newCode, $old]);
}
echo "✅ Transformations Data terminées ($itemsMigres prévisions remappées).<hr>";
} catch (PDOException $e) {
echo "❌ Erreur sur la base $dbName : " . $e->getMessage() . "<hr>";
}
} // FIN DE LA BOUCLE FOREACH FAMILLES
echo "<h1>🎉 Synchronisation et Migration terminées avec succès !</h1>";
} catch (Exception $e) {
die("❌ Erreur fatale Meta DB : " . $e->getMessage());
}
?>