BackBone Models & Play!> Models jouent ensemble : La suite
Pré-requis (indispensable!)
Avoir lu la 1Ăšre partie : http://k33g.github.com/2012/02/04/BB-MODELS-AND-PLAY.html
Problématique :
Je souhaite affecter une technologie Ă un bookmark (je classe mes bookmarks par techno).
Câest Ă dire que cĂŽtĂ© Backbone, je vais avoir ceci :
Dans app/views/Application/index.html, je vais avoir un nouveau modĂšle Techno :
window.Techno = Backbone.Model.extend({
url : 'bb/techno',
defaults : {
id: null,
label : ""
}
});
window.Technos = Backbone.Collection.extend({
model : Techno,
url : 'bb/technos'
});
Et je voudrais pouvoir faire des âchosesâ comme celles-ci :
var jstechno = technosCollection.find(function(techno){ return techno.get("label") == "Javascript";})
var LyonJS = new Bookmark({
label : "LyonJS",
website : "http://lyonjs.org/",
techno : jstechno
});
var ChezMoi = new Bookmark({
label : "ChezWouam",
website : "http://www.maison.fr",
techno : new Techno({ label : "NOTECHNO" })
});
Mais avant dâen arriver lĂ , il va falloir aller coder en consĂ©quence cĂŽtĂ© Play!>.
Préparation :
Créer un modÚle Play!> : Techno.java
Donc dans app/models, créer une classe Java, Techno.java :
package models;
import javax.persistence.Entity;
import play.data.validation.Required;
import play.db.jpa.Model;
@Entity
public class Techno extends Model {
@Required public String label = "???";
public Techno(String label) {
this.label = label;
}
public Techno() {
}
@Override
public String toString() {
return label;
}
}
Créer une relation entre Bookmark.java & Techno.java
Donc dans app/models, modifier la classe Java, Bookmark.java en ajoutant un membre techno de type Techno et annoté avec @ManyToOne (pour la relation) :
package models;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import play.data.validation.Required;
import play.db.jpa.Model;
@Entity
public class Bookmark extends Model {
@Required public String label;
@Required public String website;
//La modif c'est par lĂ
/* === Relations ===
- un Bookmark est lié à une techno
*/
@ManyToOne
public Techno techno;
//Fin de la modif.
public Bookmark(String label, String website) {
this.label = label;
this.website = website;
}
public Bookmark() {
}
@Override
public String toString() {
return label;
}
}
Modification conf/routes :
Modifions le fichier conf/routes : (lĂ mĂȘme chose que pour le tuto prĂ©cĂ©dent avec les bookmarks)
# Technos routes
GET /bb/techno Application.getTechno
POST /bb/techno Application.postTechno
PUT /bb/techno Application.putTechno
DELETE /bb/techno Application.deleteTechno
GET /bb/technos Application.allTechnos
Modifions le contrĂŽleur Application.java :
Dans la classe app/controllers/Application.java, ajoutons les mĂ©thodes nĂ©cessaires pour gĂ©rer les opĂ©rations de CRUD des technos lorsque Backbone âenvoieâ des informations : (câest exactement le mĂȘme principe que pour le tuto prĂ©cĂ©dent) :
/*=== dans Application.java ===*/
/* TECHNOS */
/* GET */
public static void getTechno(String model) {
System.out.println("getTechno : "+model);
Gson gson = new Gson();
Techno techno = gson.fromJson(model,Techno.class);
Techno forFetchtechno = Techno.findById(techno.id);
//tester if found ...
renderJSON(forFetchtechno);
}
/* POST (CREATE) */
public static void postTechno(String model) {
System.out.println("postTechno : "+model);
Gson gson = new Gson();
Techno techno = gson.fromJson(model,Techno.class);
techno.save();
renderJSON(techno);
}
/* PUT (UPDATE) */
public static void putTechno(String model) {
System.out.println("putTechno : "+model);
Gson gson = new Gson();
Techno techno = gson.fromJson(model,Techno.class);
Techno updatedTechno = Techno.findById(techno.id);
updatedTechno.label = techno.label;
updatedTechno.save();
renderJSON(updatedTechno);
}
/* DELETE */
public static void deleteTechno(String model) {
System.out.println("deleteTechno : "+model);
Gson gson = new Gson();
Techno techno = gson.fromJson(model,Techno.class);
Techno technoToBeDeleted = Techno.findById(techno.id);
//tester if found ...
technoToBeDeleted.delete();
renderJSON(technoToBeDeleted);
}
/* GET */
public static void allTechnos() {
System.out.println("allTechnos");
List<Techno> technos = Techno.findAll();
renderJSON(new Gson().toJson(technos));
}
Maintenant, passons aux choses sérieuses :
Gestion de la relation cÎté Play!>
Nous allons modifier les méthodes relatives aux bookmarks dans Application.java.
Les méthodes suivantes ne changent pas (pour ce qui concerne ce tuto, car je ne traßte pas tous les cas de figure) :
public static void getBookmark(String model) // GET-readpublic static void deleteBookmark(String model) // DELETE-deletepublic static void allBookmarks() // GET
Seules changent : postBookmark et putBookmark
Que fait on ?
Lorsque Play!> (en fait le contrĂŽleur) reçoit un bookmark âJSONisĂ©â de Backbone, on vĂ©rifie :
- si il a une techno (si il est lié à une techno),
- et si cette techno a un
idrenseigné - si cet
idnâest pas renseignĂ©, on crĂ©e/persiste la techno en base :bookmark.techno.save(); -
dans le cas dâun
update, avant de faire unsave, on associe lâinstance techno Ă lâinstance bookmark (sinon Hibernate va gâŠ..r au moment dusave()) :updatedBookmark.techno = techno;/*=== dans Application.java ===*/ /* BOOKMARKS */ /* POST (CREATE) */ public static void postBookmark(String model) { System.out.println("postBookmark : "+model); Gson gson = new Gson(); Bookmark bookmark = gson.fromJson(model,Bookmark.class); if(bookmark.techno!=null){ Techno techno = bookmark.techno; //si la techno n'existe pas on la crĂ©e if(bookmark.techno.id == null) { bookmark.techno.save(); } } bookmark.save(); renderJSON(bookmark); } /* PUT (UPDATE) */ public static void putBookmark(String model) { System.out.println("putBookmark : "+model); Gson gson = new Gson(); Bookmark bookmark = gson.fromJson(model,Bookmark.class); Bookmark updatedBookmark = Bookmark.findById(bookmark.id); updatedBookmark.label = bookmark.label; if(bookmark.techno!=null){ Techno techno = bookmark.techno; //si la techno n'existe pas on la crĂ©e if(bookmark.techno.id == null) { bookmark.techno.save(); } updatedBookmark.techno = techno; } updatedBookmark.save(); renderJSON(updatedBookmark); }
Allons faire un tour chez Backbone
Maintenant que tout est prĂȘt cĂŽtĂ© serveur, on peut aller sâamuser cĂŽtĂ© client.
Ajoutons quelques technos :
Dans la console de votre navigateur préféré :
t1 = new Techno({label:"Javascript"}).save();
t2 = new Techno({label:"Coffeescript"}).save();
t3 = new Techno({label:"Java"}).save();
t4 = new Techno({label:".Net"}).save();
t5 = new Techno({label:"Scala"}).save();
t6 = new Techno({label:"HTML5"}).save();
t7 = new Techno({label:"CSS3"}).save();
t8 = new Techno({label:"Kotlin"}).save();
t9 = new Techno({label:"Ceylon"}).save();
//Oui je sais, normalement il faut que j'utilise des callbacks, mais on s'en fout, c'est une démo
Puis on vĂ©rifie que âtout sâest bien passĂ©â :
technosCollection = new Technos();
technosCollection.fetch({success: function() {
technosCollection.each(function(techno){ console.log(techno.get("id"),techno.get("label")); });
}});

CrĂ©ation dâun nouveau bookmark avec une techno existante
Toujours dans la console :
var jstechno = technosCollection.find(function(techno){ return techno.get("label") == "Javascript";})
var LyonJS = new Bookmark({
label : "LyonJS",
website : "http://lyonjs.org/",
techno : jstechno
});
LyonJS.save();
CrĂ©ation dâun nouveau bookmark avec une nouvelle techno
Toujours dans la console :
var ChezMoi = new Bookmark({
label : "ChezWouam",
website : "http://www.maison.fr",
techno : new Techno({ label : "NOTECHNO" })
});
ChezMoi.save();
Vérifications :
Encore dans la console :
bookmarks = new Bookmarks();
bookmarks.fetch({
success: function() {
bookmarks.each(function(bookmark) { console.log(bookmark.get("id"), bookmark.get("label")); });
}
});
On voit que nos bookmarks ont bien été ajoutés :

Vérifions aussi les technos :
technosCollection = new Technos();
technosCollection.fetch({
success: function() {
technosCollection.each(function(techno){ console.log(techno.get("id"),techno.get("label")); });
}
});

Vérification ultime :
On reste dans la console :
var LyonJS = bookmarks.find(function(bookmark){ return bookmark.get("label") == "LyonJS";});
Puis faites :
LyonJS.get("techno");
ça câest bon, donc on continue :
LyonJS.get("techno").get("label");
Arghhhh ! Et lĂ , câest le drame (encore) :
Vous obtenez un âhorribleâ TypeError: Object # <Object> has no method 'get'

Mais que sâest-il donc passĂ© ?
En fait, lorsque que vous avez fait un fetch() du bookmark, il a bien ârĂ©cupĂ©rĂ©â les infos du serveur, mais nâa pas âcastĂ©â la techno du bookmark en Backbone.Model, donc votre techno (du bookmark) existe bien, mais est un simple Object. Les donnĂ©es sont bien lĂ , vous pouvez vĂ©rifier en faisant ceci : LyonJS.get("techno").label;
Mais ça mâarrange pas ! Comment fait-on ?
Je vais vous prĂ©senter une mĂ©thode âĂ lâarracheâ, pas forcĂ©ment la plus Ă©lĂ©guante, mais qui a le mĂ©rite dâĂȘtre simple et donc de vous Ă©viter beaucoup de soucis (dâeffets de bord) pour finalement pas beaucoup dâeffort.
Nous allons ajouter une méthode fetchWithTechno à notre modÚle Bookmark (nous sommes toujours cÎté Backbone pour mémoire) :
window.Bookmark = Backbone.Model.extend({
url : 'bb/bookmark',
defaults : {
id: null,
label : "",
website : ""
},
initialize : function Bookmark(){
console.log("Hello i'am a bookmark ...");
return this;
},
fetchWithTechno : function (callbck) {
this.fetch({success:function(model){
if(model.get("techno")){
var techno = new Techno({id:model.get("techno").id});
techno.fetch({
success:function(technoModel){
delete model.techno;
model.set({techno : technoModel});
if(callbck) callbck(model);
}
})
} else {
if(callbck) callbck(model);
}
}});
}
});
# Que fait donc fetchWithTechno() ?
Cette mĂ©thode, fait un fetch du bookmark, vĂ©rifie sâil a une techno, et si câest la cas, fait un fetch de cette techno afin de la âcasterâ en Backbone.Model.
# On vérifie ? (penser à raffraßchir la page de votre navigateur)
//on récupÚre la liste des bookmarks
var bookmarks = new Bookmarks();
bookmarks.fetch({
success: function() {
bookmarks.each(function(bookmark) {
console.log(bookmark.get("id"), bookmark.get("label"));
});
}
});
//on récupÚre notre bookmark :
var LyonJS = bookmarks.find(function(bookmark){ return bookmark.get("label") == "LyonJS";});
//on le "fetch" mais avec la nouvelle méthode
LyonJS.fetchWithTechno(function(model){ console.log(model.get("techno").get("label"));})
//ou
LyonJS.fetchWithTechno(function(){ console.log(LyonJS.get("techno").get("label"));})
Et lĂ la techno de notre bookmark est bien un Backbone.Model. :)
Allez, un dernier pour la route, on fait la mĂȘme chose pour la collection :
window.Bookmarks = Backbone.Collection.extend({
model : Bookmark,
url : 'bb/bookmarks',
fetchWithTechnos : function(callbck) {
var that = this; // c'est important (on conserve le contexte)
this.fetch({
success : function() {
that.each(function(bookmark){
if(bookmark.get("techno")) {
var techno = new Techno({id : bookmark.get("techno").id});
techno.fetch({
success:function(technoModel){
delete bookmark.techno;
bookmark.set({techno : technoModel});
console.log("model with techno : ", bookmark, bookmark.get("techno").get("label"));
} // end success
}); // end fetch techno
} // end if
}); // end each
if(callbck) callbck(that);
} // end success
}); //end fetch collection
}
});
# Et on se fait une derniÚre vérification :
var bookmarks = new Bookmarks();
bookmarks.fetchWithTechnos(function(){ console.log("Rahhhh ! Lovely ! ça fonctionne ...");})
Et ça marche !

Bon, câest terminĂ©. Maintenant Ă vous de bosser ! Il reste plein de choses Ă faire : par exemple si vous tentez de supprimer une techno qui est attachĂ©e Ă un ou des bookmarks, ça va p***r cĂŽtĂ© serveur.
PS: : Si vous relevez des erreurs, si vous avez quelque chose de plus âĂ©lĂ©gantâ pour le faire, etc. ⊠Allez-y, je ne me vexerais pas, bien au contraire.
@+
⊠tiens avec Play!> v°2, ça donnerait quoi ? ⊠;)
Tweet