Cellule de tableau éditable PHP / MySQL et Ajax

Introduction

Ce tutorial, fait suite au tutorial Ajax Inline publié par Mr Olance de Developpez.com.
D'ailleurs Mr Olance a publié 2 versions de ce magnifique script, pour ceux qui auront réussi cette application en lisant son script, ce qui est mon cas, cela n'enléve rien à sa rédaction tout à fait compréhensible.

Mais quelques fois, il arrive qu'une autre personne par sa propre expérience explique différemments afin que la mise en place d'un script soit mieux comprise et permet de comprendre le fonctionnement du script. C'est un script que j'emploi souvent pour la partie administration de mes applications.

Donc à travers ce tutorial, je vais vous montrer comment rendre des cellules de tableau éditable.
Lorsque vous cliquerez sur un cellule, cette dernière sera en mode édition, qui permettra de changer la valeur pour la modifier, puis de mettre à jour la table de la base de données avec les nouvelles valeurs.

Je vais décrire pas à pas la réalisation de cette application.
Cette application peut être appliquée avec une requête d'affichage en boucle des valeurs provenant d'une base de données, mais elle peut être aussi utilisée avec Dreamweaver pour ceux qui l'utilise.
En tout état de cause la modification du tableau sera identique dans les deux cas, c'est à dire qu'il faudra mettre les mains dans le code dans le cas comme dans l'autre.

Pour la réalisation de ce tableau éditable je vais faire comme si vous utilisiez Dreamweaver, vous devez savoir ce que cela implique ?

Etape 1

En premier lieu, vous devez avoir une table dans votre base de données (je parle ici en local) pour faire les tests.
Dans le dossier de votre applications qui se trouve dans le dossier de votre serveur web, créer un sous dossier que vous nommerez (js).
Nous allons y mettre le fichier *.js dedans.

Etape 2

