Wiki Tuto Ajax - afficher au format JSON des articles e-shop depuis MySQL-MariaDB grâce à PHP

0
23
fév.
2017

Sommaire

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 voir Farm Link à la fin du wiki.

Introduction

  • Nous allons ici étudier un script PHP, rédigé par votre humble serviteur en 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 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 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] gère la LIMITE SQL (nombre d'entrées max à renvoyer)
  • &orderByAttributs=[string] l'attribut pour ORDER BY (par exemple l'id, le prix, etc)
  • &orderByAttributsLastValue=[float] la valeur la plus haute (ou basse selon la recherche) de l'attribut ORDER BY afin de commencer la recherche à partir de là
  • &categorie=[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] 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 :

  • www.hostname.be/ajaxArticles.php?articleMax=10&orderByAttributsLastValue=42&orderByAttributs=prix&orderByType=1&categorie=film

    • On demande à voir les 10 prochains articles, classé par la cellule prix du plus petit au plus grand à partir du prix 42, de la catégorie film

Le code

Pour l'installer créez un ajaxArticles.php et collez le code suivant dedans.

<?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()
  2. articleCategorieList()
  3. genBanItemForMySQLRequest()
  4. displaySomeArticleInJSONUniversal($nbLimit, $categorie, $inputOrderByAttributs, $inputOrderByAttributsLastValue)
  5. displayInJsonMyArray($myArray)

articleOrderByList()

  • Récupère la liste des attributs possible (nom de cellule) pour la table de données.
    • Exemple de sorties :
$liste[1] = "film"
$liste[2] = "séries"
$liste[3] = "musiques"

articleCategorieList()

  • Retourne une liste sous forme d'un array contenant les noms des différentes catégories sans doublon.
    • Exemple de sorties :
$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.
    • Exemple de sorties :
    $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) puis l'envoyer à displayInJsonMyArray($myArray).

displayInJsonMyArray($myArray)

  • Fonction toute banale recevant un tableau (array) et affichant les informations contenues via print_r() au format JSON avec la vérification des entiers.

-


-

-


-

Note pour la rédaction :

  • il faut encore simplifier le tuto, ajouter des exemples (et mettre en place un exemple en ligne afin que les lecteurs puissent tester (comme dans cet exemple lié à ce tuto))
  • le code aurait besoin d'une fraîcheur afin de le rendre plus compréhensible et généralisable au max (le but étant qu'un lecteur néophyte puisse copier-coller, éventuellement avec une légère modification puis en route)
  • des exemples d'utilisations suivront (un avec javascript et un avec angular 4)

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.