Créer un système de notation 5 étoiles avec jQuery, AJAX et PHP

Dans ce tuto, vous aller apprendre à créer un système de notation avec AJAX, PHP et jQuery. Enregistrer les notes et la mise à jour en temps réel avec la magie d’AJAX, et nous utilisons la puissance de PHP pour que vous n’ayez même pas besoin d’une base de données !

Étape 1. Construire le HTML

Nous allons créer une page simple qui répertorie deux restaurant et vous permet de les noter. Cela signifie que nous avons besoin des étoiles pour afficher la note actuelle et pour permettre le vote. Nous voulons également une zone pour afficher le total des votes et la note actuelle à une décimale près.

<div class='restaurant_choix'>
            Noter: Le Parisien
            <div id="r1" class="rate_widget">
                <div class="star_1 ratings_stars"></div>
                <div class="star_2 ratings_stars"></div>
                <div class="star_3 ratings_stars"></div>
                <div class="star_4 ratings_stars"></div>
                <div class="star_5 ratings_stars"></div>
                <div class="total_votes">votes</div>
            </div>
        </div>
        
        <div class='restaurant_choix'>
            Noter: Le Lyonnais
            <div id="r2" class="rate_widget">
                <div class="star_1 ratings_stars"></div>
                <div class="star_2 ratings_stars"></div>
                <div class="star_3 ratings_stars"></div>
                <div class="star_4 ratings_stars"></div>
                <div class="star_5 ratings_stars"></div>
                <div class="total_votes">votes</div>
            </div>
        </div>

Utilisons du CSS pour embélir cette page HTML

        .rate_widget {
            border:     1px solid #CCC;
            overflow:   visible;
            padding:    10px;
            position:   relative;
            width:      180px;
            height:     32px;
        }
        .ratings_stars {
            background: url('etoile_vide.png') no-repeat;
            float:      left;
            height:     28px;
            padding:    2px;
            width:      32px;
        }
        .ratings_vote {
            background: url('etoile_remplie.png') no-repeat;
        }
        .ratings_over {
            background: url('etoile_actuelle.png') no-repeat;
        }

Cette première partie du CSS accomplit plusieurs choses :

Donne le début « vide » par défaut à chaque emplacement d’étoile
Configure des classes pour les étoiles remplies et les étoiles en surbrillance

  • Définit et stylise le conteneur des étoiles.
  • Vous pouvez soit utiliser les graphiques fournis dans le téléchargement, soit créer les vôtres.
  • Il doit y avoir un graphique pour chacun des trois états : vide, plein et en surbrillance.

Ensuite, nous ajoutons un peu plus de CSS pour positionner la zone de total des votes et centrons les widgets afin que la page corresponde au graphique au début de cette section.

.total_votes {
            background: #eaeaea;
            top: 58px;
            left: 0;
            padding: 5px;
            position:   absolute;  
        } 
        .movie_choice {
            font: 10px verdana, sans-serif;
            margin: 0 auto 40px auto;
            width: 180px;
        }

Étape 2. Ajout de l’interactivité de l’interface utilisateur

$('.ratings_stars').hover(
            // Handles the mouseover
            function() {
                $(this).prevAll().andSelf().addClass('ratings_over');
                $(this).nextAll().removeClass('ratings_vote'); 
            },
            // Handles the mouseout
            function() {
                $(this).prevAll().andSelf().removeClass('ratings_over');
                set_votes($(this).parent());
            }
        );

Nous profitons des puissantes méthodes .prevAll() et .nextAll() de jQuery pour obtenir les étoiles précédant et suivant l’étoile actuellement survolée par la souris.

Le code ci-dessus ajoute et supprime ensuite les classes pour rendre les étoiles sous la souris et avant « surlignées », et les étoiles après « non mises en évidence ».

Qu’en est-il de set_votes() ?

Il s’agit d’une fonction qui vérifie quelles étoiles doivent être à l’état « complet » et est étroitement liée à l’étape suivante, où nous récupérons les données distantes du serveur.

Étape 3. Récupération des données du serveur

