<?php

abstract class aGttSql {

	/*** Attributs : */
	private $base_de_donnees = GTT_BDD_NOM;
	protected $table_nom;
	protected $correspondance;
	
	/*** Accesseurs : */

	// Base De Donnees
	function getBaseDeDonnees()
	{
		return $this->base_de_donnees;
	}
	function setBaseDeDonnees($bdd)
	{
		$this->base_de_donnees = $bdd;
	}
	// TableNom
	function getTableNom()
	{
		return $this->dao_table_nom;
	}
	function setTableNom($tn)
	{
		$this->dao_table_nom = $tn;
	}
	// Correspondance
	function getCorrespondance($champ = null)
	{
		if (!is_null($champ)) {
			return $this->dao_correspondance[$champ];
		}
		return $this->dao_correspondance;
	}
	function setCorrespondance($c)
	{
		$this->dao_correspondance = $c;
	}

	/*** Méthodes : */

    /** Instancie un objet utilisateur à partir d'un enregistrement issu de la base de donnée ou l'inverse.
	* Cette métohode permet de s'abstraire des noms des champs présent dans la base de donnée.
	*/
	protected function basculerEnregistrementObjet($donnees, $instancier = false)
	{
		$classe = get_class($this);
		if (is_array($donnees)) {
			if ($instancier) {
				foreach ($this->getCorrespondance() as $champ => $attribut) {
					if (isset($donnees[$champ]) && !is_null($donnees[$champ])) {
						$methode = $this->donnerMethodeSetAvecAttribut($attribut);
						$this->$methode($donnees[$champ]);
					}
				}
			} else {
				$Objet = new $classe;
				foreach ($this->getCorrespondance() as $champ => $attribut) {
					if (isset($donnees[$champ]) && !is_null($donnees[$champ])) {
						$methode = $this->donnerMethodeSetAvecAttribut($attribut);
						$Objet->$methode($donnees[$champ]);
					}
				}
				return $Objet;
			}
		} else if ($donnees instanceof $classe) {
			$enregistrement = array();
			foreach ($this->getCorrespondance() as $champ => $attribut) {
				$methode = $this->donnerMethodeGetAvecAttribut($attribut);
				if (method_exists($donnees, $methode)) {
					if (!is_null($donnees->$methode())) {
				 		$enregistrement[$champ] = $donnees->$methode();
					}
				}
			}
			return $enregistrement;
		}
	}
	private function donnerMethodeGetAvecAttribut($attribut)
	{
		return 'get'.str_replace(' ', '', ucwords(str_replace('_', ' ', $attribut)));
	}
	private function donnerMethodeGetAvecChamp($champ)
	{
		return 'get'.str_replace(' ', '', ucwords(str_replace('_', ' ', $this->getCorrespondance($champ))));
	}
	private function donnerMethodeSetAvecAttribut($attribut)
	{
		return 'set'.str_replace(' ', '', ucwords(str_replace('_', ' ', $attribut)));
	}
	private function donnerMethodeSetAvecChamp($champ)
	{
		return 'set'.str_replace(' ', '', ucwords(str_replace('_', ' ', $this->getCorrespondance($champ))));
	}

	/**
	* Consulter un ou plusieurs enregistrements dans la base de données.
	* Chaque requête comportant des paramêtre doivent les inclures sous la forme "#0" pour le paramêtre 0,
	* puis "#1" pour le paramêtre 1 et ainsi de suite.
	* Exemple : SELECT * FROM gestion_projet WHERE gp_id_projet = #0
	* ou		SELECT * FROM gestion_projet WHERE gp_nom_projet = "#0"
	* @return mixed false, un objet, un tableau d'objet ou rien et instancie l'objet courant.
	*/
	public function consulter($requete = '', $parametres = null, $instancier = false)
	{
		// Formatage de la requête avec les paramêtres s'il y en a
		if (!is_null($parametres)) {
			if (!is_array($parametres)) {
				$parametres = array('#0' => $parametres);
			} else {
				// Ajout d'un # devant chaque clé numérique
				if (count($parametres) > 0) {
					foreach ($parametres as $c => $v) {
						$parametres['#'.$c] = $v;
					}
				}
			}
			// les indices numériques contiennent des copies des valeurs présentes
			// dans les indices [#0], [#1]... => wtf ? si le préfixe de table
			// contient un chiffre, celui-ci sera remplacé et tout va péter
			// => bricolage cracra, on enlève tous les indices numériques
			foreach ($parametres as $k => &$v) {
				if (is_numeric($k)) {
					unset($parametres[$k]);
				}
			}
			// Remplacement dans la requete par les valeurs des paramêtres
			$requete = strtr($requete, $parametres);
		}
		if (GTT_DEBOGAGE_SQL) {
			trigger_error($requete, E_USER_NOTICE);
		}
		$tps = microtime(true);
		$resultat = $GLOBALS['db']->query($requete);
		$GLOBALS['_GTT_']['chrono']->setTempsSql($tps, microtime(true));
		(DB::isError($resultat)) ? trigger_error(GestionnaireErreur::retournerErreurSql(__FILE__, __LINE__, $resultat->getMessage(), $requete), E_USER_ERROR) : '' ;
		$tab_resultat = array();
		while ($donnees =& $resultat->fetchRow(DB_FETCHMODE_ASSOC)) {
			$tab_resultat[] = $this->basculerEnregistrementObjet($donnees, $instancier);
		}
		$resultat_nbre = count($tab_resultat);
		if ($resultat_nbre >= 1) {
			return $tab_resultat;
		} else if ($resultat_nbre == 0) {
			return false;
		}
	}

