salut la communauté sa fait pratiquement deux semaine je case la tête pour creer une condition left join à travers la classe modele du tuto developpez un site de A à Z je vous montre ma classe un peut modifier :

Mes tables sont nommée par la convention suivante : nom du champs_table au pluriel. exemple : name_medias

<?php

/**
* Class qui interagie avec les données
* @ les differentes fonctions global sql ou autre
**/

class Model {

static $connexions = array();
public $conf = 'babd';
public $table = false;
public $db;
public $primarykey = false;
public $id;
public $errors = array();
public $Form;

public function __construct() {
//j'initialise qques variables
if($this->table === false){
$this->table = strtolower(get_class($this)).'s';
}
if($this->primarykey === false){
$this->primarykey = 'id_'.strtolower(get_class($this)).'s';
}
//connexions a la base de données avec Pdo
$data = Conf::$databases$this->conf];

if(isset(Model::$connexions$this->conf])){
$this->db = Model::$connexions$this->conf];
return true;
}

try {

$dns = 'mysql:host = '.$data'host'].';dbname='.$data'database'];
$user = $data'login'] ;
$pwd = $data'password'];
// Options de connection
$options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
);

$pdo = new PDO($dns, $user, $pwd, $options);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
Model::$connexions$this->conf] = $pdo ;
$this->db = $pdo;

} catch (Exception $e) {
if(Conf::$debug >= 1){
echo $e->getMessage();
} else {
die('Impossible de se connectez à la base de données');
}
}

}
public function find($req = array()){

//construction de la selection des champs

$sql = 'SELECT ' ;
if (isset($req'fields'])) {
if(is_array($req'fields'])){
$sql .= implode(', ',$req'fields']);
}else{
$sql .= $req'fields'];
}

}else{
$sql .= '*';
}
$sql .= ' FROM '.$this->table.' as '.get_class($this).' ' ;
// contruction de la requete avec left join
//on verifie si les condition existe
if(isset($req'table']) && isset($req'condj'])){
//on verifie si les conditions sont different d'un tableau
if(!is_array($req'table']) && !is_array($req'condj'])){
$sql .= 'LEFT JOIN '.$req'table'].' as '.ucfirst(substr($req'table'], 0 ,-1)).' ON '.$req'condj'].' ';
}else{
// si les conditions sont des tableaux
$tab = array();
foreach ($req'table'] as $k => $v){
$cdj = array();
foreach ($req'condj'] as $y => $w) {
if(!is_numeric($v)){
$w = '"'.mysql_escape_string($w).'"';
}
$cdj] = "$w";
}
$tab] ='LEFT JOIN '.$v.' as '. ucfirst(substr($v, 0 ,-1)).' ON '.$w.' ' ;
}
$sql .= implode(' ', $tab);
}
}
debug($sql);
die();
//contruction de la conditions

if(isset($req'conditions'])){
$sql.= 'WHERE ';

// tester si la condition est un tableau ou pas
if(!is_array($req'conditions'])){
$sql.= $req'conditions'];
}else{
$cond = array();
foreach ($req'conditions'] as $k => $v) {
if(!is_numeric($v)){
$v = "'".mysql_escape_string($v)."'";
}
$cond] = "$k=$v";
}
$sql .= implode(' AND ',$cond);
}

}
// Construction de la groupe
if (isset($req'group'])) {
$sql .=' GROUP BY ';
if(!is_array($req'group'])){
$sql .=$req'group'];
}else{
$cond = array();
foreach ($req'group'] as $k ) {
if(!is_numeric($v)){
$v = '"'.mysql_escape_string($v).'"';
}
$cond] = "$k";
}
$sql .= implode(' , ', $cond);
}
}
/* debug($sql);
die();*/
if (isset($req'order'])) {
$sql .=' ORDER BY ';
if(!is_array($req'order'])){
$sql .=$req'order'];
}else{
$cond = array();
foreach ($req'order'] as $k ) {
if(!is_numeric($v)){
$v = '"'.mysql_escape_string($v).'"';
}
$condo] = "$k";
}
$sql .= implode(' , ', $condo);
}
}
if(isset($req'limit'])){
$sql .= 'LIMIT'.$req'limit'];
}
$pre = $this->db->prepare($sql);
$pre->execute();
return $pre->fetchAll(PDO::FETCH_OBJ);
}

