tm.samf.me Open in urlscan Pro
2600:9000:2250:ac00:17:a50f:5380:93a1  Public Scan

URL: https://tm.samf.me/html/requetes_sql.html
Submission: On October 24 via api from US — Scanned from DE

Form analysis 1 forms found in the DOM

GET search.html

<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
  <input type="text" name="q" placeholder="Search docs">
  <input type="hidden" name="check_keywords" value="yes">
  <input type="hidden" name="area" value="default">
</form>

Text Content

Tutoriel - création d'un forum

 * Introduction
   * Présentation
   * Enjeux
     * NodeJS côté serveur
     * Javascript côté client
     * Socket.io
     * MySQL
     * Bootstrap
     * HTML et CSS
   * Pré-requis
 * Installation
   * NodeJS
   * Les modules NodeJS
   * Bootstrap
   * MySQL
     * Sur Windows
     * Sur Mac OSX
     * Sur Linux
 * jQuery

 * Hello World
   * Les fichiers et dossiers
   * Code
   * Résultat
 * Socket.io
   * Code côté serveur
   * Code côté client
   * Résultat
 * Requêtes SQL et AJAX
   * phpMyAdmin
   * Code
     * Lecture de données depuis MySQL
       * avec socket.io
       * avec EJS
   * Requêtes AJAX

 * Base de données
   * Users
   * Messages
   * Discussions
   * Likes
   * Session
   * Quiz
   * Création de la base de données

 * TP - page d’accueil
   * Pistes
   * Correction
     * Code côté serveur
     * Code côté client

 * Préparation des requêtes SQL
   * Introduction
   * Code
   * Utilisation

 * Sessions
   * Introduction
   * Fonctionnement général
   * Code

 * TP - connexion et déconnexion
   * Pistes
   * Correction
     * Code côté serveur
     * Code côté client
       * Barre de navigation
 * TP - création de comptes
   * Pistes
   * Correction
     * Code côté serveur
     * Code côté client
       * HTML et EJS
       * Feuilles de style
       * Javascript
 * TP - page de profil
   * Pistes
   * Correction
     * Code côté serveur
     * Code côté client
       * HTML et EJS
       * Feuilles de style
       * Javascript

 * TinyMCE
   * Présentation
   * Préparation

 * TP - page de discussions
   * Pistes
   * Correction
     * Routes
     * Chargement initial de la page
       * Code côté serveur
       * Code côté client
     * Gestion des messages
       * Code côté serveur
       * Code côté client
     * Nouvelles discussions
       * Code côté serveur
       * Code côté client

 * Améliorations
   * Failles de sécurité
   * Base de données
   * Gestion des erreurs
   * Fonctionnalités complémentaires
 * Conclusion
   * Mot final

 * Annexes
   * Dépôts bitbucket
 * Code-source
   * Code côté serveur
   * Code côté client
   * HTML et EJS

 
Tutoriel - création d'un forum
 * Docs »
 * Requêtes SQL et AJAX
 * View page source

--------------------------------------------------------------------------------


REQUÊTES SQL ET AJAX¶

Pour apprivoiser le système de requêtes SQL, nous allons insérer une dernière
fonctionnalité sur notre Hello World.

Note

Cette fonctionnalité n’est pas utile en elle-même, mais elle permet de mieux
connaître la façon d’exécuter des requêtes SQL dans une base de données et vous
prépare ainsi pour la suite de ce tutoriel.

Lorsque nous cliquerons sur le bouton de la page d’accueil, l’application
enregistrera la date de clic dans une base de données. La page d’accueil
affichera toutes les dates de clic et s’actualisera en temps réel.


PHPMYADMIN¶

Pour commencer, démarrez votre serveur WAMP, MAMP ou XXAMP (selon votre système
d’exploitation) et allez sur la page d’accueil du serveur Apache. Pour ce faire,
il cliquez sur le bouton prévu à cet effet depuis l’interface logicielle :

Avertissement

Il est possible que votre logiciel ne veuille pas se lancer si le port est déjà
utilisé par votre serveur node. Pour régler ce problème, il suffit d’aller dans
les préférences et de changer le port du serveur Apache :

Sur la page principale, vous obtenez les informations nécessaires pour connecter
la base de données au script nodeJS :