Nos étoiles sont mises en subrillance lorsque vous passez la souris dessus, et c’est un bon début. Mais qu’en est-il des étoiles rouges indiquant le vote en cours ? Pour atteindre cette étape, nous devons à la fois obtenir les informations du serveur et écrire du JavaScript pour gérer ces données.

$('.rate_widget').each(function(i) {
            var widget = this;
            var out_data = {
                widget_id : $(widget).attr('id'),
                fetch: 1
            };
            $.post(
                'ratings.php',
                out_data,
                function(INFO) {
                    $(widget).data( 'fsr', INFO );
                    set_votes(widget);
                },
                'json'
            );
        });

Ce bloc de code – en fait tout le JavaScript – va dans un bloc document.ready. Ce code particulier s’exécute immédiatement. Il interroge le serveur et obtient des informations sur chaque widget de vote sur la page.

Tout d’abord, nous configurons un objet, out_data, pour contenir les informations que nous envoyons au serveur. Notre script PHP s’attend à voir « récupérer » lorsqu’il récupère simplement des données, nous l’incluons donc ici. Nous incluons également l’ID du widget, qui permet au script côté serveur de savoir quelles données nous recherchons. Lorsque la fonction de rappel se déclenche, elle contient un objet JavaScript qui ressemble à ceci :

{
            "widget_id"     : "r1",
            "number_votes"  : 129,
            "total_points"  : 344,
            "dec_avg"       : 2.7,
            "whole_avg"     : 3
        }

La méthode .data() est un peu de magie jQuery qui permet d’associer des données arbitraires à un DOM
objet.

Si vous regardez attentivement le code, vous verrez que nous prenons cet objet (stocké dans la variable INFO) et
faire quelque chose avec via la méthode .data().

La méthode .data() est un peu de magie jQuery qui permet d’associer des données arbitraires à un DOM
objet. Dans ce cas, nous stockons les données dans le widget div. On peut y accéder plus tard comme ceci :