Dans cette 2éme étapes nous allons créer le fichier (inlinemod.js)

        // retourne un objet xmlHttpRequest.
        // méthode compatible entre tous les navigateurs (IE/Firefox/Opera)
        function getXMLHTTP()
        {
            var xhr = null;
            if(window.XMLHttpRequest)
            { // Firefox et autres
                xhr = new XMLHttpRequest();
            }
            else if(window.ActiveXObject)
            { // Internet Explorer
                try
                {
                    xhr = new ActiveXObject("Msxml2.XMLHTTP");
                }
                catch(e)
                {
                    try
                    {
                        xhr = new ActiveXObject("Microsoft.XMLHTTP");
                    }
                    catch(e1)
                    {
                        xhr = null;
                    }
                }
            }
            else
            { // XMLHttpRequest non supporté par le navigateur
                alert("Votre navigateur ne supporte pas les objets XMLHTTPRequest...");
            }
        
            return xhr;
        }
        
        
        //Fonction renvoyant le code de la touche appuyée lors d'un événement clavier
        function getKeyCode(evenement)
        {
            for (prop in evenement)
            {
                if(prop == 'which')
                {
                    return evenement.which;
                }
            }
        
            return event.keyCode;
        }
        
        
        //Suppression des espaces/sauts de ligne inutiles
        function trim(value) {
           var temp = value;
           var obj = /^(\s*)([\W\w]*)(\b\s*$)/;
           if (obj.test(temp)) { temp = temp.replace(obj, '$2'); }
           var obj = /  /g;
           while (temp.match(obj)) { temp = temp.replace(obj, " "); }
           return temp;
        }
        
        //Fonction donnant la largeur en pixels du texte donné (merci SpaceFrog !)
        function getTextWidth(texte)
        {
            //Valeur par défaut : 150 pixels
            var largeur = 150;
        
            if(trim(texte) == "")
            {
                return largeur;
            }
        
            //Création d'un span caché que l'on "mesurera"
            var span = document.createElement("span");
            span.style.visibility = "hidden";
            span.style.position = "absolute";
        
            //Ajout du texte dans le span puis du span dans le corps de la page
            span.appendChild(document.createTextNode(texte));
            document.getElementsByTagName("body")[0].appendChild(span);
        
            //Largeur du texte
            largeur = span.offsetWidth;
        
            //Suppression du span
            document.getElementsByTagName("body")[0].removeChild(span);
            span = null;
        
            return largeur;
        }
        
        
        //Fonction renvoyant une valeur "aléatoire" pour forcer le navigateur (ie...)
        //à envoyer la requête de mise à jour
        function ieTrick(sep)
        {
            d = new Date();
            trick = d.getYear() + "ie" + d.getMonth() + "t" + d.getDate() + "r" + d.getHours() + "i" + d.getMinutes() + "c" + d.getSeconds() + "k" + d.getMilliseconds();
        
            if (sep != "?")
            {
                sep = "&";
            }
        
            return sep + "ietrick=" + trick;
        }
        
        
        //On ne pourra éditer qu'une valeur à la fois
        var editionEnCours = false;
        
        //variable évitant une seconde sauvegarde lors de la suppression de l'input
        var sauve = false;
        
        //Fonction de modification inline de l'élément double-cliqué
        function inlineMod(id, obj, nomValeur, type)
        {
            if(editionEnCours)
            {
                return false;
            }
            else
            {
                editionEnCours = true;
                sauve = false;
            }
        
            //Objet servant à l'édition de la valeur dans la page
            var input = null;
        
            //On crée un composant différent selon le type de la valeur à modifier
            switch(type)
            {
                //Valeur de type texte ou nombre
                case "texte":
                case "nombre":
                    input = document.createElement("input");
                    break;
        
                //Valeur de type texte multilignes
                case  "texte-multi":
                    input = document.createElement("textarea");
                    break;
            }
        
            //Assignation de la valeur
            if (obj.innerText)
                input.value = obj.innerText;
            else
                input.value = obj.textContent;
                
            input.value = trim(input.value);
        
            //On lui donne une taille un peu plus large que le texte à modifier
            input.style.width  = getTextWidth(input.value) + 30 + "px";
        
            //Remplacement du texte par notre objet input
            obj.replaceChild(input, obj.firstChild);
        
            //On donne le focus à l'input et on sélectionne le texte qu'il contient
            input.focus();
            input.select();
        
            //Assignation des deux événements qui déclencheront la sauvegarde de la valeur
        
            //Sortie de l'input
            input.onblur = function sortir()
            {
                sauverMod(id, obj, nomValeur, input.value, type);
                delete input;
            };
        
            //Appui sur la touche Entrée
            input.onkeydown = function keyDown(event)
            {
                if (!event&&window.event)
                {
                    event = window.event;
                }
                if(getKeyCode(event) == 13)
                {
                    sauverMod(id, obj, nomValeur, input.value, type);
                    delete input;
                }
            };
        }
        
        //Objet XMLHTTPRequest
        var XHR = null;
        
        //Fonction de sauvegarde des modifications apportées
        function sauverMod(id, obj, nomValeur, valeur, type)
        {
            //Si on a déjà sauvé la valeur en cours, on sort
            if(sauve)
            {
                return false;
            }
            else
            {
                sauve = true;    
            }
        
            //Si l'objet existe déjà on abandonne la requête et on le supprime
            if(XHR && XHR.readyState != 0)
            {
                XHR.abort();
                delete XHR;
            }
        
            //Création de l'objet XMLHTTPRequest
            XHR = getXMLHTTP();
        
            if(!XHR)
            {
                return false;
            }
        
            //URL du script de sauvegarde auquel on passe la valeur à modifier
            XHR.open("GET", "sauverModMot.php?id=" + id + "&champ=" + nomValeur + "&valeur=" + escape(valeur) + "&type=" + type + ieTrick(), true);
        
            //On se sert de l'événement OnReadyStateChange pour supprimer l'input et le replacer par son contenu
            XHR.onreadystatechange = function()
            {
                //Si le chargement est terminé
                if (XHR.readyState == 4)
                {
                    //Réinitialisation de la variable d'état d'édition
                    editionEnCours = false;
        
                    //Remplacement de l'input par le texte qu'il contient
                    obj.replaceChild(document.createTextNode(valeur), obj.firstChild);
                }
            }
        
            //Envoi de la requête
            XHR.send(null);
        }
        

Le fichier (sauverMod.php)

