Différences entre versions de « Js AJAX »
Ligne 540 : | Ligne 540 : | ||
==Ajout des événements== | ==Ajout des événements== | ||
− | Comme vous avez pu le remarquer, notre fichier ''index.html'' inclue le fichier ''buttons.js''. C'est dans ce fichier que l'on va mettre les | + | Comme vous avez pu le remarquer, notre fichier ''index.html'' inclue le fichier ''buttons.js''. C'est dans ce fichier que l'on va mettre les fonctions qui vont appeler les Web Serivces en fonction de l'appuie sur les boutons : |
<source lang="javascript" style="border:1px solid black;font-size:120%"> | <source lang="javascript" style="border:1px solid black;font-size:120%"> | ||
function doCreateUser(){ | function doCreateUser(){ |
Version du 11 mars 2016 à 20:03
Introduction
L'architecture informatique Ajax (acronyme d'Asynchronous JavaScript and XML) permet de construire des applications Web et des sites web dynamiques interactifs sur le poste client en se servant de différentes technologies ajoutées aux navigateurs web entre 1995 et 2005.
Ajax combine JavaScript, les CSS, JSON, XML, le DOM et le XMLHttpRequest afin d'améliorer maniabilité et confort d'utilisation des applications internet riches (RIA) 1,2 :
- DOM et JavaScript permettent de modifier l'information présentée dans le navigateur en respectant sa structure ;
- l'objet XMLHttpRequest sert au dialogue asynchrone avec le serveur Web ;
- XML structure les informations transmises entre serveur Web et navigateur.
Outre le XML, les échanges de données entre client et serveur peuvent utiliser d'autres formats, tels que JSON.
Les applications Ajax fonctionnent sur tous les navigateurs Web courants : Google Chrome, Safari, Mozilla Firefox, Internet Explorer, Konqueror, Opera, etc.
Nous allons utiliser l'objet XmlHttpRequest (XHR) avec une structure des informations en JSON. La partie serveur sera en Php et la partie cliente en Javascript / HTML / CSS.
Pré-requis
Pour développer en PHP, il ne faut pas oublier d'installer Apache HTTPD, PHP et pour ce projet nous allons utiliser également Memcached.
Partie serveur
Structure du projet
Créez l'arborescence suivante dans votre projet :
Objet user
Nous allons créer un objet User qui contiendra les attributs suivants:
- nom;
- prenom;
- age;
Cet objet implémentera une fonction toJson qui permettra de récupérer la chaine de caractères représentant l'objet ainsi que la méthode inverse fromJson.
Cela nous donne le contenu suivant pour le fichier user.php:
<?php
class User {
public $id, $nom, $prenom, $age;
public function User($id, $nom, $prenom, $age) {
$this->id = $id;
$this->nom = $nom;
$this->prenom = $prenom;
$this->age = $age;
}
/**
* Retourne la chaîne Json représentant l'objet
*/
public function toJson() {
return json_encode ( $this );
}
/**
* Retourne un objet User à partir de la chaîne Json ou false
* @param User | boolean $json
*/
public static function fromJson($json) {
$obj = json_decode ( $json );
if (isset ( $obj->id ) && isset ( $obj->nom ) && isset ( $obj->prenom ) && isset ( $obj->age )) {
return new User ( $obj->id, $obj->nom, $obj->prenom, $obj->age );
}
return false;
}
}
?>
Database Access Object
Pour stoker nos objets nous allons utiliser Memcache et pour respecter la programmation MVC nous allons utiliser le patron de conception DAO.
Dans ce DAO, nous allons implémenter les méthodes setUserTab, pour sauvegarder notre tableau d'utilisateur, et toutes les méthodes pour faire le CRUD.
Ajoutez les lignes suivantes, dans le fichier UserDao.class.php :
<?php
class UserDao {
const USER_TAB = "users";
const USER_ID = "user_id";
const MEMCACHE_ADDRESS = "127.0.0.1";
const MEMCACHE_PORT = 11211;
/**
* Retourne l'objet utilisateur en fonction de son identifiant
*
* @param int $id
*/
public function getUserById($id) {
$users = UserDao::getUsers ();
foreach ( $users as $user ) {
if ($user->id == $id) {
return $user;
}
}
return false;
}
/**
* Récupére le tableau d'utilisateur depuis memchache
*/
public function getUsers() {
$users = UserDao::getMemcacheServer ()->get ( UserDao::USER_TAB );
if (! isset ( $users ) || ! $users) {
$users = array ();
}
return array_values ( $users );
}
/**
* Retourne l'id de l'utilisateur créer
*
* @param User $user
*/
public function createUser(User $user) {
// On récupére l'identifiant
$id = UserDao::getCurrentId ();
error_log ( "Current id : " . $id );
// On le positionne
$user->id = $id;
// On récupére le tableau d'utilisateur
$users = UserDao::getUsers ();
// On ajoute l'utilisateur
$users [$id] = $user;
// On sauvegarde le tableau
UserDao::setUserTab ( $users );
// On retourne l'identifiant
return $id;
}
/**
* Met à jour l'objet utilisateur
*
* @param User $user
*/
public function updateUser(User $user) {
$users = UserDao::getUsers ();
// On vérifie que l'utilisateur existe
foreach ( $users as $index => $tmp ) {
if ($tmp->id == $user->id) {
// On le remplace
$users [$index] = $user;
// On sauvegarde le tableau
UserDao::setUserTab ( $users );
return true;
}
}
return false;
}
/**
* Efface l'objet utilisateur
*
* @param int $id
*/
public function deleteUser($id) {
$users = UserDao::getUsers ();
// On vérifie que l'utilisateur existe
foreach ( $users as $index => $user ) {
if ($user->id == $id) {
// On l'efface du tableau
unset ( $users [$index] );
// On sauvegarde le tableau
UserDao::setUserTab ( $users );
return true;
}
}
return false;
}
/**
* Stocke le tableau d'utilisateur dans memchache
*/
private function setUserTab($users) {
UserDao::getMemcacheServer ()->set ( UserDao::USER_TAB, $users );
}
/**
* Retourne l'identifiant du prochain utilisateur
*/
private function getCurrentId() {
$user_id = UserDao::getMemcacheServer ()->get ( UserDao::USER_ID );
if (! isset ( $user_id ) || ! $user_id) {
$user_id = 0;
UserDao::getMemcacheServer ()->set ( UserDao::USER_ID, $user_id );
}
UserDao::getMemcacheServer ()->increment ( UserDao::USER_ID );
return $user_id;
}
/**
* Retourne le serveur memchache
*/
private static function getMemcacheServer() {
$memcache = new Memcache ();
$memcache->addserver ( UserDao::MEMCACHE_ADDRESS, UserDao::MEMCACHE_PORT );
return $memcache;
}
}
?>
Web Service RESTful en PHP
Nous allons faire une page UserWS.class.php qui répond au GET, au POST, au PUT ainsi qu'au DELETE. GET sert à récupérer des informations, POST sert à créer / insérer des informations, PUT à les modifier et DELETE à les supprimer. N'hésitez pas à relire le cours !
<?php
class UserWS {
const VAR_ID = "id";
/**
* Execute le web service
*
* @param string $methode
* La méthode demandée (GET | POST | PUT | DELETE)
*/
public static function execute($method) {
// On test si on fait une requête en POST
error_log($method);
if ($method == "POST") {
// On fait un POST sur index.php
$id = UserWS::doPost ();
if ($id === FALSE) {
// HTTP 400 : Bad Request
http_response_code ( 400 );
} else {
// HTTP 201 : Created
http_response_code ( 201 );
// On retourne l'identifiant du nouvel utilisateur
echo json_encode ( $id );
}
} else if ($method == "GET") {
// On fait un GET sur index.php
if (! UserWS::doGet ()) {
// HTTP 400 : Bad Request
http_response_code ( 404 );
}
} else if ($method == "PUT") {
// On fait un PUT sur index.php
$result = UserWS::doPut ();
if ($result === -1) {
// HTTP 400 : Bad Request
http_response_code ( 400 );
} else if ($result === FALSE) {
// HTTP 404 : Not found
http_response_code ( 404 );
}else if ($result === TRUE) {
// HTTP 202 : Accepted
http_response_code ( 202 );
}
} else if ($method == "DELETE") {
// On fait un DELETE sur index.php
$result = UserWS::doDelete ();
if ($result === -1) {
// HTTP 400 : Bad Request
http_response_code ( 400 );
} else if ($result === FALSE) {
// HTTP 404 : Not found
http_response_code ( 404 );
}else if ($result === TRUE) {
// HTTP 202 : Accepted
http_response_code ( 202 );
}
}else {
// HTTP 405 : Method Not Allowed
http_response_code ( 405 );
}
}
/**
* Fonction appelée lors d'un GET
*/
private static function doGet() {
if (! isset ( $_GET [UserWS::VAR_ID] )) {
// Pas d'identifiant on retourne le tableau
echo json_encode ( UserDao::getUsers() );
return true;
} else {
$user = UserDao::getUserById ( $_GET [UserWS::VAR_ID] );
if ($user != false) {
// L'utilisateur demandé existe
echo json_encode ( $user );
return true;
}
}
// L'utilisateur demandé n'existe pas
return false;
}
/**
* Fonction appelée lors d'un POST
* Elle retourne l'identifiant de
*/
private static function doPost() {
/*
* On récupére le contenu de la requête HTTP
* qui contient la chaîne Json représentant l'utilisateur
*/
$json = file_get_contents ( 'php://input' );
// On transforme la chaîne Json en objet
$user = User::fromJson ( $json );
if ($user !== false) {
// On retourne l'identifiant du nouvel utilisateur
return UserDao::createUser ( $user );
}
return false;
}
/**
* Fonction appelée lors d'un PUT
*/
private static function doPut() {
/*
* On récupére le contenu de la requête HTTP
* qui contient la chaîne Json représentant l'utilisateur
*/
$json = file_get_contents ( 'php://input' );
// On transforme la chaîne Json en objet
$user = User::fromJson ( $json );
// Si la chaîne ne contient pas un objet User
if (! $user) {
return -1;
}
// On transforme la chaîne Json en objet
return UserDao::updateUser ( $user );
}
/**
* Fonction appelée lors d'un DELETE
*/
private static function doDelete() {
// On récupére l'identifiant utilisateur
$id = json_decode ( file_get_contents ( 'php://input' ) );
// On test la présence de l'identifiant
if (! isset ( $id ) && !is_numeric($id)) {
return -1;
}
// On efface l'utilisateur
return UserDao::deleteUser ( $id );
}
}
?>
On remarque au passage que la méthode http_response_code() permet de paramétrer le code HTTP dans l'entête. Cela va permettre de donner des informations au client sur le déroulement de la requête. Plus d'informations sur les codes HTTP ici
Appel du Web Service
Pour appeler notre Web Service nous devons encore ajouter quelques lignes. Tout d'abord, il faut autoriser l'appel depuis un autre serveur que celui ou sera exécuter le code PHP. Ajouter au début de index.php les lignes suivantes:
<?php
// Utilisé pour autoriser les appel Web Service en AJAX depuis toutes les adresses
header ( "Access-Control-Allow-Origin: *" );
// Utilisé pour autoriser les méthodes GET, POST, PUT et DELETE
header ( "Access-Control-Allow-Methods: GET, POST, PUT, DELETE" );
// On autorise les données dans le header
header ( "Access-Control-Allow-Headers: Content-Type" );
// On positione l'encodage en UTF-8 sinon json_encode ne fonctionnera pas !
header ( 'Content-Type: application/json; charset=utf-8' );
// On inclut l'objet User ainsi que les classes nécessaires
include_once 'class/User.class.php';
include_once 'class/UserDao.class.php';
include_once 'class/UserWS.class.php';
/**
* Fonction principale
*/
function main() {
// On récupére la méthode
$method = $_SERVER ['REQUEST_METHOD'];
// Si la méthode est de type option on ne continue pas l'exécution du script
// Cela arrive avec certains navigateurs
if ($method == "OPTIONS") {
header ( 'Access-Control-Allow-Origin: *' );
exit ();
}
// On appel notre Web Service
UserWS::execute ( $method );
}
main ();
?>
?>
On est fin prêt à passer à la partie cliente !
Partie cliente
Structure du projet
Créez l'arborescence suivante dans votre projet :
Objet user
Nous allons créer un objet User qui contiendra les attributs suivants:
- id;
- nom;
- prenom;
- age.
Cet objet implémentera une fonction toJson qui permettra de récupérer la chaine de caractères représentant l'objet. Cela nous donne le contenu suivant pour le fichier user.class.js :
function User(id, nom, prenom, age) {
this.id = id;
this.nom = nom;
this.prenom = prenom;
this.age = age;
this.toJson = function(){
return JSON.stringify(this);
}
}
// Simulation d'une méthode statique en la définissant après l'objet
User.fromJson = function (json){
try{
var obj = JSON.parse(json);
if(!obj){
return false;
}
return new User(obj.id, obj.nom, obj.prenom, obj.age);
}catch (e) {
return false;
}
}
User.fromObject = function (obj){
try{
if(!obj){
return false;
}
return new User(obj.id, obj.nom, obj.prenom, obj.age);
}catch (e) {
return false;
}
}
XmlHttpRequest
Dans cette partie, il s'agit de créer la fonction qui retournera l'objet XmlHttpRequest en fonction du navigateur. Ajoutez les lignes suivantes dans le fichier ajax.js :
function getXhr() {
var xhr;
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
xhr = new XMLHttpRequest();
} else {
// code for IE6, IE5
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
return xhr;
}
Appel des Web Services
Il faut maintenant appeler les Web Service grâce à l'objet XmlHttpRequest, ce que nous allons faire dans le fichier user-ws.js :
function getUsers() {
// Création de l'objet XHR
var xhr = getXhr();
// Ouverture de l'URL en GET
xhr.open("GET", "http://127.0.0.1/index.php", false);
// Envoie de la requête
xhr.send();
// Récupération de la réponse
var json = xhr.responseText;
// Parse du tableau d'utilisateur
var users = JSON.parse(json);
// Parse de chaque utilisateur
for(var i=0; i < users.length; i++){
users[i] = User.fromObject(users[i]);
}
return users;
}
function getUserById(id) {
// Création de l'objet XHR
var xhr = getXhr();
// Ouverture de l'URL en GET avec l'identifiant
xhr.open("GET", "http://127.0.0.1/index.php?id="+id, false);
// Envoie de la requête
xhr.send();
// Récupération de la réponse
var json = xhr.responseText;
// Parse de l'utilisateur
var user = User.fromJson(json);
return user;
}
function addUser(user){
// Création de l'objet XHR
var xhr = getXhr();
// Ouverture de l'URL en POST
xhr.open("POST", "http://127.0.0.1/index.php", false);
// Envoie de la requête avec l'objet User dans le body
xhr.send(user.toJson());
// Récupération de la réponse
var result = xhr.responseText;
return result;
}
function updateUser(user){
// Création de l'objet XHR
var xhr = getXhr();
// Ouverture de l'URL en POST
xhr.open("PUT", "http://127.0.0.1/index.php", false);
xhr.setRequestHeader('Content-Type', 'application/json');
// Envoie de la requête avec l'objet User dans le body
xhr.send(user.toJson());
// Récupération de la réponse
return xhr.status == 202;
}
function deleteUser(id){
// Création de l'objet XHR
var xhr = getXhr();
// Ouverture de l'URL en DELETE
xhr.open("DELETE", "http://127.0.0.1/index.php", false);
// Envoie de la requête avec l'objet User dans le body
xhr.send(JSON.stringify(id));
// Récupération de la réponse
return xhr.status == "202";
}
index.html
Il nous faut maintenant un fichier d'entrée pour appeler ce code Javascript, ce que nous allons faire avec index.html :
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>GUI Utilisateurs</title>
<script type="text/javascript" src="js/ajax.js" ></script>
<script type="text/javascript" src="js/user.class.js" ></script>
<script type="text/javascript" src="js/user-ws.js" ></script>
<script type="text/javascript" src="js/buttons.js" ></script>
</head>
<body>
<div>
Nom:<br/>
<input type="text" id="nom" ><br/>
Prénom:
<br/>
<input type="text" id="prenom" ><br/>
Age:
<br/>
<input type="text" id="age" >
<br/><br/>
<button onClick="doCreateUser();">Créer un utilisateur</button>
<button onClick="doUpdateUser();">Modifier un utilisateur</button>
<hr>
<button onClick="doGetUser();">Afficher un utilisateur</button>
<input type="text" id="id" placeholder="user id" >
<button onClick="doDeleteUser();">Effacer un utilisateur</button>
<br/><br/>
<button onClick="doGetUsers();">Afficher tous les utilisateurs</button>
</div>
<div id="affiche">
</div>
</body>
</html>
Ajout des événements
Comme vous avez pu le remarquer, notre fichier index.html inclue le fichier buttons.js. C'est dans ce fichier que l'on va mettre les fonctions qui vont appeler les Web Serivces en fonction de l'appuie sur les boutons :
function doCreateUser(){
var nom = document.getElementById("nom").value;
var prenom = document.getElementById("prenom").value;
var age = document.getElementById("age").value;
if(nom == "" || prenom == "" || age == ""){
alert("Missing field !");
return false;
}
var user = new User(0, nom, prenom, age);
if(!addUser(user)){
alert("Something went wrong !");
return false;
}
alert("Creation successful !");
eraseFieldValues();
return true;
}
function doGetUser(){
var id = document.getElementById("id").value;
if(id == ""){
alert("Missing field !");
return false;
}
var user = getUserById(id);
if(!user){
alert("Something went wrong !");
return false;
}
document.getElementById("nom").value = user.nom;
document.getElementById("prenom").value = user.prenom;
document.getElementById("age").value = user.age;
var div = document.getElementById("affiche");
div.innerHTML = user.toJson();
return true;
}
function doGetUsers(){
var users = getUsers();
if(!users){
alert("Something went wrong !");
return false;
}
var div = document.getElementById("affiche");
if(users.length == 0){
div.innerHTML = "Pas d'utilisateur !";
}else{
div.innerHTML = "";
for(var i=0; i < users.length; i++){
var user = users[i];
div.innerHTML += user.toJson()+"<br/>";
}
}
return true;
}
function doUpdateUser(){
var nom = document.getElementById("nom").value;
var prenom = document.getElementById("prenom").value;
var age = document.getElementById("age").value;
var id = document.getElementById("id").value;
if(nom == "" || prenom == "" || age == "" || id == ""){
alert("Missing field !");
return false;
}
var user = new User(id, nom, prenom, age);
if(!updateUser(user)){
alert("Something went wrong !");
return false;
}
alert("User modified !");
return true;
}
function doDeleteUser(){
var id = document.getElementById("id").value;
if(id == ""){
alert("Missing field !");
return false;
}
if(!deleteUser(id)){
alert("User does not exists !");
return false;
}
alert("User deleted !");
return true;
}
function eraseFieldValues(){
document.getElementById("nom").value = "";
document.getElementById("prenom").value = "";
document.getElementById("age").value = "";
return true;
}