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 un save, on associe l’instance techno Ă  l’instance bookmark (sinon Hibernate va g
..r au moment du save()) : 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")); });
}});

Alt "bbplay_2_001.png"

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 :

Alt "bbplay_2_002.png"

VĂ©rifions aussi les technos :

technosCollection = new Technos();
technosCollection.fetch({
	success: function() {
        technosCollection.each(function(techno){ console.log(techno.get("id"),techno.get("label")); });
	}
});

Alt "bbplay_2_003.png"

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'

Alt "bbplay_2_004.png"

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 !

Alt "bbplay_2_005.png"

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 ? 
 ;)

blog comments powered by Disqus

Related posts