Une application MVC utilisant un service REST.
Le but est de construire une petite application qui permet de gérer ses contacts mails. La gestion des données sera assuré par un service REST. Toute la partie applicative écrite en javascript, avec jQuery, "respectera" le pattern MVC (model-view-controller). La liaison avec les données (l'api rest) sera assurée au moyen d'ajax.
Un aperçu de l'interface :
Le tp est découpé en 2 parties : Le service REST, et l'application.
https://dwarves.iut-fbleau.fr/git/monnerat/FI_WIM4.git
Pourque qu'ils fonctionnent, Il vous faudra modifier :
.htaccess
avec la bonne url de réécriture.model.php
, avec vos paramètres de connexion à la BD.model.js
, avec votre url pour le service rest.Le service REST utilisera votre base mysql sur dwarves. ( http://dwarves.iut-fbleau.fr/phpmyadmin).
CREATE TABLE `contacts` ( `id` INT(11) NOT NULL, `nom` VARCHAR(50) NOT NULL, `prenom` VARCHAR(50) NOT NULL, `email` VARCHAR(50) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `contacts` ADD PRIMARY KEY (`id`); ALTER TABLE `contacts` MODIFY `id` INT(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=0;
Vous pouvez utiliser le site generatedata pour obtenir un jeu de données dans votre table.
La partie serveur, l'api REST, sera écrite avec PHP. Le but est d'obtenir l'api suivante :
URL | Method | Action |
---|---|---|
/vers/votre/api/rest/contacts | GET | Récupére la liste de tous les contacts |
/vers/votre/api/rest/contacts/id | GET | Récupére le contact id |
/vers/votre/api/rest/contacts/id | PUT | Modifie le contact id |
/vers/votre/api/rest/contacts/id | DELETE | Efface contact id |
/vers/votre/api/rest/contacts | POST | Ajoute un contact |
Les données seront échangées au format JSON. Le fonctionnement de l'api
http GET localhost/~denis/json/contacts
HTTP/1.1 200 OK Connection: Keep-Alive Content-Length: 337 Content-Type: application/json Date: Tue, 15 Dec 2015 19:22:17 GMT Keep-Alive: timeout=5, max=100 Server: Apache/2.4.17 (Unix) PHP/5.6.15 X-Powered-By: PHP/5.6.15 {"contacts" : [ { "email": "lacus.Quisque.imperdiet@Nulla.org", "id": "59", "nom": "Warner", "prenom": "Scarlet" }, { "email": "gravida@sit.com", "id": "81", "nom": "Sullivan", "prenom": "Kieran" }, { "email": "magna.Cras.convallis@pharetraQuisque.edu", "id": "88", "nom": "Welch", "prenom": "Tyrone" }, { "email": "valarcher@u-pec.fr", "id": "134", "nom": "Valarcher", "prenom": "Pierre" } ] }
echo '{"nom":"Monnerat","prenom":"Denis","email":"monnerat@u-pec.fr"}'|http POST localhost/~denis/json/contacts
HTTP/1.1 201 Created Connection: Keep-Alive Content-Length: 74 Content-Type: application/json Date: Tue, 15 Dec 2015 19:39:14 GMT Keep-Alive: timeout=5, max=100 Location: /contacts/135 Server: Apache/2.4.17 (Unix) PHP/5.6.15 X-Powered-By: PHP/5.6.15 { "email": "monnerat@u-pec.fr", "id": "135", "nom": "Monnerat", "prenom": "Denis" }
echo '{"nom":"Monnerat","prenom":"Denis","email":"denis.monnerat@u-pec.fr"}'|http PUT localhost/~denis/json/contacts/135
HTTP/1.1 204 No Content Connection: Keep-Alive Content-Length: 0 Content-Type: text/html; charset=UTF-8 Date: Tue, 15 Dec 2015 19:42:56 GMT Keep-Alive: timeout=5, max=100 Server: Apache/2.4.17 (Unix) PHP/5.6.15 X-Powered-By: PHP/5.6.15
http GET localhost/~denis/json/contacts/135
HTTP/1.1 200 OK Connection: Keep-Alive Content-Length: 80 Content-Type: application/json Date: Tue, 15 Dec 2015 19:44:54 GMT Keep-Alive: timeout=5, max=100 Server: Apache/2.4.17 (Unix) PHP/5.6.15 X-Powered-By: PHP/5.6.15 { "email": "denis.monnerat@u-pec.fr", "id": "135", "nom": "Monnerat", "prenom": "Denis" }
http DELETE localhost/~denis/json/contacts/135
HTTP/1.1 204 No Content Connection: Keep-Alive Content-Length: 0 Content-Type: text/html; charset=UTF-8 Date: Tue, 15 Dec 2015 19:45:59 GMT Keep-Alive: timeout=5, max=100 Server: Apache/2.4.17 (Unix) PHP/5.6.15 X-Powered-By: PHP/5.6.15
Pour simplifier le code de l'api REST, nous allons écrire une couche intermédiaire, le modèle : Le but ? abstraire notre table et la base qui la stocke sous forme d'une classe php qui permettra de gérer les contacts simplement (Create/Read/Update/Delete). Il existe une classe PHP PDO que nous pourrions utiliser. Nous allons utiliser un framework (ORM) qui permet, à partir des informations de la table, d'avoir un modèle correspondant (mapping) avec les méthodes de gestion classique déjà prêtes.
On va utiliser l'orm paris, très simple d'utilisation.
<?php require_once "/vers/idiorm/idiorm.php"; require_once "/vers/paris/paris.php"; ORM::configure('mysql:host=localhost;dbname=contacts'); ORM::configure('username', ''); ORM::configure('password', ''); class Contacts extends Model { } ?>
Cette simple définition crée la classe Contacts, que l'on peut manipuler très simplement :
<?php $contact = Contacts::create(); $contact->nom = "Monnerat"; $contact->prenom = "Denis" $contact->email = "monnerat@u-pec.fr"; $contact->save(); $contact=Contacts::find_one(12); $contact->delete(); ?>
Comme vous le voyez, on ne manipule pas de sql. On a directement accès aux objets (une table <-> une classe). On peut facilement créer au niveau des objets des relations, inutiles pour notre exemple).
Là encore, pour nous simplifier la vie, nous allons utiliser un petit framework php, slim (branche 2.x), pour :
Vous pouvez télécharger les sources ici, et consulter la documentation.
<?php // inlusion de slim et du modele require 'Slim-2.6.0/Slim/Slim.php'; require './models/model.php'; \Slim\Slim::registerAutoloader(); $app = new \Slim\Slim(); // les routes de l'api REST $app->get('/contacts(/:id)','getContacts'); $app->post('/contacts','addContact'); $app->delete('/contacts/:id','deleteContact'); $app->put('/contacts/:id','updateContact'); $app->run(); ?>
Il suffit ensuite d'écrire les fonctions exécutées pour chaque route. Exemple avec la suppression d'un contact
<?php function deleteContact($id) { $response=\Slim\Slim::getInstance()->response(); $response->headers->set('Content-Type', 'application/json'); $contact = Contacts::find_one($id); if ($contact) { $contact->delete(); $response->setStatus(200); }else{ $reponse->setStatus(404); } ?>
Le css est géré par concisecss. La partie fonctionnelle utilise jQuery, et l'échange de données se fait avec ajax, au format json (brute).
Au chargement, le formulaire est vide, et seul le bouton d'ajout est actif. On peut selectionner un contact en cliquant sur la ligne correspondante. Les informations sont automatiquement chagées dans le formulaire; seuls les boutons de suppression et de modification sont alors actifs. On peut delectionner un contact en recliquant sur la ligne correspondante.
Nous allons découper l'application suivant le modèle MVC :
model.js
view.js
controller.js
La partie html se résumera au strict nécessaire :
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <!-- code html pout la table et le formulaire --> <script src="jquery-1.10.1.js"></script> <script src="./model.js"></script> <script src="./view.js"></script> <script src="./controller.js"></script> <script> var model=$.Contact; var view=new $.View(); var controller=new $.Controller(view,model); </script> </body> </html>
Il fournit une "classe" contact qui permet de représenter un contact, avec les méthodes de mise à jour classique : lecture, ajout, modification,
suppression.
Une "méthode statique" permettra en outre de récupérer tous les contacts.
Une fragment du modèle :
var serviceUrl = "/~denis/rest/contacts"; // a configurer vers votre propre url ! $.extend({ Contact:function(id,nom,prenom,email){ var self = this; this.data={ "id":id, "nom":nom, "prenom":prenom, "email":email }; this.add=function(){ return $.ajax({ url : serviceUrl, method:"post", processData:false, contentType:"application/json", data:JSON.stringify(this.data), }) .then(function(data){ return data; }) }; } }); $.Contact.getAll=function(){ return $.getJSON(serviceUrl,null) .then(function(data){ var items=[]; $.each(data.contacts,function(i,el){ items .push( new $.Contact(el.id,el.nom,el.prenom,el.email) ); }) return items; }); }
Il faut le compléter de manière à disposer des "méthodes" de mise à jour. Notez que j'utilise, pour gérer l'asynchronisme d'ajax, la notion de promesse en javascript, avec jQuery.
C'est le module qui :
Un fragment de la vue
$.extend({ View:function(){ var form=$('#form'); // le formulaire var table=$('#ct'); // la table var self=this; // une réference vers moi this.updateTable=function(contacts){ var matable=$("#ct").children("tbody").empty(); $.each(contacts,function(i,contact){ matable.append("<tr data-id='"+contact.data.id+"'>" +"<td>"+contact.data.nom+"</td>" +"<td>"+contact.data.prenom+"</td>" +"<td>"+contact.data.email+"</td>" +"</tr>" ); }); }; this.listeners={ Add:function(){}, Update:function(){}, Delete:function(){}, }; // listeners vide par défaut, que le // controleur complétera pour etre notifié // des événements dans la vue. } });
C'est lui qui décide des actions des modifications sur la vue en fonction des événements.
Un fragment du contrôleur
$.extend({ Controller:function(view,contacts){ var self=this; this._reloadContacts = function(){ view.working(); contacts .getAll() .done(function(items){ view.updateTable(items); view.noworking(); }) } this._reloadContacts(); } });