* @author Jean-Pascal MILCENT * @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org) * @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL * @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL * @version $Id$ */ class MigrationMotsCles { const SEPARATEUR_MOT_CLE_TEXTE = ','; const truncate = true; //Doit on vider les tables de destination ? const dry_run = false; private $bdd = null; private $script = null; private $nouvellesTables = array('cel_mots_cles_obs', 'cel_mots_cles_images', 'cel_images_mots_cles', 'cel_obs_mots_cles'); public static $bdd_cel_migration; public static $bdd_utilisateurs; private $tableau_utilisateurs = array(); /** Tableau associatif permettant de stocker l'avancement dans une boucle. * La clé est un md5 du message à afficher au démarrage de la boucle. * @var array */ private static $avancement = array(); public function __construct(Conteneur $conteneur) { $bddMigration = $conteneur->getParametre('database_cel.database_migration'); if ($bddMigration == null || $bddMigration == '') { echo 'Attention la variable de configuration database_migration dans la section database_cel, contenant la base de données d\'arrivée, doit être remplie '."\n"; exit; } $bddIdentification = $conteneur->getParametre('database_ident.database'); if ($bddIdentification == null || $bddIdentification == '') { echo 'Attention la variable de configuration database dans la section database_ident, contenant la base de données utilisateurs, doit être remplie '."\n"; exit; } self::$bdd_cel_migration = $conteneur->getParametre('database_cel.database_migration'); self::$bdd_utilisateurs = $conteneur->getParametre('database_ident.database'); $this->bdd = $conteneur->getBdd(); $this->script = $conteneur->getScript(); } /** * Méthode appelée pour executer le script. */ public function executer($params) { echo "--MIGRATION DES MOTS CLES --------------------------------------\n"; if (self::truncate) { echo "-------------------------------------------------------------------\n"; echo " ETAPE 0. Vider les tables ... \n"; echo "-------------------------------------------------------------------\n"; $this->viderTables(); } echo "-------------------------------------------------------------------\n"; echo " ETAPE 1. Paramétrage ... \n"; echo "-------------------------------------------------------------------\n"; $this->getUtilisateurs(); echo "-------------------------------------------------------------------\n"; echo " ETAPE 2. Migration des mots clés ... \n"; echo "-------------------------------------------------------------------\n"; $this->migrerTableMotsClesObs(); $this->migrerTableMotsClesImages(); echo "-------------------------------------------------------------------\n"; echo " ETAPE 3. Migration des liaisons mots clés ... \n"; echo "-------------------------------------------------------------------\n"; $this->migrerLiaisonsMotsClesObs(); $this->migrerLiaisonsMotsClesImages(); echo "-------------------------------------------------------------------\n"; echo " ETAPE 4. Génération des index des mots clés ... \n"; echo "-------------------------------------------------------------------\n"; $this->genererIndexTexteMotsClesObs(); $this->genererIndexTexteMotsClesImages(); $this->mettreANullMotsClesTxtVide(); } private function viderTables() { foreach ($this->nouvellesTables as $nomTable) { echo 'Vider la table '.$nomTable.'...'; $requete = 'TRUNCATE TABLE '.self::$bdd_cel_migration.'.'.$nomTable; $resultat = $this->bdd->executer($requete); echo "ok \n"; } } private function executerRequeteSimple($requete) { // Fonction de commodité pour afficher les requetes au lieu de les executer if (self::dry_run) { echo str_replace('),','),'."\n", $requete); return true; } else { return $this->bdd->executer($requete); } } private function getUtilisateurs() { echo "SELECTION DES UTILISATEURS\n"; $requete = 'SELECT U_ID as id, U_MAIL as mail, U_NAME as nom, U_SURNAME as prenom, U_PASSWD as pass '. 'FROM '.self::$bdd_utilisateurs.'.annuaire_tela'; $tableau_utilisateurs = $this->bdd->requeter($requete); foreach( $tableau_utilisateurs as &$utilisateur) { $this->tableau_utilisateurs[$utilisateur['mail']] = $utilisateur; } echo count($this->tableau_utilisateurs)." utilisateurs sélectionnés \n"; } private function migrerTableMotsClesObs() { $this->migrerTableMotsCles('obs'); } private function migrerTableMotsClesImages() { $this->migrerTableMotsCles('images'); } private function migrerTableMotsCles($image_ou_obs) { echo "MIGRATION DES MOTS CLES $image_ou_obs\n"; $pas = 1; //limite des mots clés $requeteNbMotsCles = 'SELECT count(*) as nb FROM cel_mots_cles_'.$image_ou_obs; $nbMotsCles = (int) $this->bdd->requeter($requeteNbMotsCles, Bdd::SQL_RETOUR_COLONNE); for ($i = 0; $i < $nbMotsCles; $i += $pas) { $requete = 'SELECT * '. 'FROM cel_mots_cles_'.$image_ou_obs.' '. 'ORDER BY cmc_niveau '. "LIMIT $i,$pas "; $arbres_mots_cles = $this->bdd->requeter($requete); if (count($arbres_mots_cles) > 0) { $champ_parent = ($image_ou_obs == "obs") ? 'ce_mot_cle_obs_parent' : 'ce_mot_cle_image_parent'; $champ_id = ($image_ou_obs == "obs") ? 'id_mot_cle_obs' : 'id_mot_cle_image'; $requete = 'INSERT INTO '.self::$bdd_cel_migration.".cel_mots_cles_$image_ou_obs ". "($champ_id, id_utilisateur, mot_cle, md5, bg, bd, niveau, $champ_parent) ". 'VALUES '; $sous_requete = array(); foreach ($arbres_mots_cles as $arbre_mot_cle) { $sous_requete[] = $this->construireSousRequeteInsertionArbresMotsCles('cmc_', $arbre_mot_cle); } $sous_requete = implode(',', $sous_requete); $requete .= $sous_requete; $migration = $this->executerRequeteSimple($requete); if (!$migration) { echo "La migration des mots cles $image_ou_obs a échoué ! "."\n"; } else { $this->script->afficherAvancement("Migration des mots clés $image_ou_obs (par $pas)"); } } } echo "\n"; } private function construireSousRequeteInsertionArbresMotsCles($prefixe, $ligne) { $id_proprietaire_mot_cle = $ligne[$prefixe.'id_proprietaire']; $id_proprietaire_mot_cle = $this->renvoyerIdPourMigration($id_proprietaire_mot_cle); $sous_requete = '('.$this->bdd->proteger($ligne[$prefixe.'id_mot_cle_utilisateur']).','. $this->bdd->proteger($id_proprietaire_mot_cle).','. $this->bdd->proteger($ligne[$prefixe.'mot_cle']).','. $this->bdd->proteger($ligne[$prefixe.'id_mot_cle_general']).','. $this->bdd->proteger($ligne[$prefixe.'bg']).','. $this->bdd->proteger($ligne[$prefixe.'bd']).','. $this->bdd->proteger($ligne[$prefixe.'niveau']).','. $this->bdd->proteger($ligne[$prefixe.'id_parent']). ')'; return $sous_requete; } private function renvoyerIdPourMigration($utilisateur) { // si tout les test suivant échouent, on garde l'utilisateur tel quel // (cas de la chaine de session des utilisateur anonymes) $retour = $utilisateur; // si le mail correspond a un utilisateur de la bdd if (isset($this->tableau_utilisateurs[$utilisateur])) { // on renvoie son id $retour = $this->tableau_utilisateurs[$utilisateur]['id']; } else { // sinon si c'est un mail inconnu, on garde le md5 if ($this->mailValide($utilisateur)) { $retour = md5($utilisateur); } } return $retour; } private function mailValide($mail) { // vérification bidon mais ça suffit pour ici return !(strpos($mail, '@') === false); } private function migrerLiaisonsMotsClesObs() { $requete = 'SELECT mots_cles AS mots_cles, id AS id, identifiant AS id_utilisateur '. 'FROM cel_inventory '. 'WHERE mots_cles != "" '. ' AND mots_cles != "NULL" '. ' AND mots_cles != "null" '. ' AND mots_cles IS NOT NULL '. 'ORDER BY identifiant '; $mots_cles_obs = $this->bdd->requeter($requete); $requete = 'INSERT INTO '.self::$bdd_cel_migration.'.cel_obs_mots_cles '. '(id_observation, id_mot_cle_obs, id_utilisateur) '. 'VALUES '. $this->construireSousRequeteInsertionLiaisons($mots_cles_obs, ';'); $insertion = $this->executerRequeteSimple($requete); if (!$insertion) { echo 'La migration des mots cles obs a échoué ! '."\n"; } else { echo "Migration des mots cles obs : OK\n"; } } private function migrerLiaisonsMotsClesImages() { $requete = 'SELECT ci_meta_mots_cles AS mots_cles, ci_id_image AS id, ci_ce_utilisateur AS id_utilisateur '. 'FROM cel_images '. 'WHERE ci_meta_mots_cles != "" '. ' AND ci_meta_mots_cles != "NULL" '. ' AND ci_meta_mots_cles != "null" '. ' AND ci_meta_mots_cles IS NOT NULL '. 'ORDER BY ci_ce_utilisateur'; $mots_cles_images = $this->bdd->requeter($requete); $requete = 'INSERT INTO '.self::$bdd_cel_migration.'.cel_images_mots_cles '. '(id_image, id_mot_cle_image, id_utilisateur) '. 'VALUES '. $this->construireSousRequeteInsertionLiaisons($mots_cles_images, ','); $insertion = $this->executerRequeteSimple($requete); if (!$insertion) { echo 'La migration des mots cles images a échoué ! '."\n"; } else { echo "Migration des mots cles images : OK\n"; } } private function construireSousRequeteInsertionLiaisons($tableau_mots_cles, $separateur) { $sous_requete = array(); foreach ($tableau_mots_cles as $element) { $mots_cles_ids = $this->parserMotsCles($element['mots_cles'], $separateur); foreach ($mots_cles_ids as $mot_cle_id) { $id = $this->bdd->proteger($element['id']); $id_mot_cle = $this->bdd->proteger($mot_cle_id); $id_utilisateur = $this->bdd->proteger($this->renvoyerIdPourMigration($element['id_utilisateur'])); $sous_requete[] = "($id, $id_mot_cle, $id_utilisateur)"; } } $sous_requete_chaine = implode(',', $sous_requete); return $sous_requete_chaine; } private function parserMotsCles($mot_cles, $separateur = ',') { $mot_cles = trim($mot_cles, $separateur); $tableau_mots_cles = explode($separateur, $mot_cles); $tableau_mots_cles_formates = array(); foreach ($tableau_mots_cles as $mot_cle) { $mot_cle = str_replace($separateur.$separateur, '', $mot_cle); $mot_cle = str_replace('null', '', $mot_cle); $mot_cle = trim($mot_cle); if ($this->estUnIdentifiantMotCle($mot_cle)) { // certains mots clés mal formatés contiennent des virgules if (strpos($mot_cle, ',') !== false) { $tab_mot_cle_mal_formate = explode(',', $mot_cle); foreach ( $tab_mot_cle_mal_formate as $mot_cle_mal_formate) { if ($this->estUnIdentifiantMotCle($mot_cle_mal_formate)) { $tableau_mots_cles_formates[$mot_cle_mal_formate] = $mot_cle_mal_formate; } } } else { // on met le mot clé dans sa propre case afin d'éviter // facilement les doublons provoqués par de mauvais formatages $tableau_mots_cles_formates[$mot_cle] = $mot_cle; } } else if ($mot_cle != '') { echo "N'est pas un mot clé : $mot_cle\n"; } } return $tableau_mots_cles_formates; } private function estUnIdentifiantMotCle($chaine) { return preg_match('/^(?:[-][0-9]+[.][0-9]+|[-_a-z0-9]+(?:[.][-_a-z0-9]+)*)$/i', $chaine); } private function genererIndexTexteMotsClesObs() { $requete = 'SELECT DISTINCT id_observation, id_utilisateur '. 'FROM '.self::$bdd_cel_migration.'.cel_obs_mots_cles '; $obs_a_mots_cles = $this->bdd->requeter($requete); foreach ($obs_a_mots_cles as $obs) { $mots_cles_texte = $this->obtenirMotsClesTexte($obs['id_observation'], $obs['id_utilisateur'], 'obs'); if (is_array($mots_cles_texte) && count($mots_cles_texte) > 0) { $mots_cles_texte_chaine = implode(',', $mots_cles_texte); $mise_a_jour_index = $this->executerRequeteMiseAJourMotCleTexte($mots_cles_texte_chaine, $obs['id_observation'], $obs['id_utilisateur'], 'obs'); $this->script->afficherAvancement('Génération des index mots clés obs (par 1)'); } } echo "\n"; } private function genererIndexTexteMotsClesImages() { $requete = 'SELECT DISTINCT id_image, id_utilisateur '. 'FROM '.self::$bdd_cel_migration.'.cel_images_mots_cles '; $images_a_mots_cles = $this->bdd->requeter($requete); foreach ($images_a_mots_cles as $image) { $mots_cles_texte = $this->obtenirMotsClesTexte($image['id_image'], $image['id_utilisateur'], 'images'); $mots_cles_texte_chaine = ''; if (is_array($mots_cles_texte) && count($mots_cles_texte) > 0) { $mots_cles_texte_chaine = implode(',', $mots_cles_texte); $mise_a_jour_index = $this->executerRequeteMiseAJourMotCleTexte($mots_cles_texte_chaine, $image['id_image'], $image['id_utilisateur'], 'images'); $this->script->afficherAvancement('Génération des index mots clés images (par 1)'); } } echo "\n"; } private function obtenirMotsClesTexte($id_image_ou_obs, $id_utilisateur, $mode) { $bdd = self::$bdd_cel_migration; $champ_id_mot_cle = ($mode == 'obs') ? 'id_mot_cle_obs' : 'id_mot_cle_image'; $champ_id_obs_ou_img = ($mode == 'obs') ? 'id_observation' : 'id_image'; $id_image_ou_obs = $this->bdd->proteger($id_image_ou_obs); $id_utilisateur = $this->bdd->proteger($id_utilisateur); $requete = 'SELECT mot_cle '. "FROM $bdd.cel_mots_cles_$mode AS a ". " INNER JOIN $bdd.cel_{$mode}_mots_cles AS b ". " ON (a.$champ_id_mot_cle = b.$champ_id_mot_cle AND a.id_utilisateur = b.id_utilisateur) ". "WHERE b.$champ_id_obs_ou_img = $id_image_ou_obs ". " AND a.id_utilisateur = $id_utilisateur "; $resultats = $this->bdd->requeter($requete); $mots_cles = array(); foreach ($resultats as $resultat) { $mots_cles[] = $resultat['mot_cle']; } return $mots_cles; } private function executerRequeteMiseAJourMotCleTexte($mots_cles_texte_chaine, $id_image_ou_obs, $id_utilisateur, $mode_image_ou_obs) { $bdd = self::$bdd_cel_migration; $table = ($mode_image_ou_obs == 'obs') ? 'cel_obs' : 'cel_images'; $chp_id_obs_ou_img = ($mode_image_ou_obs == 'obs') ? 'id_observation' : 'id_image'; $id_image_ou_obs = $this->bdd->proteger($id_image_ou_obs); $mots_cles_texte = $this->bdd->proteger($mots_cles_texte_chaine); $ce_utilisateur = $this->bdd->proteger($id_utilisateur); $requete = "UPDATE $bdd.$table ". "SET mots_cles_texte = $mots_cles_texte ". "WHERE $chp_id_obs_ou_img = $id_image_ou_obs ". " AND ce_utilisateur = $ce_utilisateur "; return $this->bdd->executer($requete); } private function mettreANullMotsClesTxtVide() { $bdd = self::$bdd_cel_migration; $tables = array('cel_obs', 'cel_images'); foreach ($tables as $table) { $requete = "UPDATE $bdd.$table ". 'SET mots_cles_texte = NULL '. "WHERE mots_cles_texte = '' "; $this->bdd->executer($requete); } } }