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-read
public static void deleteBookmark(String model) // DELETE-delete
public 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
id
renseigné - si cet
id
nâ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