Magazine High tech

Développer un système de notation complet avec jQuery, Php et MySQL

Publié le 31 mars 2012 par Dominikg

Lorsqu'on souhaite développer une fonctionnalité interactive pour un site Web, on peut chercher un plugin jQuery qui répond au problème à résoudre.

Malheureusement, il faut souvent adapter le site et le plugin au besoin réel et c'est souvent là que les choses se gâtent : il faut modifier l'architecture, créer une table, écrire du SQL, écrire des nouvelles pages php, de nouvelles fonctions, ...

J'ai souvent constaté ces difficultés lors de formations et la lecture des forums confirme la difficulté.
Tant qu'on n'a pas un exemple effectif complet qui fonctionne, on ne sait pas pourquoi notre développement échoue : est ce une incompréhension majeure ou est ce une faute de frappe ?

Ce billet présente les différentes parties à développer pour réaliser un site de vote pour des photos : conception de la base de données, écriture du php nécessaire pour accéder à la base, écriture du php pour répondre à une requête Ajax, écriture de la page d'affichage, écriture du javascript avec jQuery et les plugin rating et form.

C'est lui aussi un exemple que j'utilise lors de formation que j'anime (PHP, Javascript ou jQuery) et il devrait être suffisamment détaillé pour que vous pussiez le comprendre, le reproduire et l'adapter à votre propre besoin.

Le cahier des charges est simple : la page Web doit afficher une photo, avec une note mémorisée et permettre au visiteur de voter une seule fois.
Il n'y a pas de contrôle de votant : s'il effectue une nouvelle visite (ou recharge la page), il peut à nouveau voter.

Vous pouvez voir un exemple de fonctionnement ici. 

La mémorisation des votes s'effectue dans une table SQL contenant trois champs : le nom de la photo (varchar(30), clé primaire), la note (float) et le nombre de votant (int). L'instruction SQL de création de la table est :

CREATE TABLE `basededonnee`.`VotePhoto` (`nom` VARCHAR(30) NOT NULL, `note` FLOAT NOT NULL, `nbvote` INT NOT NULL, PRIMARY KEY (`nom`)) ENGINE = MyISAM;

L'accès à la base se fait avec une classe PHP dont voici la définition :

<?php
require_once("bddClasse.php");
class Vote {
   public $nom;
   public $nbvote;
   public $note;
   public function __construct($nom,$nbvote,$note) {
      $this->nom = $nom;
      $this->nbvote = $nbvote;
      $this->note = $note;
   }
   static public function getVote($nom) {
      $base = new bdd();
      $nom = mysql_real_escape_string($nom);
      $sql = "SELECT nom,nbvote,note FROM VotePhoto"
      . " WHERE nom = '$nom'";
      $res = $base->requete($sql);
      if (($val = mysql_fetch_object($res)) !== false)   {
         return new Vote($val->nom, $val->nbvote,$val->note);
      }
      else {
         $sql = "INSERT INTO VotePhoto (nom,nbvote,note) values "
            . " ('$nom',0,0)";
         $res = $base->requete($sql);
         return new Vote($nom,0,0);
      }
   }
   static public function setVote($vote,$note) {
      $vote->note = ($vote->note * $vote->nbvote++ + $note) / $vote->nbvote;
      $base = new bdd();
      $nom = mysql_real_escape_string($nom);
      $note = mysql_real_escape_string($note);
      $sql = "UPDATE VotePhoto set note='" . $vote->note . "',nbvote=nbvote+1"
      . " WHERE nom = '$vote->nom'";
      $res = $base->requete($sql);  
      return $vote;
   }
}
?>

La classe Vote dispose d'un constructeur __construct qui ne sert qu'à initialiser les champs.

Elle fournit deux méthodes statiques :

  • l'une pour construire une instance de Vote à partir du nom de la photo et du contenu de la base de données : getVote
    Cette méthode crée automatiquement un enregistrement vide si la photo n'a pas encore eu de vote.
  • l'autre pour mettre à jour la note dans la base de données : setVote
    Elle renvoie l'instance de Vote passée en paramètre après l'avoir mise à jour.

Notez l'utilisation de mysql_real_escape_string pour éviter des attaques de la base de données.

Cette classe Vote emploie une classe utilitaire pour accéder à MySQL :

<?php
class bdd {
   private $connexion;
   const NOM='domi';
   const PASSE='imod38vps17';
   const SERVEUR ='localhost';
   const BASE = 'domi';
   function __construct() {
   $this->connexion = mysql_pconnect(self::SERVEUR, self::NOM, self::PASSE);
   if (!$this->connexion)
   {
   throw new Exception("Connexion au serveur impossible");
   }
   if (!mysql_select_db(self::BASE, $this->connexion)) {
   throw new Exception("Accès à la base impossible");
   }
   }
function requete($req,$debug=false) {
   $res = mysql_query($req,$this->connexion);
   if ($res === false ) {
   print $req . "<br>";
   print mysql_error();
   }
   if ($debug) {
      error_log($req);
      error_log(mysql_error());
   }
   return $res;
}
function __destruct() {
   mysql_close($this->connexion);
   }
}
?>

Cette classe ne vise pas à fournir une abstraction complète de l'accès à la base de données, mais uniquement à nous faciliter la vie pour travailler avec MySQL.

La page web complète est