Ce fichier aussi doit être exempt de tous code. Puis ajouter le code ci-dessous. Ce code permet la mise à jour des nouvelles valeurs dans la table de la base de données.

          <?php 
          require_once('Connections/cnxPDO.php');
          $table = 'moteur'; 
          
          //On sort en cas de paramètre manquant ou invalide
          if(empty($_GET['id']) or empty($_GET['type']) or empty($_GET['champ']) or empty($_GET['valeur'])
             or !is_numeric($_GET['id'])
             or !in_array(
                  $_GET['champ'],
                  array('url', 'titre', 'description')
                  ))
          {
              exit;
          }
              //Construction de la requête en fonction du type de valeur
          switch($_GET["type"])
          {
              case "texte":
              case "texte-multi":
              $update = 'UPDATE '.$table.' SET '.$_GET["champ"].' =" '.$_GET["valeur"].'" WHERE id= '.$_GET["id"].' ';
              break;
          
              case 'nombre':
              $update = 'UPDATE '.$table.' SET '.$_GET["champ"].' =" '.$_GET["valeur"].'" WHERE id= '.$_GET["id"].' ';  
              break;
          
              default:
                  exit;
          }
              echo '<br>';
              echo 'id ' .$_GET["id"];
              echo '<br>';
              echo 'type ' .$_GET["type"];
              echo '<br>';
              echo 'champ ' .$_GET["champ"];
              echo '<br>';
              echo 'valeur ' .$_GET["valeur"];
              echo '<br>';
          
              //Exécution de la requête
              try {
              $a = $PDO->exec($update);
              }
              catch(Exception $e)
              {
              echo 'Erreur : '.$e->getMessage().'';
              echo 'N° : '.$e->getCode();
              }
          ?>
        

Etape 3

Maintenant que nous avons créés nos deux fichiers et que vous les avez paramétré en fonction de votre table de base de données, nous allons maintenant créer un tableau dans une page, en affichant avec une boucle toutes les données de notre table.
Je vous rappelle que nous travaillons avec Dreamweaver, mais vous pouvez faire sans.
Supposons donc que vous utilisiez Dreamweaver, créer une page que vous nommerez (index.php) dans le dossier de votre dossier de votre serveur web.
A l'intérieur vous avez le dossier (js) contenant le fichier (inlinemod.js).
Dans le dossier au même niveau que la page (index.php) vous devez avoir le fichier (sauverMod.php) que nous venons de créer.

Avec Dreamweaver, créer un Jeu d'enregistrements issus de votre table.
Insérer ensuite un tableau de 2 lignes et autant de colonne que comporte le nombre de champ de votre table.
Dans la première ligne saisissez les intitulés correspondant.
Dans la deuxième ligne insérer les variables dynamique avec le Panneau -> Liaison.
Sélectionner ensuite la ligne contenant ces variables dynamiques, puis appliquer une Région Répétée.

Etape 4

Dans votre page (index.php) insérer l'appel du fichier js issu du dossier js dans la partie <head> et </head>
<script type="text/javascript" src="js/inlinemod.js"></script>

Etape 5

Dans Dreamweaver, passer en mode Code, afin de voir le code du tableau.
Voici le code du tableau et comment le modifier.
Regarder bien la 2éme partie où se trouve les variables dynamiques.
En réalité on modifie la balise (td) qui elle même devient lorsque l'on clic dessus un champ de texte ou une zone de texte.

Construisez votre tableau de cette façon, par exemple pour la 2éme cellule, la balise <td> porte le nom d'un des champs, relatif à la variable dynamique du champ (id) soit de la clè primaire de votre table, on y ajoute l'évenement ondblclick qui est = à la fonction inlineMod() contenant la variable dynamique soit l'id de votre table qui sera pour le champ url en mode texte avec la variable dynamique qui nous sert d'affichage des données du champ.

        <td align="center"><?php echo $row_rsmoteur['id']; ?></td>
        
        <td id="url-<?php echo $row_rsmoteur['id']; ?>" ondblclick="inlineMod(<?php echo $row_rsmoteur['id']; ?>, this, 'url', 'texte')"><?php echo $row_rsmoteur['url']; ?></td>
        
        <td id="titre-<?php echo $row_rsmoteur['id']; ?>" width="145" ondblclick="inlineMod(<?php echo $row_rsmoteur['id']; ?>, this, 'titre', 'texte')"><?php echo $row_rsmoteur['titre']; ?></td>
        
        <td id="description-<?php echo $row_rsmoteur['id']; ?>" width="188" ondblclick="inlineMod(<?php echo $row_rsmoteur['id']; ?>, this, 'description', 'texte-multi')"><?php echo $row_rsmoteur['description']; ?></td>        
        

Conclusion

Avec ce genre de script (voir la démo de Mr Olance), comme vous avez pu le constater, permet de modifier une valeur dans une cellule de tableau sans passer par un formulaire de modification.
Comme vous avez pu le remarquer sans doute, j'ai laissé une colonne Action, cette dernière me permet d'ajouter une icône d'édtion avec un lien avec passage de paramètre via l'id vers un formulaire pour modifier une ou plusieurs valeurs, ce qui permet d'avoir une double modification dans le cas l'un où l'autre ne fonctionnerais pas.

Bon dev