Note

Les informations sont les mêmes sur Windows et sur OSX, il y a juste le mot de
passe qui change. Sur OSX, le mot de passe par défaut est root et sur Windows,
il n’y a pas de mot de passe.

Vous pouvez accéder à phpMyAdmin en entrant l’adresse suivante :
http://localhost:votre_port/phpMyAdmin. L’interface de cette application
apparaît alors.

Commençons par créer une base de données toute simple du nom de hello_world pour
enregister les dates de clics. Insérons-y une table du nom de “clics” avec deux
collones, id et date, sans oublier de cocher la case auto_increment pour le
champ id.


CODE¶

Maintenant que la base de données est prête, commençons à coder !

Il nous faudra tout d’abord un nouveau fichier du nom de sql.js qui ira dans le
dossier serveur.

    .
    ├── app.js
    ├── node_modules
    ├── serveur
    │   ├── routes.js
    │   ├── socket.js
    │   └── sql.js
    ├── static
    └── views


Pour éviter de devoir changer à plusieurs endroits les données d’accès à la base
de données, nous créons une fonction qui les retourne :

./serveur/sql.js

exports.pool = function(mysql) {
  // informations pour se connecter à la base de données
  var pool = mysql.createPool({
    host     : 'localhost',
    user     : 'root',
    // mettre '' comme mot de passe sur Windows
    password : 'root',
    database : 'hello_world',
    charset  : 'UTF8_UNICODE_CI',
    multipleStatements: true
  });

  // à l'appel de cette fonction, ces informations sont retournées
  return pool;
}


Puis, une fonction pour exécuter des requêtes SQL ainsi qu’une autre pour gérer
les erreurs :

./serveur/sql.js

exports.requete = function(mysql, sql, requete_sql, callback) {
  // nous récupérons les données de la fonction pool() écrite plus haut
  // nous utilisons getConnection() pour avoir une connection pool
  sql.pool(mysql).getConnection(function(err, connection) {
    /* lorsque nous avons les informations sur cette connection,
    nous exécutons la requête */
    connection.query(requete_sql, function(err, results) {
      // nous affichons les erreurs, s'il y en a,  à l'aide d'une autre fonction
      sql.query_error(err);
      /* nous testons l'existence du paramètre callback, qui est facultatif si nous
      ne voulons pas obtenir le résultat de la requête (par exemple pour les UPDATE) */
      if(typeof(callback) !== 'undefined') {
        // s'il existe, nous renvoyons les résultats dans une fonction de callback
        callback(results);
      }
      // nous libérons la connection pour éviter de surcharger le pool
      connection.destroy();
    });
  });
}

// cette fonction permet d'afficher une erreur dans la requête SQL
exports.query_error = function(erreur) {
  if (erreur) {
    console.log('query error : ' + erreur.stack);
  }
}


Note

Dans la fonction exports.requete, en plus des arguments requete_sql et callback
qui contiennent respectivement la requête SQL et la fonction appelée pour
retourner les résultats d’une requête, il existe deux autres arguments qui ne
sont pas forcément évidents. mysql et sql sont des dépendances nécessaires pour
que les instructions effectuées dans la fonction exports.requete s’exécutent
correctement. sql contient les fonctions du fichier ./serveur/sql.js (sql.pool
en l’occurence) et mysql le module node-mysql qui permet d’exécuter les requêtes
SQL.

Ensuite, nous entrons dans le fichier socket.js et y insérons une requête SQL
qui s’exécutera lorsque le client cliquera sur le bouton. Toutefois, le fichier
socket.js doit charger le module node-mysql et le fichier sql.js. Nous ajoutons
donc deux paramètres dans sa fonction principale :

./serveur/socket.js

exports.f = function(io, mysql, sql) {
  // ...
}


Note

Les arguments que nous joignons à cette fonction sont obligatoires, car la
fonction sql.requete_sql en a besoin, comme nous l’avons vu précédemment.

Le fichier app.js doit également contenir d’autres require() pour accéder à ces
éléments et les envoyer dans les fichiers socket.js et sql.js :

./app.js

var http = require('http');
var path = require('path');
var express = require('express');
// récupération du module node-mysql
var mysql = require('mysql');

