Un petit exercice qui nécessite une correction

Petit nouveau ! | 2 Messages

11 juin 2013, 17:12

Bonjour,
je me présente. Mon pseudonyme est Glloqglloq et je commence l'apprentissage de PHP depuis peu par loisir.
Je suis de niveaux débutants en PHP, POO et MYSQL et c'est pour cela que j'ai besoin de vos conseils d'expert avant d'aller plus loin.
Pour m'entrainer et prendre de bonne habitude, je me suis imposé un petit exercice.
En voici le sujet :
Je crée souvent des erreurs de code dans mes requêtes PDO et j'ai donc décidé de me crée une classe qui mettra en ordre mes requêtes automatiquement et qui sera plus simple visuellement pour moi.
Cette classe devra regrouper toutes les requêtes SQL que j'utilise régulièrement ex : SELECT, INSERT, UPDATE, DELETE avec leur condition WHERE, GROUP BY, etc.
Elle devrait également posséder des protections pour éviter par exemple les suppressions de table suite à un oubli.

Seulement, voila un gros problème se pose ! j'ai de grosses difficultés a me corriger moi-même tellement les avis et conseils diffère sur les forums internet.
C'est donc maintenant que je fais appel à vous si vous le souhaitez bien, pour m'apporter vos conseils sur mon code (bonne pratique, incohérence, alternative plus efficace, etc.).

Voici le code en question avec un petit exemple:
class SIUD
{
	private static $bdd;
	protected 	$table,
			$colonneName='*',
			$condition='',
			$conditionWhere='',
			$group='',
			$hav='',
			$conditionHaving='',
			$orderByValue='',
			$limitValue='',
			$conditionLimit='',
			$tableJoin='',
			$insertIntoValues='',
			$updateValues='';
	
