From e6c81685fb760e660fe423aa40f0c838c48d657d Mon Sep 17 00:00:00 2001 From: xdrm-brackets Date: Sun, 24 Jul 2016 17:42:54 +0200 Subject: [PATCH] =?UTF-8?q?ORM:=20Refactor=20complet=20gestion=20du=20FETC?= =?UTF-8?q?H=20avec=20possibilit=C3=A9=20de=20select=20parmi=20toutes=20le?= =?UTF-8?q?s=20jointures=20+=20de=20faire=20des=20jointures=20si=20la=20ci?= =?UTF-8?q?ble=20est=20la=20m=C3=AAme,=20si=20la=20jointure=20est=20la=20c?= =?UTF-8?q?ible=20et=20inversement=20+=20gestion=20des=20jointures=20+=20g?= =?UTF-8?q?estion=20des=20conditions=20des=20jointures=20dans=20la=20claus?= =?UTF-8?q?e=20FROM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manager/ORM/Rows.php | 154 +++++++++++++++++++++++++---------- manager/ORM/SQLBuilder.php | 159 +++++++++++++++++++++++++++---------- test/automate.php | 70 ++++++++++------ view/js/groups.js | 2 - 4 files changed, 271 insertions(+), 114 deletions(-) diff --git a/manager/ORM/Rows.php b/manager/ORM/Rows.php index b644fe3..48d96f1 100644 --- a/manager/ORM/Rows.php +++ b/manager/ORM/Rows.php @@ -270,33 +270,75 @@ /* JOINT UNE SECONDE TABLE () * - * @field Nom d'une colonne + * @localField Nom d'une colonne locale * @rows Rows d'une autre table * * @return this Retourne l'object courant * */ - public function join($field, $rows){ - /* [0] Vérification des paramètres + public function join($localField, $rows){ + /* [0] Vérification / Formattage des paramètres =========================================================*/ - if( !is_string($field) || !($rows instanceof Rows) ) + /* (1) Si le champ n'est pas au bon format */ + if( !is_string($localField) ) return $this; + /* (2) Si @rows n'est pas au bon format */ + if( !($rows instanceof Rows) ) + return $this; + + /* (3) Si le champ n'existe pas dans la table */ + if( !isset($this->schema['columns'][$localField]) ) + return $this; + + /* (4) On récupère les données du champ local dans une variable */ + $localFieldData = $this->schema['columns'][$localField]; /* [1] On vérifie que la clé étrangère est correcte =========================================================*/ - /* (1) On vérifie que la colonne existe et qu'elle est étrangère*/ - if( !isset($this->schema['columns'][$field]['references']) ) + /* (1) Si la colonne n'existe pas et qu'elle n'est pas primaire, on ne fait rien */ + if( !isset($localFieldData['references']) && !$localFieldData['primary'] ) return $this; - /* (2) On vérifie que la table de @rows est correcte */ - if( $this->schema['columns'][$field]['references'][0] != $rows->schema['table'] ) + /* (2) On vérifie que la colonne a une référence vers la table de @rows */ + $referencesToRows = isset($localFieldData['references']) && $localFieldData['references'][0] == $rows->schema['table']; + $rowsField = null; + + /* (3) On vérifie que la colonne est la référence d'un champ de @rows */ + $referencesFromRows = false; + + // On vérifie chaque champ de @rows + foreach($rows->schema['columns'] as $field=>$data) + // Si un champ de la table de @rows a pour référence le champ local + if( isset($data['references']) && $data['references'][0] == $this->schema['table'] && $data['references'][1] == $localField ){ + $referencesFromRows = true; + $rowsField = $field; + break; + } + + /* (4) On vérifie que la colonne a la même référence qu'une colonne de @rows */ + $referencesSameTarget = false; + + // On vérifie toutes les colonnes de @rows + foreach($rows->schema['columns'] as $field=>$data) + // Si on trouve un champ avec la même référence + if( isset($data['references']) && isset($localFieldData['references']) && count(array_diff($data['references'], $localFieldData['references'])) == 0 ){ + $referencesSameTarget = true; + $rowsField = $field; // On enregistre le champ qui a la même cible + break; + } + + /* (4) Si aucune référence en commun, on ne fait rien */ + if( !$referencesToRows && !$referencesFromRows && !$referencesSameTarget ) return $this; /* [2] On enregistre la référence =========================================================*/ - $this->joined[$field] = $rows; + $this->joined[$localField] = [ + 'object' => $rows, + 'field' => is_null($rowsField) ? $localFieldData['references'][1] : $rowsField // On met le nom du champ de @rows à lier + ]; /* [3] On retourne l'object courant @@ -416,7 +458,7 @@ $requestS .= $this->schema['table'].'.'.$localField." in (\n\t"; // {2} On récupère la requête // - $RowsFetched = $rows->fetch(false); + $RowsFetched = $rows[0]->fetch(false); // {3} On ajoute la requête // $requestS .= implode("\n\t", explode("\n", $RowsFetched['request'])); @@ -625,7 +667,7 @@ $requestS .= $this->schema['table'].'.'.$localField." in (\n\t"; // {2} On récupère la requête // - $RowsFetched = $rows->fetch(false); + $RowsFetched = $rows[0]->fetch(false); // {3} On ajoute la requête // $requestS .= implode("\n\t", explode("\n", $RowsFetched['request'])); @@ -676,6 +718,16 @@ * */ public function fetch($execute=true){ + /* [0] On initialise + =========================================================*/ + /* (1) On initialise la requête */ + $requestS = []; + + /* (2) On initialise le conteneur des variables "bindés" */ + $binded = []; + + + /* [1] On rédige la clause SELECT =========================================================*/ /* (1) On formatte les données */ @@ -686,81 +738,95 @@ /* (3) On ajoute les champs des jointures */ foreach($this->joined as $rows) - $selectTables[$rows->schema['table']] = $rows->select; + $selectTables[$rows['object']->schema['table']] = $rows['object']->select; /* (4) On génère la clause SELECT */ - $requestS = SQLBuilder::SELECT($selectTables)."\n"; + $requestS['SELECT'] = SQLBuilder::SELECT($selectTables); + /* [2] On rédige la clause FROM ========================================================*/ + /* (0) On initialise la clause */ + $requestS['FROM'] = []; + /* (1) Table locale */ - $requestS .= SQLBuilder::FROM( array_keys($selectTables) )."\n"; + $requestS['FROM'][] = $this->schema['table']; + + /* (2) On ajoute les tables de jointures */ + // Note: On ajoute les tables de jointures dans la clause FROM avec comme alias le nom de la table + foreach($this->joined as $field=>$data){ + // {1} On récupère la requête/les params de chaque jointure // + $joinedFetched = $data['object']->fetch(false); + + // {2} On met '*' pour la clause SELECT // + $joinedFetched['request']['SELECT'] = [ $data['object']->schema['table'].'.*' ]; + + // {3} On construit la nouvelle requête // + $joinedRequest = SQLBuilder::BUILD($joinedFetched['request']); + + // {4} On supprime les retours à la ligne // + $joinedRequest = str_replace("\n", " ", $joinedRequest); + + // {5} On l'ajoute à la clause FROM avec comme alias le nom de la table de @data['object'] // + $requestS['FROM'][] = "($joinedRequest) as ".$data['object']->schema['table']; + + // {6} On ajoute les variables à la requête courante // + $binded = array_merge($binded, $joinedFetched['binded']); + } + /* [5] On rédige la clause WHERE/AND =========================================================*/ - /* (0) On initialise le conteneur des variables "bindés" */ - $binded = []; - - /* (1) On met les conditions locales */ $c = 0; + $requestS['WHERE'] = []; foreach($this->where as $field=>$conditions) foreach($conditions as $cdt=>$value){ if( $value[1] === self::COND_IN ) // Si condition IN - $requestS .= SQLBuilder::IN([$this->schema['table'], $field], $value[0], $c, $binded)."\n"; + $requestS['WHERE'][$c] = SQLBuilder::IN([$this->schema['table'], $field], $value[0], $c, $binded); else // Sinon - $requestS .= SQLBuilder::WHERE([$this->schema['table'], $field], $value, $c, $binded)."\n"; + $requestS['WHERE'][$c] = SQLBuilder::WHERE([$this->schema['table'], $field], $value, $c, $binded); $c++; } /* (2) On ajoute les jointures */ - foreach($this->joined as $localField=>$rows){ - if( $c == 0 ) $requestS .= 'WHERE '.$this->schema['table'].'.'.$localField.' = '.$this->schema['columns'][$localField]['references'][0].'.'.$this->schema['columns'][$localField]['references'][1]."\n"; - else $requestS .= 'AND '.$this->schema['table'].'.'.$localField.' = '.$this->schema['columns'][$localField]['references'][0].'.'.$this->schema['columns'][$localField]['references'][1]."\n"; + foreach($this->joined as $localField=>$data){ + $requestS['WHERE'][$c] = $this->schema['table'].".$localField = ".$data['object']->schema['table'].".".$data['field']; $c++; } - /* (3) On ajoute les conditions des jointures */ - foreach($this->joined as $rows) - foreach($rows->where as $field=>$conditions) - foreach($conditions as $cdt=>$value){ - - if( $value[1] === self::COND_IN ) // Si condition IN - $requestS .= SQLBuilder::IN([$rows->schema['table'], $field], $value[0], $c, $binded)."\n"; - else // Sinon - $requestS .= SQLBuilder::WHERE([$rows->schema['table'], $field], $value, $c, $binded)."\n"; - - $c++; - } - /* [6] Clause LIMIT =========================================================*/ - if( $this->unique ) - $requestS .= "LIMIT 1"; + $requestS['LIMIT'] = ($this->unique) ? SQLBuilder::LIMIT(1) : SQLBuilder::LIMIT([]); - /* [7] On exécute la requête + /* [7] On compose/prépare la requête =========================================================*/ /* (1) Si on veut pas exécuter on renvoie la requête + bindedParams */ if( !$execute ) return [ 'request' => $requestS, 'binded' => $binded]; - /* (2) On prépare la requête */ - $request = Database::getPDO()->prepare($requestS.';'); + /* (2) On compose la requête */ + $requestString = SQLBuilder::BUILD($requestS).';'; + // var_dump($requestString); + // var_dump($binded); + // exit(); + + /* (3) On prépare la requête */ + $request = Database::getPDO()->prepare($requestString); /* [8] On exécute la requête et retourne le résultat =========================================================*/ /* (1) On exécute la requête */ $request->execute($binded); - /* (2) Si unique */ if( $this->unique ) return $this->format( $request->fetch() ); @@ -826,8 +892,8 @@ $existingColumns = $this->schema['columns']; // {2} On ajoute les colonnes des jointures // - foreach($this->joined as $rows) - $existingColumns = array_merge( $existingColumns, $rows->schema['columns'] ); + foreach($this->joined as $j) + $existingColumns = array_merge( $existingColumns, $j['object']->schema['columns'] ); // {3} On vérifie chaque clé, si c'est une colonne qui existe // foreach($formatted as $i=>$entry) diff --git a/manager/ORM/SQLBuilder.php b/manager/ORM/SQLBuilder.php index a657ba2..bb4343c 100644 --- a/manager/ORM/SQLBuilder.php +++ b/manager/ORM/SQLBuilder.php @@ -9,26 +9,24 @@ class SQLBuilder{ - /* CONSTRUIT LA REQUETE TEXTUELLE "SELECT" AVEC UNE LISTE DE CHAMPS + /* CONSTRUIT LA REQUETE FORMATTEE "SELECT" AVEC UNE LISTE DE CHAMPS * - * @tables Liste de tables contenant des champs + * @sqlFields Liste de champ/tables contenant des champs * - * @return sql Renvoie la clause remplie + * @return sql Renvoie un tableau formatté * */ - public static function SELECT($tables){ + public static function SELECT($sqlFields){ /* [0] Initialisation =========================================================*/ - $sql = 'SELECT '; + $sql = []; /* [1] On construit la requête =========================================================*/ $c = 0; - foreach($tables as $table=>$fields) + foreach($sqlFields as $table=>$fields) foreach($fields as $field){ - if( $c > 0 ) $sql .= ', '; - - $sql .= $table.'.'.$field; + $sql[$c] = $table.'.'.$field; $c++; } @@ -41,30 +39,15 @@ - /* CONSTRUIT LA REQUETE TEXTUELLE "FROM" AVEC UNE LISTE DE TABLES + /* CONSTRUIT LA REQUETE FORMATTEE "FROM" AVEC UNE LISTE DE TABLES * - * @tables Liste de tables + * @tables Liste de tables OU SQL PUR * - * @return sql Renvoie la clause remplie + * @return sql Renvoie un tableau formatté * */ public static function FROM($tables){ - /* [0] Initialisation - =========================================================*/ - $sql = 'FROM '; - - /* [1] On construit la requête - =========================================================*/ - $c = 0; - foreach($tables as $table){ - if( $c > 0 ) $sql .= ', '; - - $sql .= $table; - - $c++; - } - - return $sql; + return $tables; } @@ -72,21 +55,21 @@ - /* CONSTRUIT LA REQUETE TEXTUELLE "UPDATE" AVEC LA TABLE EN QUESTION + /* CONSTRUIT LA REQUETE FORMATTEE "UPDATE" AVEC LA TABLE EN QUESTION * * @table Table en question * - * @return sql Renvoie la clause remplie + * @return sql Renvoie un tableau formatté * */ public static function UPDATE($table){ /* [0] Initialisation =========================================================*/ - $sql = 'UPDATE '; + $sql = []; /* [1] On construit la requête =========================================================*/ - $sql .= $table; + $sql[] = $table; return $sql; } @@ -103,13 +86,13 @@ * @offset Permet de rendre la condition unique (nommage des variables) * @binded Tableau associatif contenant les variables "bindés" -> ajout des champs * - * @return sql Renvoie la clause remplie + * @return sql Renvoie le textuel formatté * */ public static function IN($field, $array, $offset=0, &$binded){ /* [0] Initialisation =========================================================*/ - $sql = ( $offset == 0 ) ? 'WHERE ' : 'AND '; + $sql = ''; /* [1] On construit la requête =========================================================*/ @@ -142,13 +125,13 @@ * @offset Permet de rendre la condition unique (nommage des variables) * @binded Tableau associatif contenant les variables "bindés" -> ajout des champs * - * @return sql Renvoie la clause remplie + * @return sql Renvoie le textuel formatté * */ public static function WHERE($field, $value, $offset=0, &$binded){ /* [0] Initialisation =========================================================*/ - $sql = ( $offset == 0 ) ? 'WHERE ' : 'AND '; + $sql = ''; /* [1] On construit la requête @@ -172,18 +155,18 @@ - /* CONSTRUIT LA REQUETE TEXTUELLE "SET" AVEC UNE LISTE DE TABLES + /* CONSTRUIT LA REQUETE FORMATTEE "SET" AVEC UNE LISTE DE TABLES * * @values Tableau de la forme [ field=>value, field2=>value2 ] * @binded Tableau associatif contenant les variables "bindés" -> ajout des champs * - * @return sql Renvoie la clause remplie + * @return sql Renvoie un tableau formatté * */ public static function SET($values, &$binded){ /* [0] Initialisation =========================================================*/ - $sql = 'SET '; + $sql = []; /* [1] On construit la requête @@ -191,11 +174,12 @@ $c = 0; foreach($values as $field=>$value){ /* (1) Champ */ - if( $c > 0 ) $sql .= "\n, "; - $sql .= $field.' = '; + $sql[$c] = ($c>0) ? "\n, " : ''; + + $sql[$c] .= $field.' = '; /* (2) Variable */ - $sql .= ':update_'.$field; + $sql[$c] .= ':update_'.$field; $binded[':update_'.$field] = $value; @@ -207,6 +191,97 @@ + + + /* CONSTRUIT LA REQUETE FORMATTEE "LIMIT" AVEC UN NOMBRE D'ENTREES + * + * @count Nombre limite + * + * @return sql Renvoie un sql formatté + * + */ + public static function LIMIT($count=null){ + /* [0] Initialisation + =========================================================*/ + $sql = []; + + + /* [1] On construit la requête + =========================================================*/ + if( intval($count) == $count ) + $sql = intval($count); + + return $sql; + } + + + + + + + + + + + + + + /* CONSTRUIT LA REQUETE A PARTIR D'UNE REQUETTE FORMATTEE + * + * @request Requête formattée + * + * @return sql Requête formattée en SQL + * + */ + public static function BUILD($request){ + /* [1] Clause SELECT + =========================================================*/ + $sql = 'SELECT '; + + $c = 0; + foreach($request['SELECT'] as $field){ + $sql .= ($c==0) ? $field : ", $field"; + $c++; + } + + $sql .= "\n"; + + /* [2] Clause FROM + =========================================================*/ + $sql .= 'FROM '; + + $c = 0; + foreach($request['FROM'] as $field){ + $sql .= ($c==0) ? $field : ", $field"; + $c++; + } + + $sql .= "\n"; + + /* [3] Clause WHERE + =========================================================*/ + $c = 0; + foreach($request['WHERE'] as $field){ + $sql .= ($c==0) ? "WHERE $field\n" : "AND $field\n"; + $c++; + } + + $sql .= ($c==0) ? '' : "\n"; + + + /* [4] Clause LIMIT + =========================================================*/ + if( is_numeric($request['LIMIT']) ) + $sql .= 'LIMIT '.intval($request['LIMIT']); + + + /* [5] On retourne le résultat + =========================================================*/ + return $sql; + } + + + } diff --git a/test/automate.php b/test/automate.php index bfa7657..959e287 100755 --- a/test/automate.php +++ b/test/automate.php @@ -345,24 +345,25 @@ /* [1] FETCH =========================================================*/ - // $warehouse = - // Table::get('warehouse') // Access to table 'warehouse' - // ->whereName(['stef-montauban', Rows::COND_EQUAL]); // condition : name = 'my-warehouse' - // - // - // $myUser = - // Table::get('user') // Access to table 'user' - // ->whereId([100, Rows::COND_INF]) // PRIMARY KEY (other condition on same field) - // ->whereId([[1,4,6,9], Rows::COND_IN]) // PRIMARY KEY (other condition on same field) - // ->whereUsername(['%e%', Rows::COND_LIKE]) // Dynamic getter 'getByMySuperColumn' -> 'my_super_column' - // ->select(['mail', 'username', 'firstname']) // Select clause - // ->select('id_user') // Select clause (added) - // ->join('id_warehouse', $warehouse) // joins warehouse (with name 'my-warehouse') to column 'id_warehouse' - // // ->unique() // unique result - // - // // SELECT - // ->fetch(); // Result - // var_dump($myUser); + $warehouse = + Table::get('warehouse') // Access to table 'warehouse' + ->whereName(['stef-montauban', Rows::COND_EQUAL]) // condition : name = 'my-warehouse' + ->select('name'); + + + $myUser = + Table::get('user') // Access to table 'user' + ->whereId([100, Rows::COND_INF]) // PRIMARY KEY (other condition on same field) + ->whereId([[92, 93], Rows::COND_IN]) // PRIMARY KEY (other condition on same field) + ->whereUsername(['%', Rows::COND_LIKE]) // Dynamic getter 'getByMySuperColumn' -> 'my_super_column' + ->select(['mail', 'username', 'firstname']) // Select clause + ->select('id_user') // Select clause (added) + ->join('id_warehouse', $warehouse) // joins warehouse (with name 'my-warehouse') to column 'id_warehouse' + // ->unique() // unique result + + // SELECT + ->fetch(); // Result + var_dump($myUser); /* [2] Modification =========================================================*/ @@ -436,17 +437,34 @@ // var_dump($myUser); - $module = Table::get('module') - ->select('id_module'); - $warehouse = Table::get('warehouse') - ->whereId(7); + // $warehouse = Table::get('warehouse') + // ->whereName(['stef%', Rows::COND_LIKE]); + // + // $module_merge = Table::get('module_merge') + // ->join('id_warehouse', $warehouse); + // + // $module = Table::get('module') + // ->join('id_module', $module_merge); + // + // $chip = Table::get('chip') + // ->select('id_chip') + // ->join('id_module', $module); + // + // // $chip = Table::get('chip') + // // ->join('id_module', $module_merge) + // + // var_dump($chip->fetch()); - $module_merge = Table::get('module_merge') - ->join('id_module', $module) - ->join('id_warehouse', $warehouse); + // SELECT module.id_module + // FROM chip, + // (SELECT * FROM module) as module, + // (SELECT * FROM module_merge) as module_merge, + // (SELECT * FROM warehouse WHERE warehouse.name LIKE 'stef%') as warehouse + // WHERE chip.id_module = module.id_module + // AND module.id_module = module_merge.id_module + // AND module_merge.id_warehouse = warehouse.id_warehouse - var_dump($module_merge->fetch(false)); // $a = new ModuleRequest('authentificationDefault/warehouse', [ diff --git a/view/js/groups.js b/view/js/groups.js index 5c6fabc..9cadf05 100644 --- a/view/js/groups.js +++ b/view/js/groups.js @@ -1,5 +1,3 @@ -// TODO: Supprimer les retours vers la cible lors de 'création'/'modification' - // On referencie toutes les sections var section = { view: {