<?php
$home = $_SERVER['DOCUMENT_ROOT'];
$photo = "GeminidAurora.jpg";
require($home . "/notation/voteClasse.php");
$vote = Vote::getVote($photo);
function check($val,$vote) {
   if (round($vote->note,0) == $val) {
      print 'checked="checked"';
   }
}
?>
<!DOCTYPE HTML>
<html LANG="en">
<head>
<meta CHARSET="iso-8859-1">
<title>Exemple de vote sur une photo</title>
<script TYPE="text/javascript" SRC="/js/jquery-1.6.min.js"></script>
<script TYPE="text/javascript" SRC="/js/rating/jquery.MetaData.js"></script>
<script TYPE="text/javascript" SRC="/js/rating/jquery.rating.js"></script>
<script TYPE="text/javascript" SRC="/js/jquery.form.js"></script>
<link HREF="/js/rating/jquery.rating.css" REL="stylesheet" TYPE="text/css">
<script TYPE="text/javascript" >
$('document').ready(function () {
   envoi = true;
   $('input.vote').rating({
   callback: function(value,link) {
         if (envoi) {
            envoi = false;
            $(this.form).ajaxSubmit({
               dataType:'json',
               success: function(rep) {
                  var note = rep.note;
                  var nombre = rep.nombre;
                  $('input.vote').rating('select', note);
                  $("#nombre").html(nombre);
                  $('input.vote').rating('disable');
               }
            });
         }
      }
   });
});
</script>
</head>
<body>
<form ID="note" ACTION="/notation/vote.php" METHOD="post">
<img SRC="/notation/<?php print $photo; ?>"><br>
<input NAME="photo" TYPE="hidden" VALUE="<?php print $photo; ?>">
<input NAME="star" TYPE="radio" CLASS="vote" VALUE="1" <?php check(1,$vote); ?>>
<input NAME="star" TYPE="radio" CLASS="vote" VALUE="2" <?php check(2,$vote); ?>>
<input NAME="star" TYPE="radio" CLASS="vote" VALUE="3" <?php check(3,$vote); ?>>
<input NAME="star" TYPE="radio" CLASS="vote" VALUE="4" <?php check(4,$vote); ?>>
<input NAME="star" TYPE="radio" CLASS="vote" VALUE="5" <?php check(5,$vote); ?>>
<br>(<span ID="nombre"><?php print $vote->nbvote ?> vote<?php print ($vote->nbvote<2)?"":"s"; ?></span>)
</form>
</body>
</html>

Cette page comporte plusieurs parties :

  • une initialisation php qui permet d'obtenir la note mémorisée dans la base de données. On y trouve l'utilisation de la méthode statique getVote.
  • le script javascript qui permet d'activer le plugin rating et de définir une fonction appelée lorsque le visiteur vote pour une note. Nous allons y revenir plus en détail.
  • le code html qui affiche la photo et définit le formulaire qui est transformé par le plugin rating en affichage d'étoiles correspondant à la note.

Il y a quelques éléments php pour peaufiner l'affichage. La fonction check permet de définir l'étoile correspondant à la note. Il y a également un peu de code PHP pour gérer correctement l'accord du mot vote. 

Revenons sur la fonction anonyme appelée lorsque le visiteur vote :

envoi = true;
function(value,link) {
   if (envoi) {
          envoi = false;  // utilisation d'une variable de portée étendue pour ne permettre qu'un seul vote
         $(this.form).ajaxSubmit({ // envoi du formulaire à l'aide d'une requête ajax
         dataType:'json',
         success: function(rep) { // définition de la fonction anonyme appelée lorsque la réponse Ajax est reçue

            var note = rep.note;
            var nombre = rep.nombre;
            $('input.vote').rating('select', note);
            $("#nombre").html(nombre);
            $('input.vote').rating('disable');
         }
      }); // fin de définition de la fonction anonyme appelée lorsque la réponse Ajax est reçue
   }
}

Elle utilise le plugin form pour effectuer l'envoi du formulaire à l'aide d'une requête Ajax. Cette requête AJax attend une réponse au format json, de la forme : {'note':'3', 'nombre' : '5 votes'}, qui est utilisée pour mettre à jour les informations affichées sur la page.
Comme un visiteur ne doit pas voter plusieurs fois, on utilise une variable externe pour n'envoyer qu'une seule fois la requête. Attention, si vous voulez gérer plusieurs votes sur la même page, il faut penser à utiliser une variable différente pour chaque photo !

Et le dernier élément de notre architecture est la page PHP utilisée par la requête Ajax :

<?php
$home = $_SERVER['DOCUMENT_ROOT'];
require($home . "/notation/voteClasse.php");
$photo = $_POST['photo'];
$note = $_POST['star'];
$vote = Vote::getVote($photo);
$vote = Vote::setVote($vote,$note);
$texte = "$vote->nbvote vote" . (($vote->nbvote<2)?"":"s");
print '{"note" : "' . round($vote->note,0) . '", "nombre" : "' . $texte . '" }';
?>

On utilise à nouveau la classe Vote, en appelant successivement les deux méthodes statiques pour obtenir l'instance de Vote correspondant à la photo, puis pour mettre à jour la note. La dernière instruction crée la réponse json attendue pour faire la mise à jour de la page HTML.

Chaque partie de cet exemple est relativement simple et la complexité vient plutôt de l'imbrication des différentes parties.

Si certains points demandent plus d'explications, n'hésitez pas à l'indiquer dans les commentaires.


Retour à La Une de Logo Paperblog

A propos de l’auteur


Dominikg 19 partages Voir son profil
Voir son blog

l'auteur n'a pas encore renseigné son compte l'auteur n'a pas encore renseigné son compte

Dossier Paperblog