	public function __construct($table)
	{
		$this->table=$table;
	}
	public static function bdd($dbname,$username,$password)
	{
		$pdo_options[PDO::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES utf8";
		$pdo_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
		self::$bdd = new PDO('mysql:host=localhost;dbname='.$dbname, $username, $password, $pdo_options);
	}
	public function select($reset=true)
	{
		$condExec=$this->arrConditionExecute($this->conditionWhere,$this->conditionHaving,$this->conditionLimit);
		if($this->condition||$this->hav||$this->limitValue)
		{
			$req=self::$bdd->prepare('SELECT '.$this->colonneName.' FROM '.$this->table.$this->tableJoin.$this->condition.$this->group.$this->hav.$this->orderByValue.$this->limitValue);
			foreach($condExec as $v)
			{
				$req->bindParam($v['identifiant'], $v['variable'], $v['type']);
			}
			$req->execute();
			
		}
		else
		{
			$req=self::$bdd->query('SELECT '.$this->colonneName.' FROM '.$this->table.$this->tableJoin.$this->group.$this->hav.$this->orderByValue);
		}
		if($reset)
		{
			$this->reset();
		}
		$donnees = $req->fetchAll(PDO::FETCH_ASSOC);
		$req->closeCursor();
		return $donnees;
	}
	public function insert($reset=true)
	{
		if($this->insertIntoValues)
		{
			$condExec=$this->arrConditionExecute($this->insertIntoValues['valeur']);
			$req=self::$bdd->prepare('INSERT INTO '.$this->table.' ('.$this->insertIntoValues['colonne'].') VALUES ('.$this->insertIntoValues['marqueur'].')');
			foreach($condExec as $v)
			{
				$req->bindParam($v['identifiant'], $v['variable'], $v['type']);
			}
			$resultat = $req->execute();
			$req->closeCursor();
		}
		else
		{
			throw new InvalidArgumentException('Attention SIUD::insert() ne peut pas être executé si SIUD::values() n\'est pas renseigné');
		}
		if($reset)
		{
			$this->reset();
		}
		return $resultat;
	}
	public function update($reset=true,$shield=true)
	{
		if($this->updateValues)
		{
			$condExec=$this->arrConditionExecute($this->updateValues['valeur'],$this->conditionWhere);
			if($this->conditionWhere)
			{
				$req=self::$bdd->prepare('UPDATE '.$this->table.' SET '.$this->updateValues['colonne'].$this->condition);
			}
			elseif($shield)
			{
				throw new InvalidArgumentException('Attention, si vous omettez la clause WHERE, tous les enregistrements seront mis à jour!');
			}
			else
			{
				$req=self::$bdd->prepare('UPDATE '.$this->table.' SET '.$this->updateValues['colonne']);
			}
			foreach($condExec as $v)
			{
				$req->bindParam($v['identifiant'], $v['variable'], $v['type']);
			}
			$resultat = $req->execute();
			$req->closeCursor();
		}
		else
		{
			throw new InvalidArgumentException('Attention SIUD::update() ne peut pas être executé si SIUD::set() n\'est pas renseigné');
		}
		if($reset)
		{
			$this->reset();
		}
		return $resultat;
	}
	public function delete($reset=true,$shield=true)
	{
		if($this->conditionWhere)
		{
			$condExec=$this->arrConditionExecute($this->conditionWhere);
			$req=self::$bdd->prepare('DELETE '.$this->colonneName.' FROM '.$this->table.$this->condition);
			foreach($condExec as $v)
			{
				$req->bindParam($v['identifiant'], $v['variable'], $v['type']);
			}
			$resultat = $req->execute();
		}
		elseif($shield)
		{
			throw new InvalidArgumentException('Attention, si vous omettez la clause WHERE, tous les enregistrements seront supprimés!');
		}
		else
		{
			$req=self::$bdd->exec('DELETE '.$this->colonneName.' FROM '.$this->table);
		}
		if($reset)
		{
			$this->reset();
		}
		$req->closeCursor();
		return $resultat;
	}
	public function colonne($colonne)
	{
		$resultat=$this->colonneArg($colonne);
		if($resultat)
		{
			$this->colonneName=$resultat;
		}
		else
		{
			throw new InvalidArgumentException('Les arguments de SIUD::colonne() doivent être soit un tableau exemple: array(\'colonne1\',\'colonne2\'...), soit une chaine de caractere exemple: (\'colonne1,colonne2,...\')');
		}
	}
	public function where()
	{
		$numargs=func_num_args();
		if ($numargs>0)
		{
			$args=func_get_args();
			$where=$this->argsCondition($args);
				if($where)
				{
					$this->condition=' WHERE '.$where['marqueur'];
			$this->conditionWhere = $where['parametre'];
				}
				else
				{
					throw new InvalidArgumentException('Les arguments pour SIUD::where() doivent être un tableau exemple: array(\'colonne=\' => $valeur) ');
				}
		}
		else
		{
			$this->condition='';
			$this->conditionwhere='';
		}
	}
	public function groupBy($group)
	{
		$resultat=$this->colonneArg($group);
		if($resultat)
		{
			$this->group=' GROUP BY '.$resultat;
		}
		else
		{
			throw new InvalidArgumentException('Les arguments de SIUD::groupBy() doivent être soit un tableau exemple: array(\'colonne1\',\'colonne2\'...), soit une chaine de caractere exemple: (\'colonne1,colonne2,...\')');
		}
	}
	public function having()
	{
		if($this->group)
		{
			$numargs=func_num_args();
			if ($numargs>0)
			{
				$args=func_get_args();
				$hav=$this->argsCondition($args);
					if($hav)
					{
						$this->hav=' HAVING '.$hav['marqueur'];
				$this->conditionHaving = $hav['parametre'];
					}
					else
					{
						throw new InvalidArgumentException('Erreur SIUD::having, Les arguments doivent être un tableau du type \'colonne=\' => $valeur ');
					}
			}
			else
			{
				$this->hav='';
				$this->conditionValue='';
			}
		}
		else
		{
			throw new InvalidArgumentException('Erreur SIUD::having, La clause HAVING necessite une clause GROUP BY');
		}
	}
	public function orderBy(array $order)
	{
		foreach($order as $key => $value)
		{
			$arrOrder[]=($value=='DESC')?$key.' DESC':$value.' ASC';
		}
		$this->orderByValue=' ORDER BY '.implode(',',$arrOrder);
	}
	public function like()
	{
		
	}
	public function limit($pointeur=false, $nbrLigne=false)
	{
		if($pointeur)
		{
			$this->limitValue= ' LIMIT ?,?';
			if($nbrLigne===false)
			{
				$nbrLigne=$pointeur;
				$pointeur=0;
			}
			if(is_int($pointeur)&&is_int($nbrLigne))
			{
				$this->conditionLimit=array($pointeur,$nbrLigne);
			}
			else
			{
				throw new InvalidArgumentException('Erreur SIUD::limit, Les arguments doivent être des entiers');
			}
		}
		else
		{
			$this->limitValue='';
		}
	}
	public function joinOn($type=false,$tableIJ=false,$on=false)
	{
		if($type&&$tableIJ&&$on)
		{
			if($type==='INNER'||$type==='LEFT'||$type==='RIGHT')
			{
				if(is_string)
				{
					$this->tableJoin=' '.$type.' '.$tableIJ.' ON '.$on;
				}
				else
				{
					throw new InvalidArgumentException('Erreur SIUD::joinOn, Les arguments doivent être des chaines de caracteres');
				}
			}
			else
			{
				throw new InvalidArgumentException('Erreur SIUD::joinOn, L\'argument $type ne peut contenir que INNER,LEFT,RIGHT');
			}
		}
		else
		{
			$this->tableJoin='';
		}
	}
	public function values(array $colonne_values)
	{
		if(!empty($colonne_values))
		{
			foreach ($colonne_values as $colonne => $values) {
				$colonneI[]=$colonne;
				$marqueurI[]='?';
				$valuesI[]=$values;
			}
			$valuesInsert['colonne']=implode(',', $colonneI);
			$valuesInsert['marqueur']=implode(',', $marqueurI);
			$valuesInsert['valeur']=$valuesI;
			$this->insertIntoValues=$valuesInsert;
		}
	}
	public function set(array $colonne_values)
	{
		if(!empty($colonne_values))
		{
			foreach ($colonne_values as $colonne => $values) {
				$colonneU[]=$colonne.'=?';
				$valuesU[]=$values;
			}
			$valuesUpdate['colonne']=implode(',', $colonneU);
			$valuesUpdate['valeur']=$valuesU;
			$this->updateValues=$valuesUpdate;
		}
	}
	private function argsCondition ($args)
	{
		$resultat['marqueur']='';
			for ($i = 0; $i < count($args); $i++)
			{
				if(is_array($args[$i]))
				{
					$resultat=($i>0)?$resultat.' OR ':$resultat;
					foreach ($args[$i] as $key => $value)
					{
							$marqueur[]=$key.'?';
							$parametre[]=$value;
					}
					$resultat['marqueur'] .= implode(' AND ',$marqueur);
					$resultat['parametre']=$parametre;
					unset($marqueur);
				}
				else
				{
					return false;
				}
			}
			return $resultat;
	}
	private function arrConditionExecute()
	{
		$arrCondition='';
		$arg=func_get_args();
		$nbr=count($arg);
		for($i=0,$j=1;$i<$nbr;$i++)
		{
			if(is_array($arg[$i]))
			{
				foreach($arg[$i] as $v)
				{
					$arrCondition[]=array('identifiant'=>$j++,'variable'=>$v,'type'=>(is_int($v))?PDO::PARAM_INT:PDO::PARAM_STR);
				}
			}
		}
		return $arrCondition;
	}
	private function colonneArg($colonne)
	{
		if(is_array($colonne))
		{
			return implode(',',$colonne);
		}
		elseif(is_string($colonne))
		{
			return $colonne;
		}
		else
		{
			return false;
		}
	}
	private function reset()
	{
				$this->colonneName='*';
				$this->condition='';
				$this->conditionWhere='';
				$this->group='';
				$this->hav='';
				$this->conditionHaving='';
				$this->orderByValue='';
				$this->limitValue='';
				$this->conditionLimit='';
				$this->tableJoin='';
				$this->insertIntoValues='';
				$this->updateValues='';
	}
}
// ==== Exemple === 
try
{
SIUD::bdd('bddname','root','');
$bddmembres = new SIUD('membres');
$bddmembres->colonne('pseudo');
$resultat=$bddmembres->select(false);
var_dump($resultat);
//retourne un array contenant toute les données de la table
$bddmembres->where(array('id<'=>4));
$resultat=$bddmembres->select();
var_dump($resultat);
//retourne un array contenant toute les données de la table dont l'id et inferieur à 4
}
catch(Exception $e)
{
	echo $e->getMessage();
}

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

11 juin 2013, 22:54

salut,


Ce que tu cherche à faire s'appel un orm :D

il en existe en php, comme doctrine ou propel.


Tu peux rendre ta classe plus souple par le chaînage des méthodes (en retournant l'objet) et ainsi permettre quelque chose comme
<?php
$res = $bbd->colonne('')->where('')->select();
Perso je ne trouve pas les méthodes très parlante.

Sinon une dao bien faite peux masquer simplement le coté sgbd, et limiter l'utilité de l'orm, bien que cela puisse limiter l'adhérence à la base de données :)


@+
Il en faut peu pour être heureux ......

Petit nouveau ! | 2 Messages

12 juin 2013, 11:32

Bonjour moogli.

Tout d'abord je te remercie pour ta participation.

1) Je trouve l'idée du chaine des methode tres interressent, c'est tout de suite plus clair et plus simple. Je vais donc des maintenant m'orienter la dessus.
2) Tu trouve les methode "pas tres parlante". Celon toi comment rendre ca plus efficase?
3) Avec ton experience, as tu trouvé d'autres erreurs ou mauvaises pratique? Je cherche à partir sur des bases saines.

Encore merci.

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

12 juin 2013, 22:01

tes méthodes sont trop complexe, il y a surement moyen de simplifier.

sinon pour les méthodes je verrais plutot un select pour le nom des champs, un form pour le nom de la table, join pour la joiture etc, bref un simulacre de sql.


pour au final faire un truc du genre : $bdd->select('les champs')->from('la table')->where ('le prédicat')->orderby('champs')-> execute();

bon y a surement plus clair, mais le principe est la.


tu peux regarder du coté des orm existant sur leurs fonctionnements.
certains te propose d'indiquer la structure des tables dans des fichiers xml par exemple du coup pas besoin d'indiquer les champs pour la requête l'orm le fait seul.

Dans l'absolue, tu devrais passer un objet pdo dans le constructeur, car cet objet ne doit pas avoir la responsabilité de la connexion, et en plus cela te permet de le réutiliser pour d'autre connexion en même temps :)


@+
Il en faut peu pour être heureux ......