var app = express();
var server = http.Server(app);
var io = require('socket.io')(server);

var routes = require('./serveur/routes.js');
var socket = require('./serveur/socket.js');
// récupération des fonctions du fichier sql.js
var sql = require('./serveur/sql.js');

routes.f(app, __dirname, mysql, sql);
socket.f(io, mysql, sql);

server.listen(8888);


Note

Vous observez que les paramètres sql et mysql ont été ajoutés dans l’appel des
fonctions routes.f() et socket.f(). Le fichier sql.js se comporte différemment
de routes.js et socket.js ; nous n’appelons pas une fonction principale
directement, mais nous donnons aux fonctions principales (socket.f et routes.f)
les éléments (ou paramètres) pour appeler les fonctions qui se trouvent dans
sql.js.

Nous pouvons enfin taper notre requête sql :

./serveur/socket.js

// à placer juste après console.log('Le client a cliqué sur le bouton !')
var requete_sql = 'INSERT INTO clics(date) VALUES(NOW())';
sql.requete(mysql, sql, requete_sql);


Grâce aux fonctions créées précédemment, écrire une requête SQL et l’exécuter ne
prend que peu de lignes de code.


LECTURE DE DONNÉES DEPUIS MYSQL¶

Chaque fois que l’utilisateur clique sur le bouton, la date du clic est
enregistrée dans la base de données. Mais comment récupérer ces dates ?

Nous avons deux possibilités : socket.io et EJS.

AVEC SOCKET.IO¶

Nous ajoutons une requête SQL pour récupérer les données lorsque le client
envoie un message :

./serveur/socket.js

socket.on('recuperation_dates', function() {

  // requête SQL
  var requete_sql = 'SELECT * FROM clics ORDER BY id';
  sql.requete(mysql, sql, requete_sql, function(results) {
    // le client récupère ensuite les données
    socket.emit('dates', results);
  });

});


Ensuite, du côté client, nous formatons les données de la manière suivante :

./views/index.ejs

<p>Hello World !</p>
<!-- la fonction avertir_serveur() s'exécute lorsque nous cliquons sur ce bouton -->
<button class="btn btn-default" onclick="avertir_serveur()">
  <span class="glyphicon glyphicon-link"></span>
  Cliquez ici !
</button><br><br>
<!-- ici s'afficheront les dates -->
<div id="dates"></div>


./static/js/index.js

socket.emit('recuperation_dates');

socket.on('dates', function(results) {
  // boucle pour parcourir l'array "results"
  for(var i = 0; i < results.length; i++) {
    byId('dates').innerHTML += results[i].date + '<br>';
  }
});


Note

Il est important de comprendre comment node-mysql envoie ses données. Elles se
trouvent dans un tableau et chaque élément de ce tableau contient des objets
portant le nom des champs de la table appelée lors de la requête SQL. Un exemple
avec quelques dates :

[
  { id: 15, date: Tue Jan 12 2016 17:01:10 GMT+0100 (CET) },
  { id: 16, date: Tue Jan 12 2016 17:08:46 GMT+0100 (CET) },
  { id: 17, date: Tue Jan 12 2016 17:08:46 GMT+0100 (CET) },
  { id: 18, date: Tue Jan 12 2016 17:08:47 GMT+0100 (CET) },
  { id: 19, date: Tue Jan 12 2016 17:08:47 GMT+0100 (CET) }
]


Cette technique fonctionne, mais elle comprend toutefois quelques inconvénients
:

 * Le client charge la page, puis envoie une requête au serveur qui envoie une
   requête à la base de données, qui renvoie au serveur les informations que le
   serveur va enfin envoyer au client. Si le verbe “envoyer” est beaucoup
   utilisé dans la phrase précédente, c’est parce qu’il y a trop de transferts.
   Ceci aura pour effet de créer un léger délai depuis le moment où la page est
   chargée et le moment où le contenu s’affiche.
 * Une fois le contenu d’une requête SQL obtenu sur le client, ce contenu doit
   être formatté pour qu’il s’affiche sur la page. Ici, nous avons utilisé la
   propriété .innerHTML avec l’ajout d’un <br> pour créer un retour à la ligne à
   chaque nouvelle date. Mais admettons que le formatage en HTML soit beaucoup
   plus conséquent ; avec une classe Bootstrap et des boutons par exemple, nous
   devrions mettre tout ce code HTML dans une chaîne de caractères Javascript !
   Le code HTML se trouverait alors dans un fichier Javascript, ce qui rendrait
   la modification plus compliquée.

