Dans ce live-coding, nous allons créer un chat géré en Ajax.
Cette méthode permet de découvrir ou redécouvrir le fonctionnement d'Ajax.
Attention : ceci est une introduction, cette méthode ne sera pas optimale et l'utilisation des websockets, par exemple, sera plus conseillée pour les sites ayant du trafic.
Base de données
Nous utiliserons une base de données simple contenant deux tables.
La table "users" contiendra 4 champs
- id, clé primaire
- password, chiffré
- pseudo
La table "messages" contiendra 4 champs
- id, clé primaire
- message
- created_at, automatique
- users_id, clé étrangère de "users"

Fichiers de base
Nous mettons en place quelques fichiers de base, téléchargeables sur Github, qui permettront de voir l'interface de chat et de gérer la session des utilisateurs.
Ces fichiers sont structurés comme ceci

Les fichiers sont utilisés comme suit :
- styles.css : fichier CSS du projet
- bdd.php : fichier de connexion à la base de données
- footer.php : pied de pages
- header.php : entêtes de pages
- scripts.js : fichier qui contiendra notre code javascript
- client_chat.sql : base de données
- connexion.php : page de connexion utilisateur
- deconnexion.php : page de déconnexion utilisateur
- index.php : page d'accueil, page principale du chat
- inscription.php : page d'inscription des utilisateurs
Envoi des messages
Une fois connecté, l'utilisateur pourra poster des messages de chat en remplissant le champ en bas de page et en validant (entrée et clic sur la coche)
Partie Javascript
Nous aurons donc besoin d'écouter 2 évènements, l'un sur la zone de texte pour la touche entrée, l'autre sur la coche pour le clic.
window.onload = () => {
// On va chercher la zone de texte
let texte = document.querySelector("#texte")
texte.addEventListener("keyup", verifEntree)
// On va chercher le bouton "valid"
let valid = document.querySelector("#valid")
valid.addEventListener("click", ajoutMessage)
}
Nous devons maintenant vérifier si la touche appuyée est la touche entrée, puis exécuter la fonction "ajoutMessage"
function verifEntree(e){
if(e.key == "Enter"){
ajoutMessage()
}
}
La fonction "ajoutMessage" se chargera d'envoyer une requête Ajax à notre serveur pour enregistrer le message
function ajoutMessage() {
// On récupère la valeur dans le champ "texte"
let message = document.querySelector("#texte").value
// On vérifie si on a un message
if (message != "") {
// On crée un objet JS pour le message
let donnees = {}
donnees["message"] = message
// On convertit les données en json
let donneesJson = JSON.stringify(donnees)
// On envoie les données en POST en Ajax
// On instancie XMLHttpRequest
let xmlhttp = new XMLHttpRequest()
// On gère la réponse
xmlhttp.onreadystatechange = function(){
if (this.readyState == 4) {
if(this.status == 201) {
// On a une réponse
// On efface le champ texte
document.querySelector("#texte").value = ""
}else{
// On reçoit une erreur, on l'affiche
let reponse = JSON.parse(this.response)
alert(reponse.message)
}
}
}
// On ouvre la requête
xmlhttp.open("POST", "ajax/ajoutMessage.php");
// On envoie la requête avec les données
xmlhttp.send(donneesJson);
}
}
Partie PHP
Une fois la partie javascript en place, nous allons devoir traiter les données reçues en PHP pour :
- Vérifier que la méthode utilisée est la bonne
- Vérifier que l'utilisateur est connecté
- Vérifier qu'on a bien un message
- Sauvegarder le message
<?php
// Ce fichier ajoute un message dans la base de données
session_start();
// On vérifie la méthode
if($_SERVER['REQUEST_METHOD'] == 'POST'){
// Bonne méthode
// On vérifie si l'utilisateur est connecté
if(isset($_SESSION['user']['id'])){
// L'utilisateur est connecté
// On récupère les données
$donneesJson = file_get_contents('php://input');
// On convertit les données
$donnees = json_decode($donneesJson);
// On vérifie qu'il y a un message
if(isset($donnees->message) && !empty($donnees->message)){
// Le message n'est pas vide
// On peut l'enregistrer
// On se connecte
require_once('../inc/bdd.php');
// On écrit la requête
$sql = 'INSERT INTO `messages`(`message`, `users_id`) VALUES (:message, :user)';
// On prépare la requête
$requete = $db->prepare($sql);
// On injecte les valeurs
$requete->bindValue(':message', strip_tags($donnees->message), PDO::PARAM_STR);
$requete->bindValue(':user', $_SESSION['user']['id'], PDO::PARAM_INT);
// On exécute la requête en vérifiant qu'elle fonctionne
if($requete->execute()){
http_response_code(201);
echo json_encode(['message' => 'Message enregistré']);
}else{
http_response_code(400);
echo json_encode(['message' => 'Une erreur est survenue']);
}
// On libère $db
$db = null;
}else{
// Le message est indéfini ou vide
http_response_code(400);
echo json_encode(['message' => 'Le message est vide']);
}
}else{
// L'utilisateur n'est pas connecté
http_response_code(400);
echo json_encode(['message' => 'Vous n\'êtes pas connecté(e)']);
}
}else{
// Mauvaise méthode
http_response_code(405);
echo json_encode(['message' => 'Mauvaise méthode']);
}
Afficher la liste des messages
Pour afficher la liste des messages, notre page d'accueil appellera un fichier PHP en Ajax à intervalle régulier (toutes les secondes dans notre exemple) qui fonctionnera comme suit :
- affichera les 5 derniers messages au chargement de la page
- affichera les nouveaux messages qui auraient été publiés depuis le chargement de la page
Nous allons donc devoir conserver l'id du dernier message affiché afin de ne charger que les messages nécessaires.
Le javascript
Dans notre fichier javascript, nous commençons par créer une variable globale permettant de conserver le dernier id chargé.
// Variables globales
let lastId = 0
Nous allons ensuite exécuter une fonction "chargeMessages" toutes les secondes (par exemple) en ajoutant la ligne ci-dessous dans "window.onload"
setInterval(chargeMessages, 1000)
Enfin, notre fonction "chargeMessages" enverra l'id du dernier message en Ajax à notre fichier "chargeMessages.php" et recevra les messages nouvellement créés (ou les 5 derniers si on recharge la page) puis les affichera dans la discussion.
function chargeMessages() {
// On charge les messages en Ajax
// On instancie XMLHttpRequest
let xmlhttp = new XMLHttpRequest()
// On gère la réponse
xmlhttp.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
// On a une réponse
// On convertit le JSON en objet JS
let messages = JSON.parse(this.response)
// On retourne la liste pour traiter l'ID le plus élevé en dernier
messages.reverse();
// On récupère la div "discussion"
let discussion = document.querySelector("#discussion")
// On boucle sur les messages
for (let message of messages) {
// On transforme la date en objet JS
let dateMessage = new Date(message.created_at)
// On ajoute le message avant le contenu déjà en place
discussion.innerHTML = `<p>${message.pseudo} a écrit le ${dateMessage.toLocaleString()} : ${message.message}</p>` + discussion.innerHTML
// On met à jour l'id
lastId = message.id
}
}else{
// On gère les erreurs
let erreur = JSON.parse(this.response)
alert(erreur.message)
}
}
}
// On ouvre la requête
xmlhttp.open("GET", "ajax/chargeMessages.php?lastId="+lastId)
// On envoie la requête
xmlhttp.send()
}
Le PHP
Il reste à créer le fichier "chargeMessages.php" qui récupérera les derniers messages publiés et les renverra en JSON.
Nous aurons 2 cas
- L'id du dernier message est égal à 0, on vient de charger la page, on envoie les 5 derniers messages
- L'id du dernier message est > 0, on charge les messages supérieurs à cet id (les nouveaux messages arrivés)
Le code sera le suivant
<?php
// On vérifie la méthode utilisée par la requête ajax
if($_SERVER['REQUEST_METHOD'] == 'GET'){
// On est en GET
// On vérifie si on a un id
if(isset($_GET['lastId'])){
// On récupère l'id et on nettoie
$lastId = (int)strip_tags($_GET['lastId']);
$filtre = ($lastId > 0) ? " WHERE `messages`.`id` > $lastId" : '';
// On va chercher les messages
// On se connecte à la bdd
require_once('../inc/bdd.php');
// On écrit la requête
$sql = 'SELECT `messages`.`id`, `messages`.`message`, `messages`.`created_at`, `users`.`pseudo` FROM `messages` LEFT JOIN `users` ON `messages`.`users_id` = `users`.`id`'.$filtre.' ORDER BY `id` DESC LIMIT 5';
// On exécute la requête
$requete = $db->query($sql);
// On récupère les données
$messages = $requete->fetchAll();
// On convertit en json
$messagesJson = json_encode($messages);
// On envoie
echo $messagesJson;
}
}else{
// Mauvaise méthode
http_response_code(405);
echo json_encode(['message' => 'Mauvaise méthode']);
}
Et voilà, un petit chat opérationnel !
Obtenir de l'aide
Pour obtenir de l'aide, vous pouvez accéder au serveur Discord pour une entraide par chat
Les fichiers de cet article sont disponibles sur Github