/*! * GJframework * model/DB.class.php * v1.1.0 (17/10/2018) * * Created by Guillaume Juncker on 24/08/2018. * Copyright © Guillaume Juncker. All rights reserved. */ /** * DB * Classe de gestion primaire de base de données */ class DB { private $db_host = "", $db_name = "", $db_user = "", $db_pass = "", $child_class = ""; protected $db_table = "", $required_vars = array(), $unique_vars = array(); public $Errors = array(); public $id = NULL, $insert_date = "", $insert_user = "", $update_date = "", $update_user = ""; /*------------------------------------------------------------------------------------------------------------------------------*/ /** * __construct * Construit un objet DB et configure ses accès à la base * [@param mixed $vars: un id ou un tableau de clés => valeurs à rechercher] * @return void */ public function __construct($vars = NULL) { if($_SERVER['SERVER_NAME']== "localhost") { $this->db_host = $_SESSION['config']['db']['local']['host']; $this->db_name = $_SESSION['config']['db']['local']['name']; $this->db_user = $_SESSION['config']['db']['local']['user']; $this->db_pass = $_SESSION['config']['db']['local']['pass']; } else { $this->db_host = $_SESSION['config']['db']['remote']['host']; $this->db_name = $_SESSION['config']['db']['remote']['name']; $this->db_user = $_SESSION['config']['db']['remote']['user']; $this->db_pass = $_SESSION['config']['db']['remote']['pass']; } $this->child_class = get_called_class(); $this->db_table = strtolower($this->child_class); if(is_numeric($vars)) { $vars = array("id" => $vars);} if(is_array($vars)) { $this->getBy($vars);} $this->initRequiredVars(); $this->initUniqueVars(); $this->initErrors(); } /*------------------------------------------------------------------------------------------------------------------------------*/ /** * initRequiredVars * Définit les noms des variables qui doivent être renseignées * @return void */ protected function initRequiredVars() {} /** * initUniqueVars * Définit les noms des variables qui doivent ne contenir que des valeurs uniques * @return void */ protected function initUniqueVars() {} /** * initErrors * Définit les erreurs possibles des fonctions de la classe * @return void */ protected function initErrors() { $this->Errors = array("I_SUCCESS" => new CustomError("success", "Les données ont bien été ajoutées"), "I_MISSING" => new CustomError("warning", "Une ou plusieurs donnée(s) requise(s) est/sont manquante(s)"), "I_EXISTS" => new CustomError("warning", "Une ou plusieurs des données fournies existe(nt) déjà et ne peu(ven)t être enregistrée(s) en doublon"), "I_ERROR" => new CustomError("danger", "Une erreur s'est produite lors de l'ajout des données, veuillez réessayer"), "U_SUCCESS" => new CustomError("success", "Les données ont bien été modifiées"), "U_MISSING" => new CustomError("warning", "Une ou plusieurs donnée(s) requise(s) est/sont manquante(s)"), "U_EXISTS" => new CustomError("warning", "Une ou plusieurs des données fournies existe(nt) déjà et ne peu(ven)t être enregistrée(s) en doublon"), "U_ERROR" => new CustomError("danger", "Une erreur s'est produite lors de la modification des données, veuillez réessayer"), "D_SUCCESS" => new CustomError("success", "Les données ont bien été supprimées"), "D_ERROR" => new CustomError("danger", "Une erreur s'est produite lors de la suppression des données, veuillez réessayer")); } /** * pdo * Permet la connexion à la base de données via la méthode PDO * @return PDO: l'objet PDO de connexion */ protected final function pdo() { if($GLOBALS['DB_PDO']== NULL) { try { $GLOBALS['DB_PDO'] = new PDO("mysql:host=$this->db_host;dbname=$this->db_name", $this->db_user, $this->db_pass, [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8']);} catch(Exception $e) { return "

Error: ".$e->getMessage()."

";} } return $GLOBALS['DB_PDO']; } /*------------------------------------------------------------------------------------------------------------------------------*/ /** * getBy * Charge l'Objet avec les données de la base connectée * @param array $vars: un tableau de clés => valeurs à rechercher * @return mixed: l'Objet chargé s'il existe, FALSE sinon */ public function getBy(array $vars) { $where = ""; $parameters = ""; $last_value = end($vars); foreach($vars as $var_key => $var_value) { $where.= "$var_key = :$var_key"; $parameters.= "$var_key => $var_value"; if($var_value!= $last_value) { $where.= " AND "; $parameters.= ", "; } } $req = $this->pdo()->prepare("SELECT * FROM ".$this->db_table." WHERE $where"); $req->execute($vars); if($fetch = $req->fetch(PDO::FETCH_ASSOC)) { foreach($fetch as $k => $v) { $this->$k = $v;} return $this; } trigger_error("No object found by the function ".__METHOD__."($parameters) ", E_USER_NOTICE); return FALSE; } /** * getById * Version réservée à l'id de la fonction getBy() * @param int $id: un id à rechercher * @return mixed: l'Objet chargé s'il existe, FALSE sinon */ public final function getById(int $id) { return $this->getBy(array("id" => $id)); } /** * getAll * Construit l'ensemble des Objets enregistrés dans la base de données * [@param string $req: le complément éventuel de la requête] * @return array: un tableau de tous les Objets */ public function getAll(string $req = "") { $req = $this->pdo()->prepare("SELECT id FROM ".$this->db_table." $req"); $req->execute(); $Objects = array(); foreach($req->fetchAll(PDO::FETCH_ASSOC) as $object) { $Objects[] = new $this->child_class($object['id']); } return $Objects; } /** * count * Calcul le nombre d'objets existants dans la base de données * @return int: le nombre de ligne de la table concernée */ public final function count() { $req = $this->pdo()->prepare("SELECT COUNT(id) FROM ".$this->db_table); $req->execute(); return (int)$req->fetch(PDO::FETCH_ASSOC)['COUNT(id)']; } /** * search * Effectue une recherche dans les données de la base * @param mixed $search: une valeur de recherche * @param array $vars_keys: un tableau des clés dans lesquelles effectuer la recherche * @return array: un tableau des Objets correspondantes à la recherche */ public function search(string $search, array $vars_keys) { $where = ""; foreach($vars_keys as $k => $key) { if($k!= 0) { $where.= " OR ";} $where.= "$key LIKE :search"; } $req = $this->pdo()->prepare("SELECT id FROM ".$this->db_table." WHERE $where"); $req->execute(array("search" => "%".str_replace(" ", "%", $search)."%")); $Objects = array(); foreach($req->fetchAll(PDO::FETCH_ASSOC) as $object) { $Objects[] = new $this->child_class($object['id']); } return $Objects; } /** * exists * Détermine si un Objet existe déjà en base de données * @param array $vars: un tableau de clés => valeur à rechercher * @return bool: TRUE si l'Objet a été trouvé, FALSE sinon */ public final function exists(array $vars) { foreach($vars as $k => $v) { if(!in_array($k, $this->unique_vars)) { unset($vars[$k]);} } $Object = new $this->child_class($vars); return sizeof($vars) > 0 AND $Object->id AND $Object->id!= $this->id; } /** * hasMissedVars * Détermine si une variable est manquante au bon fonctionnement de l'objet * @param array $vars: un tableau de clés => valeur à rechercher * @return bool: TRUE si une valeur requise a été oubliée, FALSE sinon */ protected function hasMissedVars(array $vars) { foreach($this->required_vars as $required_var) { if((!$vars[$required_var] AND !$this->$required_var) OR (isset($vars[$required_var]) AND !$vars[$required_var])) { return TRUE;} } return FALSE; } /** * in_array * Version adaptée à un Objet de base de données de la function "in_array" permetant de savoir si un Objet donné est dans un tableau d'Objets similaires * @param array $haystack: le tableau d'Objets dans lequel on effectue la recherche * @return bool: TRUE si l'Objet a été trouvé dans le tableau, FALSE sinon */ public final function in_array(array $haystack) { foreach($haystack as $Object) { if($this== $Object AND $this->id== $Object->id) { return TRUE;} } return FALSE; } /*------------------------------------------------------------------------------------------------------------------------------*/ /** * insert * Insert l'Objet en base de données * @param array $vars: un tableau de clés => valeurs à insérer * @return CustomError: l'erreur déclenchée à l'exécution */ public function insert(array $vars) { if(!$this->hasMissedVars($vars)) { if(!$this->exists($vars)) { $vars_keys = array_keys($vars); $columns = implode(", ", $vars_keys); $values = ":".implode(", :", $vars_keys); if(!in_array("insert_date", $vars_keys)) { $columns.= ", insert_date"; $values.= ", NOW()"; } if(!in_array("insert_user", $vars_keys)) { $columns.= ", insert_user"; $values.= ", '".$GLOBALS['_USER']->id."'"; } if(!in_array("update_date", $vars_keys)) { $columns.= ", update_date"; $values.= ", NOW()"; } if(!in_array("update_user", $vars_keys)) { $columns.= ", update_user"; $values.= ", '".$GLOBALS['_USER']->id."'"; } $req = $this->pdo()->prepare("INSERT INTO ".$this->db_table."($columns) VALUES($values)"); if($req->execute($vars)) { return $this->Errors['I_SUCCESS']; } else { return $this->Errors['I_ERROR'];} } else { return $this->Errors['I_EXISTS'];} } else { return $this->Errors['I_MISSING'];} } /** * update * Modifie un Objet en base de données * @param array $vars: un tableau de clés => valeurs à modifier * @return CustomError: l'erreur déclenchée à l'exécution */ public function update(array $vars) { if(!$this->hasMissedVars($vars)) { if(!$this->exists($vars)) { $set = ""; $vars_keys = array_keys($vars); $last_key = end($vars_keys); foreach($vars_keys as $key) { $set.= "$key = :$key"; if($key!= $last_key) { $set.= ", ";} } if(!in_array("update_date", $vars_keys)) { $set.= ", update_date = NOW()";} if(!in_array("update_user", $vars_keys)) { $set.= ", update_user = '".$GLOBALS['_USER']->id."'";} $req = $this->pdo()->prepare("UPDATE ".$this->db_table." SET $set WHERE id = :id"); if($req->execute($vars)) { return $this->Errors['U_SUCCESS']; } else { return $this->Errors['U_ERROR'];} } else { return $this->Errors['U_EXISTS'];} } else { return $this->Errors['U_MISSING'];} } /** * setPosition * Modifie la position de l'Objet en base de données * @param int $position: la nouvelle position * @return bool: le résultat de l'exécution */ public function setPosition(int $position) { $req = $this->pdo()->prepare("UPDATE ".$this->db_table." SET position = :position WHERE id = :id"); return $req->execute(array("id" => $this->id, "position" => $position)); } /** * reorder * Déplace l'Objet à la position donnée * @param int $new_position: la nouvelle position de l'Objet * @return void */ public function reorder(int $new_position) { // Contrainte de la nouvelle position à rester dans l'intervalle 1 à count() if($new_position < 1) { $new_position = 1;} if($new_position > ($count = $this->count())) { $new_position = $count;} // Seulement si c'est utile if($this->position!= $new_position) { $Object = new $this->child_class(); // Remontée d'un Objet if($this->position < $new_position) { for($i = $this->position + 1 ; $i<= $new_position ; $i++) { while(!$Object->getBy(array("position" => $i))->setPosition($i - 1)); } } // Descente d'un Objet elseif($this->position > $new_position) { for($i = $this->position - 1 ; $i>= $new_position ; $i--) { while(!$Object->getBy(array("position" => $i))->setPosition($i + 1)); } } // Placement de l'Objet à sa nouvelle position désormais libre while(!$this->setPosition($new_position)); } } /** * delete * Supprime un Objet de la base de données * @return CustomError: l'erreur déclenchée à l'exécution */ public function delete() { $req = $this->pdo()->prepare("DELETE FROM ".$this->db_table." WHERE id = :id"); if($req->execute(array("id" => $this->id))) { // Réordonnancement général des positions si nécessaire if(isset($this->position)) { foreach($this->getAll("ORDER BY position ASC") as $k => $Object) { while(!$Object->setPosition($k + 1)); } } return $this->Errors['D_SUCCESS']; } else { return $this->Errors['D_ERROR'];} } /*------------------------------------------------------------------------------------------------------------------------------*/ /** * backup * Sauvegarde la base de données * @return void */ public final function backup() { $Datime = new Datime(); $date_week_time = explode("_", $datetime = $Datime->format(Datime::FORMAT_DBB)); $date = explode("-", $date_week_time[0]); // Création du répertoire de backup s'il n'existe pas déjà if(!file_exists($backups_dir_path = dirname(__DIR__)."/storage/private/system/dbackups")) { mkdir($backups_dir_path, 0777, TRUE);} // Backup system("mysqldump --no-tablespaces --opt -h".$this->db_host." -u".$this->db_user." -p".$this->db_pass." ".$this->db_name." | gzip > ".$backups_dir_path."/".$this->db_name."_".$datetime.".sql.gz"); // Suppression des backups du jour les plus anciennes if(sizeof(($arrayTodayBackups = glob($backups_dir_path."/".$this->db_name."_".$date_week_time[0]."_*.gz"))) > 1) { array_pop($arrayTodayBackups); foreach($arrayTodayBackups as $file) { unlink($file);} } // Suppression des backups de la semaine dernière les plus anciennes if(sizeof(($arrayLastweekBackups = glob($backups_dir_path."/".$this->db_name."_*_".($date_week_time[1] - 1)."_*.gz"))) > 1) { array_pop($arrayLastweekBackups); foreach($arrayLastweekBackups as $file) { unlink($file);} } // Suppression des backups du mois dernier les plus anciennes if(sizeof(($arrayLastmonthBackups = glob($backups_dir_path."/".$this->db_name."_".$date[0]."-".sprintf('%02d', $date[1] - 1)."*.gz"))) > 1) { array_pop($arrayLastmonthBackups); foreach($arrayLastmonthBackups as $file) { unlink($file);} } // Suppression des backups de l'année dernière les plus anciennes if(sizeof(($arrayLastyearBackups = glob($backups_dir_path."/".$this->db_name."_".sprintf('%04d', $date[0] - 1)."*.gz"))) > 1) { array_pop($arrayLastyearBackups); foreach($arrayLastyearBackups as $file) { unlink($file);} } } }