$('#one_of_your_widgets).data('fsr').widget_id;

set_votes()

Une fois les données renvoyées par le serveur, elles sont transmises indirectement à set_votes().

function set_votes(widget) {
        
            var avg = $(widget).data('fsr').whole_avg;
            var votes = $(widget).data('fsr').number_votes;
            var exact = $(widget).data('fsr').dec_avg;
            
            $(widget).find('.star_' + avg).prevAll().andSelf().addClass('ratings_vote');
            $(widget).find('.star_' + avg).nextAll().removeClass('ratings_vote'); 
            $(widget).find('.total_votes').text( votes + ' votes recorded (' + exact + ' rating)' );
        }

Les trois premières lignes sont pour la lisibilité, car ces noms de variables sont assez longs. Voyons donc ce qui se passe ici.

Ligne 7 : ‘avg’ est un nombre entier, représentant la moyenne des votes arrondis de ce widget. Parce que c’est
un nombre 1-5, nous pouvons l’utiliser pour trouver la bonne étoile dans le widget, et la tourner, et le
précédents à notre graphique « rempli ». Notez l’utilisation de .andSelf() pour inclure l’étoile qui
nous avons sélectionné.

Ligne 8 : Ceci est assez similaire à la ligne sept, mais nous supprimons le graphique rempli des étoiles ultérieures. Cette
est nécessaire au cas où la moyenne de ce widget a baissé depuis le dernier vote.

Ligne 9 : Ici, nous mettons à jour la zone grise sous le widget, qui affiche une note plus précise,
et permet à un visiteur de savoir combien de votes ont été exprimés.

Étape 4. Que le vote commence

La dernière étape de l’interface utilisateur consiste à activer le vote. Nous allons ajouter un gestionnaire de clics à chacune des étoiles. Ce gestionnaire de clics sera responsable de l’envoi des données de vote au serveur.

Voici le gestionnaire de clics :

$('.ratings_stars').bind('click', function() {
            var star = this;
            var widget = $(this).parent();
            
            var clicked_data = {
                clicked_on : $(star).attr('class'),
                widget_id : widget.attr('id')
            };
            $.post(
                'ratings.php',
                clicked_data,
                function(INFO) {
                    widget.data( 'fsr', INFO );
                    set_votes(widget);
                },
                'json'
            ); 
        });

Dans ce bloc de code, nous commençons par créer certaines variables non seulement pour plus de clarté, mais, dans ce cas, afin qu’elles puissent être utilisées dans le rappel .post. N’oubliez pas que le gestionnaire de clics est affecté aux étoiles, nous avons donc également besoin de cette deuxième variable, widget, pour avoir l’objet contenant les données.

Tout d’abord, nous configurons nos données sortantes, que nous plaçons dans l’objet clicked_data. Nous récupérons la classe qui inclut un nom de classe au format star_# nous indiquant quel vote est donné, et nous nous préparons à l’envoyer au serveur, avec l’ID du widget.

L’ID du widget est la pierre angulaire sur laquelle repose ce système de vote. Il nous permet de rechercher nos données stockées et de montrer facilement ces données au visiteur.

Enfin, en ligne, nous envoyons ces informations au serveur. Le serveur ajoutera le vote aux totaux actuels et renverra les informations au navigateur contenant les données mises à jour. Les valeurs affichées par le widget sont ensuite mises à jour avec set_votes().

Étape 5. PHP : Création de la classe

Maintenant que l’interface utilisateur est terminée, nous devons créer un script côté serveur pour stocker et récupérer les données de vote.

Nous allons créer une classe très simple en PHP, appelée « Ratings », et l’utiliser pour gérer les requêtes du serveur pour notre système de notation. Il n’y aura que deux méthodes, plus l’invocation. L’utilisation de notre classe ressemblera à ceci:

# New Object
        $rating = new ratings($_POST['widget_id']);
    
        # either return ratings, or process a vote
        isset($_POST['fetch']) ? $rating->get_ratings() : $rating->vote();

Si vous revenez à la section quatre, vous verrez que nous chargeons les données avec la variable ‘fetch’ définie – c’est ce que nous recherchons ici à la ligne cinq. Si ce n’est pas défini, alors nous traitons un vote.

La première chose que nous allons examiner est le début de la classe et, plus précisément, le constructeur.

class ratings {
            
            private $data_file = './ratings.data.txt';
            private $widget_id;
            private $data = array();
               
        function __construct($wid) {
            
            $this->widget_id = $wid;
        
            $all = file_get_contents($this->data_file);
            
            if($all) {
                $this->data = unserialize($all);
            }
        }

serialize() et unserialize sont un excellent moyen de stocker facilement
Structures de données PHP sur disque.

Il se passe beaucoup de choses ici en très peu de lignes, je vais donc couvrir les points importants.

Ligne 3 : Cela doit être défini sur un fichier texte que vous souhaitez utiliser pour stocker vos données. Nous n’utilisons pas de base de données pour ce projet, bien que vous puissiez facilement le faire. Un simple fichier suffira à nos besoins.

Ligne 7 : Le constructeur. Ceci est appelé lorsque nous créons notre objet et stocke immédiatement l’ID du widget.

Ligne 11 : Nous essayons de charger le fichier texte. Si le fichier n’existe pas, très bien, mais sur certains systèmes, vous devrez le créer à l’avance et lui donner les autorisations appropriées pour que PHP puisse le lire et l’écrire.

Ligne 14 : Cette ligne est importante. Il prend les données du fichier texte – s’il y en a un – et le désérialise(). Le fichier contient un tableau PHP complexe qui a été converti en une représentation en texte brut, via serialize(), ce qui nous permet de le stocker et de le relire sous forme de tableau plus tard.

Étape 6. La méthode get_ratings().

Cette méthode est appelée soit seule, soit à partir de la méthode vote(). Il trouve les données d’un ID de widget particulier et les renvoie à la page de demande, au format JSON.

public function get_ratings() {
        if($this->data[$this->widget_id]) {
            echo json_encode($this->data[$this->widget_id]);
        }
        else {
            $data['widget_id'] = $this->widget_id;
            $data['number_votes'] = 0;
            $data['total_points'] = 0;
            $data['dec_avg'] = 0;
            $data['whole_avg'] = 0;
            echo json_encode($data);
        } 
    }

Cela semble seulement compliqué – c’est en fait assez simple. La première chose que nous faisons est de vérifier si le tableau stocké dans $this->data a une clé correspondant à notre ID de widget. Si c’est le cas, nous renvoyons simplement ces informations, car ce sont les données du widget que la page demandait.

Nous n’avons rien à faire avec ces données car elles sont déjà sous forme de tableau. $this->data n’est qu’un tableau de tableaux. Nous encodons le tableau que nous voulons avec json_encode() et le renvoyons au navigateur.

S’il n’y a pas de données pour l’ID de widget que nous avons demandé, nous créons un enregistrement avec des valeurs nulles et le renvoyons au navigateur.

Étape 7. La méthode vote()

Ensuite, nous devons créer une méthode pour gérer les votes entrants. Lorsque la méthode se termine, elle doit appeler get_ratings() pour renvoyer les informations mises à jour au navigateur Web.

public function vote() {
            # Get the value of the vote
            preg_match('/star_([1-5]{1})/', $_POST['clicked_on'], $match);
            $vote = $match[1];

La première chose que nous faisons est d’obtenir la valeur du vote. N’oubliez pas que quelque part dans ‘clicked_on’ se trouve un nom de classe au format star_#. « étoile_4 », par exemple. Pour obtenir cette valeur, nous utilisons une expression régulière et capturons la valeur du nombre à $match[1].

 $ID = $this->widget_id;
            # Update the record if it exists
            if($this->data[$ID]) {
                $this->data[$ID]['number_votes'] += 1;
                $this->data[$ID]['total_points'] += $vote;
            }
            # Create a new one if it does not
            else {
                $this->data[$ID]['number_votes'] = 1;
                $this->data[$ID]['total_points'] = $vote;
            }

Ici, nous stockons $this->widget_id dans $ID pour plus de clarté – le code suivant devient un peu dur pour les yeux sans lui.

Nous vérifions si des informations pour cet identifiant existent et, si c’est le cas, nous ajoutons un vote au nombre total de votes et ajoutons les points du vote reçu. Il s’agit d’un total cumulé de tous les votes ; donc si une personne donne cinq étoiles et une autre, trois, cela fait huit points au total.

Si l’enregistrement n’existe pas, nous en créons un, avec un vote, et uniquement les points du vote entrant.

Pour finir

            $this->data[$ID]['dec_avg'] = round( $this->data[$ID]['total_points'] / $this->data[$ID]['number_votes'], 1 );
            $this->data[$ID]['whole_avg'] = round( $this->data[$ID]['dec_avg'] );
                  
            file_put_contents($this->data_file, serialize($this->data));
            $this->get_ratings();
        }

Une fois que nous avons mis à jour le total des votes et des points, nous devons calculer à la fois la moyenne exprimée sous forme de nombre entier et à une décimale près. Pour éviter d’avoir à faire le calcul deux fois, nous calculons d’abord la moyenne à une décimale sur la première ligne, puis arrondissons à un nombre entier, sur la deuxième ligne.

À la ligne quatre, nous stockons les informations modifiées sur le disque après les avoir traitées avec serialize(). Une fois les données stockées en toute sécurité, nous appelons $this->get_ratings() pour envoyer les nouvelles informations mises à jour au navigateur.

Conclusion

Par souci de simplicité, ce n’est pas une solution complète à 100 % mais utilisable à 100%. Pour améliorer ce projet, nous devrions stocker un cookie pour nous assurer que les gens ne votent qu’une seule fois, ou même enregistrer l’adresse IP. Il est également possible que deux premiers votes se produisent simultanément, et qu’un seul puisse être enregistré. C’est, cependant, un bon début, et est plus que approprié pour garder une trace des votes sur quelques poignées d’articles sur votre site Web. Les pensées? Merci d’avoir lu!

Alex

Alex