salut raphy123!
Le manque de flexibilité de la méthode "find", m'a également poussé à me penché sur la question des jointures. j'ai donc revisité celle-ci de manière à ce qu'elle supporte les jointures multiples, et de diffents types. j'ai également ajouté une méthode privée (addReference) à la classe "Model" qui à chaque fois préfixe les noms de tables devant ceux des champs, ce qui évite toute ambiguïté en cas jointure.
de plus cette nouvelle méthode "find" est totalement compatible avec les codes déjà disponibles dans le tuto de "développer un site de A à Z" (7 jours). un simple copier coller et le site continuera à fonctionner sans pb. sauf que en interne, pour l'uri:
/posts/index
(par exemple) la requête SQL qui sera executée sera la suivante:
SELECT posts.* FROM posts WHERE posts.type='post' AND posts.online=1 LIMIT 0,3
au lieu de:
SELECT * FROM posts WHERE type='post' AND online=1 LIMIT 0,3
j’espère que ceci répond entièrement à tes attentes.
toutefois tout l'honneur reviens à grafikart pour la richesse de ses tutoriels.
voici le code:
/**
*ajout du nom de la table devant celui du champs
*
**/
private function addReference($row, $table=null){
if(!$table) $table = $this->table;
$needle= $table.'.';
$space = '/\s/';
$sqlFuncPattern = '/\((^)]*)\)/';//prise en charge count(),sum(), min(), max(), etc....
//$commonfunct = array('count','min','max','distinct','sum','','','','','','','','');
if(!is_array($row)) $row = explode(',',trim(trim($row), ',')); //c'est une chaine de caracteres
$row = array_map('trim',$row);
//c'est un tableau
foreach($row as $k=>$v)
{
$v = trim(trim($v, ','));
if(strpos($v,',') !== false) //un tableau, mais cet element contient les noms de champs séparés par des virgules
{
$row$k] = $this-> __METHOD__ (explode(',',$v), $table);
}
else
{//pas de virgule
if(strpos($v,'.')==false)
{
if(preg_match($sqlFuncPattern, $v,$m)) //presence d'une function mysql Ex: max(field)
{
if(preg_match($space, $m[1])) //presence d'un espace ex: count(distinct field)
{
$col = explode(' ',$m[1]);
if(strtolower($col[1])==='as')
$row$k] = str_replace('('.$m[1].')','('.$needle.current($col).' '.end($col).')',$v);
else $row$k] = str_replace('('.$m[1].')','('.current($col).' '.$needle.end($col).')',$v);
}
else $row$k] = str_replace('('.$m[1].')','('.$needle.$m[1].')',$v);
}
else
{
if(preg_match($space, $v)) //presence d'un espace ex: distinct field ou field AS alias
{
$col = explode(' ',$v);
if(strtolower($col[1])==='as') $row$k] = $needle.$v;
else $row$k] = current($col).' '.$needle.end($col);
}
else $row$k] = $needle.$v;
}
}
}//fin pas de virgule
}//fin bouble
return implode(',',$row);
}
/**
*
*
**/
private function buildJoinBlocks($req){
is_array($req) or $req = array($req);
$output=array();
$aConditions = array();
$aRules = array();
$aFields = array();
foreach($req as $join)
{
if(!isset($join'table']) or empty($join'table'])) continue;
$jointable= $join'table'];
if(isset($join'fields']) and !empty($join'fields']))
{
$aFields] = $this->addReference($join'fields'],$jointable);
}
$relationList = array('=','<>','<','>','<=','>=','IS','IS NOT','IN','NOT IN','LIKE','NOT LIKE','REGEXP','RLIKE','NOT REGEXP');
$jointype = isset($join'type'])? strtolower($join'type']) : '';
$joinstring = '';
switch($jointype){
case'left':
case'l':
$joinstring = ' LEFT JOIN '.$jointable;
break;
case'right':
case'r':
$joinstring = ' RIGHT JOIN '.$jointable;
break;
case'inner':
case'i':
$joinstring = ' INNER JOIN '.$jointable;
break;
case'outer':
case'o':
$joinstring = ' OUTER JOIN '.$jointable;
break;
case'full':
case'f':
$joinstring = ' FULL JOIN '.$jointable;
break;
case'cross':
case'c':
$joinstring = ' CROSS JOIN '.$jointable;
break;
case'natural':
case'n':
$joinstring = ' NATURAL JOIN '.$jointable;
break;
case'union':
case'u':
$joinstring = ' UNION JOIN '.$jointable;
break;
case'leouter':
case'lo':
$joinstring = ' LEFT OUTER JOIN '.$jointable;
break;
case'riouter':
case'ro':
$joinstring = ' RIGHT OUTER JOIN '.$jointable;
break;
case'fulouter':
case'fo':
$joinstring = ' FULL OUTER JOIN '.$jointable;
break;
default:
$joinstring = ' JOIN '.$jointable;
break;
}
if(isset($join'rules']))
{ $joinstring .= ' ON ';
$rul=array();
foreach($join'rules'] as $r=>$rule)
{ $rel=(isset($rule'rel']) and in_array(strtoupper($rule'rel']),$relationList))? strtoupper($rule'rel']) : '=';
if(!isset($rule'fktable']) and !isset($rule'fkindex']) and isset($rule'value']))
{ $rvalue=!is_numeric($rule'value'])? $this->db->quote($rule'value']) : $rule'value'];
$rkey=$this->addReference($rule'index'],$jointable);
$rul]= "$rkey$rel$rvalue";
}
else
{
$fktable = (isset($rule'fktable']) and !empty($rule'fktable']))? strtolower($rule'fktable']): $this->table;
$rul]= $this->addReference($rule'index'],$jointable).$rel.$this->addReference($rule'fkindex'],$fktable);
}
}
$aRules] = $joinstring.implode(' AND ',$rul);
}
else{//pas de clause pour la jointure
$aRules] = $joinstring;
}//fin de if joinrules
if(isset($join'conditions']))
{
foreach($join'conditions'] as $c=>$cond)
{
$k = $this->addReference($cond'field'],$jointable);
$v = (!is_numeric($cond'value']))? $this->db->quote($cond'value']) : $cond'value'];
$op=(isset($cond'rel']) and in_array(strtoupper($cond'rel']),$relationList))? strtoupper($cond'rel']) : '=';
$aConditions] = "$k$op$v";
}
}
}//FIN FOR EACH $jointable
if(sizeof($aFields)>0) $output'fields']= implode(',', $aFields);
if(sizeof($aRules)>0) $output'rules']= implode(' ', $aRules);
if(sizeof($aConditions)>0) $output'conditions']= implode(' AND ', $aConditions);
return (array) $output;
}
/**
*selection d'un ou +sieurs colonne dans la bdd
*
**/
public function find($req)
{ //verification de la presence des jointures
if(isset($req'join'])) $aJoinBlocks = $this->buildJoinBlocks($req'join']);
//construction de la requete finale
$sql = 'SELECT ';
if(isset($req'fields']))
{
$sql .= $this->addReference($req'fields']);
if(isset($aJoinBlocks'fields'])) $sql .= ','.$aJoinBlocks'fields'];
}
elseif(isset($aJoinBlocks'fields']))
{
$sql .= $aJoinBlocks'fields'];
}
else
{
$sql .= $this->table.'.*';
}
$sql .= ' FROM '.$this->table.' ';// ' as '.get_class($this).' '
if(isset($aJoinBlocks'rules'])) $sql .= $aJoinBlocks'rules'];
//construction de la condition
if(isset($req'conditions']) or isset($aJoinBlocks'conditions']))
{
$sql .= ' WHERE ';
if(isset($req'conditions']))
{
if(!is_array($req'conditions']))
{
$sql .= $req'conditions'];
}
else
{
$cond = array();
foreach($req'conditions'] as $k=>$v)
{
if(!is_numeric($v)) $v = $this->db->quote($v);
$k = $this->addReference($k);
$cond] = "$k=$v";
}
$sql .= implode(' AND ', $cond);
}
if(isset($aJoinBlocks'conditions'])) $sql .= ' AND '.$aJoinBlocks'conditions'];
}
else
{//la condition porte uniquement sur les tables jointes
$sql .= $aJoinBlocks'conditions'];
}//fin de if(isset($req'conditions']))
}//fin de if(isset($req'conditions']) or isset($aJoinBlocks'conditions']))
if(isset($req'groupby']))
{ $sql .= ' GROUP BY ';
if(!is_array($req'groupby']))
{
$sql .= $this->addReference($req'groupby']);
}
else
{ $gr = '';
foreach($req'groupby'] as $group)
{
if(!empty($group'fields']))
{ $groupTable = (isset($group'table']) and !empty($group'table']))? trim($group'table']) : $this->table;
$gr .= $this->addReference($group'fields'],$groupTable).',';
}
}
$sql .= trim($gr,',');
}
}
if(isset($req'orderby'])){
if(is_array($req'orderby'])){
$sql .= ' ORDER BY '.$this->addReference($req'orderby']'field']).' ';
$sql .= strtoupper($req'orderby']'direction']);
}//fin de if(is_array($req'orderby']))
} //fin de if(isset($req'orderby']))
if(isset($req'limit'])){
$sql .= ' LIMIT '.$req'limit'];
}
//return $sql;
$pre = $this->db->prepare($sql);
$pre->execute();
$arrAll = $pre->fetchAll();
//$pre->closeCursor();
//$pre = NULL;
return $arrAll;
}//fin de la function
ceci est une mise jour visant à simplifier dans un premier temps les noms des variables et à optimiser le code source.
changements majeurs
- $joindatas'name'] devient $join'table']
- $joindatas'joinType'] devient $join'type']
- ajout d'un nouveau parametre (optionnel) de jointure: $join'rules']'value']
- au niveau des tableaux, les index 'column', 'foreigntable','foreigncolumn' et 'relation' deviennent respectivement 'index' 'fktable', 'fkindex' et 'rel',
- la methode setColumnPrefix() devient addReference()
- optimisation de la methode addReference() qui est désormais 20 lignes plus courte que précédemment.
- le traitement des jointures à été isolé de la methode "find" pour encore plus de flexibilité et fait désormais l'objet d'une nouvelle methode privée: buildJoinBlocks();
ceci afin d'eviter des codes répétitifs suite un besoin de modifier la methode save() de manière à ce qu'elle accepte les jointures en cas de update. -
mise à jour de la methode buildJoinBlocks(): avant il n'était que possible de faire:
join T1 ON T1.field1 = T2.field2
désormais grâce au nouveau parametre $join'rules']'value']
il est possible de faire:
join T1 ON T1.field1 = value
pas de bug connu à ce jour. et merci de me faire part des éventuels bugs.
prochaine mise à jour: modification de la methode save() en vue de la prise en charge des jointure