	/**
	* Ajouter un enregistrement dans la base de données.
	* @return true si ok, false si aucun enregistrement effectué
	*/
	public function ajouter()
	{
		$enregistrement = $this->basculerEnregistrementObjet($this);
		$sql_attributs = '';
		$sql_valeurs = '';
		foreach($enregistrement as $champ => $val) {
			if (!is_numeric($val)) {
				$val = '"'.$val.'"';
			}
			$sql_attributs .= $champ.', ';
			$sql_valeurs .= $val.', ';
		}
		$sql_attributs = trim($sql_attributs, ', ');
		$sql_valeurs = trim($sql_valeurs, ', ');
		$requete = 	'INSERT INTO '.$this->getBaseDeDonnees().'.'.$this->getTableNom().' '.
					'( '.$sql_attributs.' ) '.
					'VALUES '.
					'( '.$sql_valeurs.' )';
		if (GTT_DEBOGAGE_SQL) {
			trigger_error($requete, E_USER_NOTICE);
		}
		$tps = microtime(true);
		$resultat = $GLOBALS['db']->query($requete);
		$GLOBALS['_GTT_']['chrono']->setTempsSql($tps, microtime(true));
		(DB::isError($resultat)) ? die (GestionnaireErreur::retournerErreurSql(__FILE__, __LINE__, $resultat->getMessage(), $requete)) : '' ;
		$nbre_enregistrement_ajoute = $GLOBALS['db']->affectedRows();
		if ($nbre_enregistrement_ajoute == 1) {
			return true;
		} elseif ($nbre_enregistrement_ajoute == 0) {
			return false;
		}
	}

	/**
	* Modifier un enregistrement dans la base de données.
	* @param object l'ancien objet contenant les valeurs de clés primaires non modifiées. Laissé vide si on ne modifie pas les clés.
	* @return true si ok, false si aucun enregistrement effectué.
	*/
	public function modifier($Ancien = null)
	{
		$enregistrement = $this->basculerEnregistrementObjet($this);
		$sql_where = '';
		$sql_set = '';
		foreach($enregistrement as $champ => $val) {
			if (!is_numeric($val)) {
				$val = '"'.$val.'"';
			}
			$sql_set .= $champ.' = '.$val.', ';
			$classe = get_class($this);
			if ($Ancien instanceof $classe) {
				$methode = $this->donnerMethodeGetAvecChamp($champ);
				$val = $Ancien->$methode();
				if (!is_numeric($val)) {
					$val = '"'.$val.'"';
				}
			}
			if (preg_match('/_id_/', $champ)) {
				$sql_where .= $champ.' = '.$val.' AND ';
			}
		}
		$sql_set = trim($sql_set, ', ').' ';
		$sql_where = trim($sql_where, ' AND ').' ';
		$requete = 	'UPDATE '.$this->getBaseDeDonnees().'.'.$this->getTableNom().' SET '.$sql_set.'WHERE '.$sql_where;
		if (GTT_DEBOGAGE_SQL) {
			trigger_error($requete, E_USER_NOTICE);
		}
		$tps = microtime(true);
		$resultat = $GLOBALS['db']->query($requete);
		$GLOBALS['_GTT_']['chrono']->setTempsSql($tps, microtime(true));
		(DB::isError($resultat)) ? die (GestionnaireErreur::retournerErreurSql(__FILE__, __LINE__, $resultat->getMessage(), $requete)) : '' ;
		$nbre_enregistrement_ajoute = $GLOBALS['db']->affectedRows();
		if ($nbre_enregistrement_ajoute == 1) {
			return true;
		} elseif ($nbre_enregistrement_ajoute == 0) {
			return false;
		}
	}

	/**
	* Supprimer un enregistrement dans la base de données.
	* @return true si ok, false si aucun enregistrement effectué
	*/
	public function supprimer()
	{
		$enregistrement = $this->basculerEnregistrementObjet($this);
		$sql_where = '';
		foreach($enregistrement as $champ => $val) {
			if (!is_numeric($val)) {
				$val = '"'.$val.'"';
			}
			//if (preg_match('/_id_/', $champ)) {
				$sql_where .= $champ.' = '.$val.' AND ';
			//}
		}
		$sql_where = trim($sql_where, ' AND ').' ';
		$requete = 'DELETE FROM '.$this->getBaseDeDonnees().'.'.$this->getTableNom().' WHERE '.$sql_where ;
		if (GTT_DEBOGAGE_SQL) {
			trigger_error($requete, E_USER_NOTICE);
		}
		$tps = microtime(true);
		$resultat = $GLOBALS['db']->query($requete);
		$GLOBALS['_GTT_']['chrono']->setTempsSql($tps, microtime(true));
		(DB::isError($resultat)) ? die (GestionnaireErreur::retournerErreurSql(__FILE__, __LINE__, $resultat->getMessage(), $requete)) : '' ;

		$nbre_enregistrement_suppr = $GLOBALS['db']->affectedRows();
		if ($nbre_enregistrement_suppr == 1) {
			return true;
		} elseif ($nbre_enregistrement_suppr == 0) {
			return false;
		}
	}
	
	/** Mettre à NULL les champs de l'objet*/
	public function initialiser()
	{
		foreach ($this->getCorrespondance() as $champ => $attribut) {
			$methode = $this->donnerMethodeSetAvecAttribut($attribut);
			$this->$methode(null);
		}
	}
	
	/** Afficher l'objet courrant. */
	public function afficher()
	{
		echo '<pre>'.print_r($this, true).'</pre>';
	}
}
?>