public function findFirst($req){

return current($this->find($req));
}

public function findCount($conditions = NULL){
if(isset($conditions)){
$res = $this->findFirst(array(
'fields' => 'COUNT('.$this->primarykey.') as count',
'conditions' => $conditions
));
} else {
$res = $this->findFirst(array(
'fields' => 'COUNT('.$this->primarykey.') as count'
));
}
return $res->count;
}

public function delete($id){
$sql = "DELETE FROM {$this->table} WHERE {$this->primarykey} = $id";
$this->db->query($sql);
}

public function save($data){
$key = $this->primarykey;
$fields = array();
$d = array();
foreach ($data as $k=>$v){
if($k != $this->primarykey){
$fields] = "$k=:$k";
$d":$k"] = $v;
}elseif(!empty ($v)){
$d":$k"] = $v;
}
}
if(isset($data->$key) && !empty($data->$key)){
$sql = 'UPDATE '.$this->table.' SET '.implode(',',$fields).' WHERE '.$key.'=:'.$key ;
$this->id = $data->$key;
$action = 'update';
} else {
$sql = 'INSERT INTO '.$this->table.' SET '.implode(',',$fields);
$action = 'insert';
}
/*debug($d);
debug($action);
debug($sql);*/
$pre = $this->db->prepare($sql);
$pre->execute($d);
if($action == 'insert'){
$this->id = $this->db->lastInsertId();
}
}
}

?>

si quelqu'un a une idée merci de m'aidez ..!

3 réponses


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

De façon generale, les jointures doivent être déclarées dans un tableau comme suit:

/*
* CONSTRUCTION DES JOINTURES
*
*/  
/*
* jointure de la premiere table
* on peut joindre plusieurs tables. dans ce cas 
* on incrémente la valeur de l'indice $i.
*/  
$i=0;//initialisation: 1ere jointure    
$join$i]'table']    = 'menutypes';//nom de la table à joindre
$join$i]'fields'] = array(); //si vous souhaitez recuperer les données de certains champs de cette table, specifiez les ici
$join$i]'type'] = 'left';//inner|left|right|outer|full|cross|leouter|riouter, : type de jointure dites moi si j'oublie qq chose
/*
* Règles de jointure de la premiere table
* on peut definir plusieurs regles de jointure. dans ce cas 
* on incrémente la valeur de l'indice $j.
*
* $rules: situation provoquant la jointure
*/
$j=0; //initialisation: 1ere regle de jointure
$join$i]'rules']$j]'index'] = 'id';//champs de la table jointe sur la quelle sera basée la comparaison(filtre de jointure)
$join$i]'rules']$j]'fktable'] = '';//table contenant la clé étrangère et en fonction de laquelle se fera la jointure;
$join$i]'rules']$j]'fkindex'] = 'typeid';//champ (la clé etrangere) nécessaire pour le tri      
$join$i]'rules']$j]'rel'] = '=';/* =,<>,<,>,<=,>= //operateur de comparaison des champs:(Ex: index >= fkindex)*/
/*
* Conditions à remplir pour certains champs de la table jointe (courante)
* on peut definir plusieurs conditions. dans ce cas 
* on incrémente la valeur de l'indice $k.
*
* $conditions: condition à associer à d'autres clauses (where) si elles existent ou non
*/
$k=0;//initialisation: 1ere condition
$join$i]'conditions']$k]'field']    = 'online';
$join$i]'conditions']$k]'value']    = '1';
$join$i]'conditions']$k]'rel'] = '=';
$k++;// 2e condition
$join$i]'conditions']$k]'field']    = 'name';
$join$i]'conditions']$k]'value']    = 'principal';
$join$i]'conditions']$k]'rel'] = '=';/* valeurs possibles: =, <>, <, >, <=, >= */
/*
* deuxieme règle de jointure
*/  $i++; $j=0; $k=0;//on incremente le $i puis $j et $k sont réinitialisés;          
$join$i]'table']    = 'posts';//nom de la table à joindre
$join$i]'fields'] = array('id','name','content', 'type','max(content) as allcontents'); //
$join$i]'type'] = 'full';//voir plus haut
//1ere regle de jointure sur la 2e table jointe
$join$i]'rules']$j]'index'] = 'slug';//champs de TableTjoin sur la quelle sera basée la jointure
$join$i]'rules']$j]'fktable'] = '';//table contenant la clé étrangère et en fonction de laquelle se fera la jointure;
$join$i]'rules']$j]'fkindex'] = 'slug';//champ (la clé etrangere)   nécessaire pour le tri      
$join$i]'rules']$j]'rel'] = '=';
//$join$i]'rules']$j]'xor'] = 'AND';
//1ere condition à verifier par les champs de la 2e table jointe
$join$i]'conditions']$k]'field']    = 'online';
$join$i]'conditions']$k]'value']    = '1';
$join$i]'conditions']$k]'rel'] = '=';
$k++;//deuxieme condition
$join$i]'conditions']$k]'field']    = 'name';
$join$i]'conditions']$k]'value']    = 'footer';
$join$i]'conditions']$k]'rel'] = '=';
//$join$i]'conditions']$k]'xor'] = 'AND';   
/*
* CONSTRUCTION DU GROUP BY
*
*/                        
$x=0;                                 
$groupby$x]'table'] = 'menus';                            
$groupby$x]'fields'] = array('id','parent','catid');    
$x++;
$groupby$x]'table'] = 'contents';                             
$groupby$x]'fields'] = array('id','name');                        
/*
* CONSTRUCTION DE LA REQUETE FINALE
*
**/                               
$options=array( 'fields'=>'id,name,count(user) As users',
                'join'=>$join,
                'conditions'=>array('online'=>1),
                'orderby'=>array('field'=> 'id', 'direction'=>'asc'),
                'groupby'=>$groupby
            );
