EauFroide : révision n°9 (23 février 2017 17:18:14)
Niveau requis pour utilisation : moyen.
Nécessite au moins PHP5.4+ avec PDO (php-mysql)
Ce tutoriel s'intègre dans une suite de tutoriel permettant d'appréhender l'[ajax](https://fr.wikipedia.org/wiki/Ajax_%28informatique%29) voir [Farm Link](#farm-link) à la fin du wiki.
# Introduction #
* Nous allons ici étudier un script PHP, rédigé par votre humble serviteur en [programmation fonctionnelle](https://fr.wikipedia.org/wiki/Programmation_fonctionnelle), permettant de faire de l'ajax. L'objectif d'origine de ces fonctions est de recevoir les requêtes GET provenant d'une interface cliente (page web angularjs) d'une boutique en ligne, de récupérer des articles depuis une table de Base de Données puis afficher les informations au format JSON.
Le but de ce topic est de vous aider a mettre en place le plus rapidement possible une fonction ajax côté serveur (pour les tuto côté client il faudra voir du côté du [Farm Link](#farm-link) en bas de page).
* Donc ce script analyse les variables $_GET afin de récupérer les informations lui permettant de forger des requêtes SQL. Ensuite il récupère les informations de la base de données MySQL/MariaDB et les affiche dans un JSON.
Pour fonctionner il faut bien entendu disposer d'une base de données contenant une table (nom gérable via $tableName) et contenant au moins les cellules : id, categorie.
Afin d'éviter les doublons entre deux requêtes clientes lorsque des articles ont des informations recherchées quasi identique: lors d'une requête GET le client doit mentionner les articles qu'il a visionné dernièrement en ajoutant dans ses requêtes GET les variables banItemX = identifiant article à bannir comme dans l'exemple suivant : ajax.php?banItem1=6&banItem2=7&banItem3=8
* La sécurité anti injection SQL est gérée suivant les cas par les [requêtes préparées de PDO](https://www.w3schools.com/TAgs/att_select_form.asp) ou par une moulinette qui vérifie la correspondance entre la table de données et les infos postées par l'utilisateur puis qui injecte les infos depuis la base de données en lieu et place des données envoyées par l'user. Sauf pour les $_GET["banItemX"] qui eux sont directement convertis en integer afin de casser toutes injections.
# Les paramètres GET acceptés par le script : #
## Paramètres obligatoires : ##
* **&articleMax=[[integer](http://php.net/manual/fr/language.types.integer.php)]** gère la [LIMITE SQL](http://sql.sh/cours/limit) (nombre d'entrées max à renvoyer)
* **&orderByAttributs=[[string](http://php.net/language.types.string)]** l'attribut pour ORDER BY (par exemple l'id, le prix, etc)
* **&orderByAttributsLastValue=[[integerfloat](http://php.net/manual/fr/language.types.integerfloat.php)]** la valeur la plus haute (ou basse selon la recherche) de l'attribut [ORDER BY](http://sql.sh/cours/order-by) afin de commencer la recherche à partir de là
* **&categorie=[[string](http://php.net/language.types.string)]** le nom de la catégorie d'article (peut facilement être remplacer par une seconde condition de recherche)
* **&orderByType=[1|-1]** décide du sens de la recherche (1 pour plus petit au plus grand (> et ASC), -1 pour plus grand au plus petit (< et DESC))
## Paramètres optionnels : ##
* **&forceChangeOrderSymbole=[1|2]** 1 pour > (plus grand que) ou 2 pour < (plus petit que) permet de changer le sens de la recherche sans affecter le reste (ASC ou DESC). Par défaut 1 (>) est sélectionné.
* **&banItem1=[[integer](http://php.net/manual/fr/language.types.integer.php)]** reçoit les identifiants à bannir (peut avoir un nombre "infini" (voir limite GET) ex: banItem2, banItem3, etc). Par défaut aucun élément n'est bannis.
## Exemples : ##
* **ww****w.hostname.be/ajaxArticles.php?articleMax=10&orderByAttributsLastValue=42&orderByAttributs=prix&orderByType=1&categorie=film**
* On demande à voir les 10 prochains articles après celui qui à l'id 42, classé par la cellule prix du plus petit au plus grand, de la catégorie film
# Le code #
**Pour l'installer créez un fichier.php et collez le code suivant dedans.**
```php
<?php
/* Licence GPLv3 - Papa : voxdemonix
infos : https://www.0rion.netlib.re/forum4/viewtopic.php?f=79&t=541&p=1156#p1156
*/
/* Je vous conseil d'insérer tout le bloc try dans un fichier séparé protégé par un .htaccess (deny for all) */
try {
/* éditez les informations nécessaire pour la connexion à la base de données */
$bdd = new PDO('mysql:host='.$main["bdd_Hostname"].';dbname='.$main['bdd_Name'].';charset=utf8', $main['bdd_Login'], $main['bdd_Pass']);
} catch( Exception $e){
error_log("ajax -> Error BDD connexion from ".$_SERVER["PHP_SELF"]);
}
$tableName = "Articles";
function displayInJsonMyArray($myArray){
header('Content-type: application/json');
print_r( json_encode($myArray, JSON_NUMERIC_CHECK) );
}
function articleOrderByList(){
//MYSQL
global $bdd;
$searchCategorieArticle = $bdd->prepare('SELECT * FROM '.$tableName.' LIMIT 1');
$searchCategorieArticle->execute();
//RECOVER THE RESULT IN A TAB
$tabCategorie = $searchCategorieArticle->fetch(PDO::FETCH_ASSOC);
$tab = array();
//READ THE TAB AND MAKE THE OPTIONS'S TAG
foreach ($tabCategorie as $key => $value) {
$tab[] = $key;
}
return $tab;
}
function articleCategorieList(){
$liste = array(); global $bdd;
$searchCategorieArticle = $bdd->prepare('SELECT `categorie` FROM '.$tableName);
$searchCategorieArticle->execute();
//MAINTENANT LISTE LES CATEGORIES DANS $tmpCategorie, (PERMET AUSSI DE CRÉER UN FORMULAIRE SELECT)
while($tmpCategorie = $searchCategorieArticle->fetch(PDO::FETCH_ASSOC)){
if (!in_array($tmpCategorie["categorie"], $liste)) {
$liste[] = $tmpCategorie["categorie"];
// var_dump($tmpCategorie); echo '<br />';
}
}
return $liste;
}
function genBanItemForMySQLRequest(){
$banRequest = '';
foreach( $_GET as $key => $value){
if (stripos( ' '.$key , 'banItem' )){
$banRequest .= ' AND (id != '.intval($value).')';
}
}
return $banRequest;
}
/*
EXPLICATIONS :
reçoit des variables GET du style :
opt &banItem1= reçoit les identifiants a bannir (peut avoir un nombre "infini" (voir limite GET) ex: banItem2, banItem3, etc)
&articleMax= gère la LIMITE SQL (nombre d'entrée max a renvoier)
&orderByAttributs= l'attribut pour ORDER BY (par exemple l'id, le prix, etc)
&orderByAttributsLastValue= la valeur la plus haute (ou basse selon la recherche) de l'attribut ORDER BY afin de commencer la recherche a partir de là
&categorie= la categorie d'article (peut facilement être remplacer par une seconde condition de recherche)
&orderByType= decide du sens de la recherche (1 pour plus petit au plus grand (> et ASC), -1 pour plus grand au plus petit (< et DESC))
opt &forceChangeOrderSymbole= permet de changer le sens (< ou >) de la recherche sans affecter le reste (ASC ou DESC)
*/
$forceChangeOrderSymbole = 0; //if = 1 => > (plus grand que); if = 2 => < (plus petit que)
if(filter_has_var(INPUT_GET, "forceChangeOrderSymbole")){
if(isset($_GET["forceChangeOrderSymbole"]) & !empty($_GET["forceChangeOrderSymbole"])){
switch(intval($_GET["forceChangeOrderSymbole"])){
case 1 :
case '1':
$forceChangeOrderSymbole = 1;
break;
case 2 :
case '2':
$forceChangeOrderSymbole = 2;
break;
}
}
}
if ( filter_has_var(INPUT_GET, "articleMax") & filter_has_var(INPUT_GET, "orderByType") & filter_has_var(INPUT_GET, "orderByAttributs") & filter_has_var(INPUT_GET, "orderByAttributsLastValue")) {
$wichCategorie = "all";
if (filter_has_var(INPUT_GET, "categorie")) {
$wichCategorie = strval($_GET["categorie"]); //categorie = idForTraduct, not the traducted version (https://www.0rion.netlib.re/forum4/viewtopic.php?f=9&t=211)
}
displaySomeArticleInJSONUniversal($_GET["articleMax"], $wichCategorie, $_GET["orderByAttributs"], $_GET["orderByAttributsLastValue"]);
}
function displaySomeArticleInJSONUniversal($nbLimit, $categorie, $inputOrderByAttributs, $inputOrderByAttributsLastValue){
// $iMinimal = l'identifiant unique minimal de l'article, afin d'envoyer les suivants
global $bdd; //PDO object
global $forceChangeOrderSymbole;
$orderByAttributs = 'id'; $orderByAttributsLastValue = 1;
/* security */
if(in_array(strval($inputOrderByAttributs) , articleOrderByList())){
$orderByAttributs = strval($inputOrderByAttributs);
$orderByAttributsLastValue = $inputOrderByAttributsLastValue;
}
$orderByType = 'ASC'; $orderSymbole = '>';
if(strval($_GET["orderByType"]) == '-1'){
$orderByType = 'DESC';
$orderSymbole = '<';
}
if($forceChangeOrderSymbole == 2){ $orderSymbole = '<'; }
if( strval($categorie == "all") || !in_array(strval($categorie), articleCategorieList() ) ){
$requestSomeArticles = $bdd->prepare("SELECT * FROM ".$tableName." WHERE `".$orderByAttributs."` ".$orderSymbole."= :orderByAttributsLastValue ".genBanItemForMySQLRequest()." ORDER BY `".$orderByAttributs."` ".$orderByType.", `id` ASC LIMIT :nbLimit");
}else{
$requestSomeArticles = $bdd->prepare("SELECT * FROM ".$tableName." WHERE `".$orderByAttributs."` ".$orderSymbole."= :orderByAttributsLastValue AND `categorie` = :categorie ORDER BY `".$orderByAttributs."` ".$orderByType.", `id` ASC LIMIT :nbLimit");
$requestSomeArticles->bindParam(':categorie', strval($categorie), PDO::PARAM_STR);
}
$requestSomeArticles->bindParam(':nbLimit', intval($nbLimit), PDO::PARAM_INT);
$requestSomeArticles->bindParam(':orderByAttributsLastValue', floatval($orderByAttributsLastValue), PDO::PARAM_STR);
$requestSomeArticles->execute();
//$myArticles = $requestSomeArticles->fetchAll(PDO::FETCH_CLASS);
$myArticles = array();
while($tmpArticle = $requestSomeArticles->fetch(PDO::FETCH_ASSOC)){
$myArticles[] = arrayUniversalTraductor($tmpArticle);
}
displayInJsonMyArray($myArticles);
}
?>
```
# Fonctionnement en détail : #
**Note :** ne serons mentionné que les noms des fonctions, leur fonctionnement et les types de sorties (avec des exemples). Il n'y aura pas de copier-coller de morceaux de code afin d'éviter de devoir éditer le tutoriel à chaque édition du code source.
## Listes des fonctions : ##
1. articleOrderByList()
1. articleCategorieList()
1. genBanItemForMySQLRequest()
1. displaySomeArticleInJSONUniversal($nbLimit, $categorie, $inputOrderByAttributs, $inputOrderByAttributsLastValue)
1. displayInJsonMyArray($myArray)
### articleOrderByList() ###
* Récupère la liste des attributs possible (nom de cellule) pour la table de données.
* Exemple de sorties :
```php
$liste[1] = "film"
$liste[2] = "séries"
$liste[3] = "musiques"
```
### articleCategorieList() ###
* Retourne une liste sous forme d'un [array](http://php.net/manual/fr/function.array.php) contenant les noms des différentes catégories sans doublon.
* Exemple de sorties :
```php
$liste[1] = "id"
$liste[2] = "prix"
$liste[3] = "nom"
$liste[4] = "timeCreation"
```
### genBanItemForMySQLRequest() ###
* Récupère les identifiants des articles à bannir de la prochaine requête SQL. Utilise les variables $_GET[banItemX] afin de générer un morceau de requête SQL du type ' AND (id != '.intval($_GET[banItemX]).')' à insérer avant l'[ORDER BY](https://www.w3schools.com/sql/sql_orderby.asp).
* Exemple de sorties :
```php
$stringMorceauRequeteSQL = ' AND (id != 1) AND (id != 2) AND (id != 3) AND (id != 5) AND (id != 9) AND (id != 8)'
```
### displaySomeArticleInJSONUniversal($nbLimit, $categorie, $inputOrderByAttributs, $inputOrderByAttributsLastValue) ###
* La fonction _displaySomeArticleInJSONUniversal($nbLimit, $categorie, $inputOrderByAttributs, $inputOrderByAttributsLastValue)_ est précédée d'un bloc de code chargé d'appréhender certaines informations envoyées par l'utilisateur avant d’exécuter la dite fonction.
C'est la fonction principale : chargée de forger et exécuter la requête SQL, insérer les résultats dans un tableau ([array](http://php.net/manual/fr/function.array.php)) puis l'envoyer à _displayInJsonMyArray($myArray)_.
### displayInJsonMyArray($myArray) ###
* Fonction toute banale recevant un tableau ([array](http://php.net/manual/fr/function.array.php)) et affichant les informations contenues via [print_r()](http://php.net/manual/fr/function.print-r.php) au format JSON avec la vérification des entiers.
# Farm Link #
* [[Ajax][Multi] Hello World](https://www.0rion.netlib.re/forum4/viewtopic.php?f=11&t=134)
* [[PHP] [MySQL] [Ajax] afficher les informations d'articles e-shop en JSON depuis MySQL](https://www.0rion.netlib.re/forum4/viewtopic.php?f=79&t=541) (Tuto d'origine)
* [[PHP] Module de traduction V2](https://www.0rion.netlib.re/forum4/viewtopic.php?f=9&t=211)