Note

Cela ne signifie pas que nous ne pouvons pas utiliser socket.io pour transmettre
des données ; cette technique doit juste servir à charger de petites
informations (comme le nombre de likes d’un post ou le contenu d’un message qui
vient d’être modifié) qui ne nécessitent pas beaucoup de formatage.

AVEC EJS¶

Mais alors, comment faire pour transférer des données importantes au client ? En
utilisant EJS (et AJAX si besoin est).

Pour cela, nous créons une nouvelle fonction dans sql.js. Celle-ci permet de
récupérer les dates et pour les envoyer à l’aide d’une fonction de callback :

./serveur/sql.js

exports.date = function(mysql, sql, callback) {
  var requete_sql = 'SELECT * FROM clics ORDER BY id';
  sql.requete(mysql, sql, requete_sql, function(results) {
    callback(results);
  });
}


Puis, nous modifions la fonction app.get dans routes.js afin que la date soit
retournée au chargement de la page. Pour ce faire, nous ajoutons un objet
contenant les résultats de la requête SQL à la fonction res.render() :

./serveur/routes.js

// affichage de la page index si on entre localhost/
app.get('/', function(req, res) {
  sql.date(mysql, sql, function(results) {
      res.render('index.ejs', {
        results : results
      });
    });
});


Important

Comme nous avons besoin de la variable mysql dans le fichier routes.js, nous
devons modifier les paramètres de la fonction principale de ce fichier :

./serveur/routes.js

exports.f = function(app, dossier, mysql, sql) {
// ...
}


Les données étant dès lors envoyées avec EJS, il ne reste plus qu’à les traiter
dans le fichier index.ejs :

./views/index.ejs

<!-- balise à ajouter juste après le bouton "cliquez ici" -->
<div id="dates">
  <!-- on récupère la variable results qui est envoyée depuis le fichier routes.js -->
  <% for (var i = 0; i < results.length; i++) { %>
    <br><%= results[i].date %>
  <% } %>
</div>


Avertissement

Il faut bien différencier les <% des <%= lorsque vous utilisez EJS. Les <%=
permettent d’écrire le contenu d’une variable EJS sur la page, tandis que les <%
décrivent simplement une instruction EJS (comme une fonction, une boucle...).
Dans les deux cas, la fin d’une instruction EJS se termine par %>.

Si nous testons le code, nous obtenons, après quelques clics sur le bouton et un
rechargement de page, le résultat suivant :

Mais il y a un inconvénient : il est nécessaire de recharger la page pour que
les dates de clic s’actualisent. Et nous aimerions une application “en temps
réel”, donc sans de telles contraintes. Socket.io et AJAX sont donc notre
solution.


REQUÊTES AJAX¶

Tout d’abord, un petit rappel sur cette technologie : AJAX est similaire à
socket.io ; il fonctionne de manière asynchrome et fait aussi des requêtes. Ce
système permet, entre autres, d’enregister le contenu d’une page web dans une
variable. Cela fonctionne comme un chargement “interne”, sans avoir besoin de
raffraîchir complètement la page web du client.

Concrètement, dans cet exemple, nous utiliserons AJAX pour aller chercher une
page créée avec EJS et l’insérer dans la page web actuelle. La page créée avec
EJS contiendra la dernière date entrée dans la base de données. La requête AJAX
s’exécutera lorsque socket.io dira au client “quelqu’un a cliqué sur le bouton”.

Note

Comme le résultat de la requête SQL n’est pas long (c’est juste une date), nous
pourrions nous contenter d’utiliser socket.io pour charger la dernière date.
Mais nous le ferons avec socket.io + EJS + AJAX pour être ensuite capable
reproduire cette méthode pour des requêtes plus conséquentes.

D’abord, nous créons une nouvelle page web (derniere_date.ejs) dans le dossier
views. Dans celle-ci, nous insérons le code suivant :

./views/derniere_date.ejs