echo find($options);

ce qui execute en sortie cette requete:

SELECT menus.id,menus.name,count(menus.user) As users,
posts.id,posts.name,posts.content,posts.type,max(posts.content) as allcontents 
FROM menus 
LEFT JOIN menutypes 
ON menutypes.id=menus.typeid 
FULL JOIN posts 
ON posts.slug=menus.slug 
WHERE 
menus.online=1 AND menutypes.online=1 AND menutypes.name=principal AND posts.online=1 AND posts.name=footer 
GROUP BY menus.id,menus.parent,menus.catid,contents.id,contents.name 
ORDER BY menus.id ASC

soit la requete suivante:

SELECT CLIENT.NomClient,
SUM(COMMANDE.TotalTTC) AS TotalTTC
FROM CLIENT 
LEFT OUTER JOIN COMMANDE
ON CLIENT.id = COMMANDE.ClientId
GROUP BY CLIENT.NomClient

pour l'executer avec cette fonction, on doit proceder coe suit:

$i=0;//initialisation   
$join$i]'table']    = 'COMMANDE';//nom de la table à joindre
$join$i]'fields'] = 'SUM(TotalTTC) AS TotalTTC';
$join$i]'type'] = 'leouter';//inner|left|right|outer|full|cross|leouter|riouter, : type de jointure dites moi si j'oublie qq chose
//clauses de jointure de la premiere table(situation provoquant la jointure)
$j=0; //initialisation
$join$i]'rules']$j]'index'] = 'ClientId';//champs de la table jointe sur la quelle sera basée la comparaison(filtre de jointure)
$join$i]'rules']$j]'fktable'] = 'CLIENT';//table contenant la clé étrangère et en fonction de laquelle se fera la jointure;
$join$i]'rules']$j]'fkindex'] = 'id';//champ (la clé etrangere) nécessaire pour le tri      
$join$i]'rules']$j]'rel'] = '=';// =,<>,<,>,<=,>= //operateur de comparaison des champs:(Ex: field >= fktable)
$x=0;                                 
$groupby$x]'table'] = 'CLIENT';                           
$groupby$x]'fields'] = array('NomClient');
//construction de la requete finale
$req=array( 'fields'=>'NomClient',
                'join'=>$join,
                //'conditions'=>array('online'=>1),
                //'orderby'=>array('field'=> 'id', 'direction'=>'asc'),
                'groupby'=>$groupby
                        );