<!-- avec cette condition, la page derniere_date retourne quelque chose
seulement si la requête SQL retourne un résultat -->
<% if (typeof(results[0].date) !== 'undefined') { %>
  <!-- nul besoin de parcourir une boucle
  puisqu'il n'y aura qu'un seul résultat à la requête SQL -->
  <br><%= results[0].date %>
<% } %>


Note

Le terme “page web” n’est pas tout-à-fait exact pour désigner derniere_date. Il
s’agit plutôt d’une partie d’une page web, puisque son code ne contient pas de
balise <!DOCTYPE html>.

Ensuite, nous modifions la fonction qui récupère les dates (qui se trouve dans
sql.js) pour qu’elle fasse une requête avec la dernière date lorsqu’on le lui
demande :

.serveur/sql.js

exports.date = function(mysql, sql, callback, derniere_date) {
  // teste si l'attribut derniere_date est true ou false
  if (!derniere_date) {
    var requete_sql = 'SELECT * FROM clics ORDER BY id';
  } else {
    // nous demandons de choisir le premier élément dans
    // l'ordre décroissant, c'est-à-dire le dernier élément
    var requete_sql = 'SELECT * FROM clics ORDER BY id DESC LIMIT 0,1'
  }
  sql.requete(mysql, sql, requete_sql, function(results) {
    callback(results);
  });
}


Puis, nous ajoutons une route pour charger le fichier derniere_date.ejs en lui
envoyant le retour de la requête SQL :

./serveur/routes.js

app.get('/derniere_date', function(req, res) {
  sql.date(mysql, sql, function(results) {
    res.render('derniere_date.ejs', {
      results : results
    });
  }, true);
});


Avertissement

Il faut insérer cet app.get() avant celui qui s’occupe de charger la page
d’erreur. Sinon, derniere_date.ejs ne se chargera jamais.

A présent, en allant sur http://localhost:8888/derniere_date, vous voyez
apparaître la dernière date de clic. Mais pour l’afficher dans la page, nous
devons encore écrire un peu de code. Il faut créer une fonction dans index.js
qui s’occupera de récupérer le contenu de la page derniere_date.ejs pour
l’afficher dans la page index.ejs lorsque socket.io le lui demandera.

./static/js/index.js

socket.on('charger_derniere_date', function() {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', 'derniere_date');
  xhr.addEventListener('readystatechange', function() {
    if (xhr.readyState === 4 && xhr.status === 200) {
      // on ajoute le contenu de derniere_date.ejs aux dates existantes
      byId('dates').innerHTML += xhr.responseText;
      // rappel : byId() = document.getElementById()
    }
  });
  xhr.send(null);
});


Note

Si vous ne comprenez pas complètement ce code, c’est tout à fait normal. Il
utilise XMLHttpRequest, qui est l’implémentation d’AJAX en dans le moteur
javascript du navigateur web. Nous n’utiliserons pas des fonctionnalités plus
poussées d’AJAX dans cette application, mais si vous souhaitez mieux connaître
le fonctionnement de XMLHttpRequest, vous pouvez consulter cette page qui
explique, entre autres, ce bout de code.

Il ne reste plus qu’à écrire un socket.emit() dans socket.js :

./serveur/socket.js

socket.on('bouton_client', function() {
  console.log('Le client a cliqué sur le bouton !')
  var requete_sql = 'INSERT INTO clics(date) VALUES(NOW())';
  sql.requete(mysql, sql, requete_sql);

  // avec la fonction socket.emit le client actuel charge la nouvelle date
  socket.emit('charger_derniere_date');
  /* avec la fonction socket.broadcast.emit,
  la nouvelle date est également envoyée aux autres clients */
  socket.broadcast.emit('charger_derniere_date');
});


Et voilà ! Désormais, lorsque vous cliquez sur le bouton, la nouvelle date de
clic apparaît directement à la suite des autres.

Astuce

Vous pouvez télécharger le programme actuel en cliquant sur les liens ci-dessous
:

 * Programme utilisant socket.io
 * Programme utilisant AJAX

Next Previous

--------------------------------------------------------------------------------

© Copyright 2015-2016, <Samuel Fringeli>.

Built with Sphinx using a theme provided by Read the Docs.