Dans ce quatrième article sur le framework Grails, nous allons aborder les relations entre entités. Cela va nous permettre de posséder enfin un modèle de données correct et permettre à l’utilisateur, dans notre cas par exemple, de choisir à quelle course il veut participer.
Nous partons du principe que vous avez déjà suivi les trois articles précédents : Introduction à Grails, Grails partie 2 : création d’une application basique et Grails partie 3 : La validation.
Nous allons voir quatre types de relations puis les mettre en oeuvre sur notre application que nous mettons à jour depuis le deuxième article. Pour rendre notre explication plus claire, nous l’agrémenterons d’exemples très parlants de la documentation officielle.
Relation many-to-one
Commençons par la relation la plus simple. Une relation many-to-one n’est rien de plus qu’une classe possédant un attribut du type d’une autre classe. Voici comment ceci est réalisé dans une application Grails :
[pastacode lang= »java » message= » » highlight= » » provider= »manual »]
class Face {
Nose nose
}
[/pastacode]
[pastacode lang= »java » message= » » highlight= » » provider= »manual »]
class Nose {
}
[/pastacode]
Rien de bien compliqué ici, cette relation est réalisée de la même façon que dans beaucoup de langages.
Si nous voulons ensuite rendre cette relation bidirectionnelle et ainsi activer la sauvegarde et la suppression en cascade, il nous suffit de rajouter un attribut à la classe Nose :
[pastacode lang= »java » message= » » highlight= » » provider= »manual »]
class Nose {
static belongsTo = [face:Face]
}
[/pastacode]
Nous spécifions ainsi que la classe Nose appartient à la classe Face. Si nous supprimons un objet Face de la base de données, son objet Nose associé sera également supprimé. Vous pouvez remarquer que la façon de déclarer cet attribut est un peu spéciale. Nous devons spécifier le nom de l’attribut suivi de son type.
Relation one-to-one
La relation one-to-one ne diffère pas beaucoup de la précédente. Nous devons utiliser la propriété hasOne du côté de la classe propriétaire.
[pastacode lang= »java » message= » » highlight= » » provider= »manual »]
class Face {
static hasOne = [nose:Nose]
}
[/pastacode]
[pastacode lang= »java » message= » » highlight= » » provider= »manual »]
class Nose {
Face face
}
[/pastacode]
Ici, il s’agit bien de la figure qui possède un nez et non l’inverse.
Relation one-to-many
La relation one-to-many est également très simple à mettre en place :
[pastacode lang= »java » message= » » highlight= » » provider= »manual »]
class Author {
static hasMany = [books: Book]
}
[/pastacode]
[pastacode lang= »java » message= » » highlight= » » provider= »manual »]
class Book {
}
[/pastacode]
Il suffit, comme vous pouvez le voir, de déclarer un attribut hasMany du côté approprié de la relation. Ici, un auteur possède plusieurs livres. Cette collection books sera automatiquement injectée par Grails et sera de type java.util.Set. Elle sera chargée de façon lazy. Si vous souhaitez rendre cette relation bidirectionnelle, il suffit de faire la même opération que précédemment :
[pastacode lang= »java » message= » » highlight= » » provider= »manual »]
class Book {
static belongsTo = [author: Author]
}
[/pastacode]
Relation many-to-many
Pour cette dernière relation, many-to-many, chaque objet possède plusieurs autres objets d’un même type. Il suffit donc de déclarer un attribut hasMany des deux côtés de la relation. En plus de ceci, vous devrez définir un belongsTo du côté de la classe non propriétaire :
[pastacode lang= »java » message= » » highlight= » » provider= »manual »]
class Book {
static belongsTo = Author
static hasMany = [authors:Author]
}
[/pastacode]
[pastacode lang= »java » message= » » highlight= » » provider= »manual »]
class Author {
static hasMany = [books:Book]
}
[/pastacode]
Ainsi, la classe Author devient « propriétaire », et si elle est supprimée, tous ses Books associés le seront également. Vous devriez cependant éviter de vous servir de cette relation, car la génération des vues et contrôleurs de Grails ne la prend pas charge. Si vous vous en servez, vous devrez donc la gérer vous-même.
Mise en application
Maintenant que nous avons passé en revue les différents types de relations pouvant exister, nous allons les mettre en place sur notre application RaceTrack. Reprenons notre modèle de données :
D’après notre schéma, nous allons ici nous retrouver avec deux relations one-to-many. Une course peut avoir plus enregistrements et un participant peut participer à plusieurs courses (et donc avoir plusieurs enregistrements). Voici le résultat de la mise en place de ces deux relations :
[pastacode lang= »java » message= » » highlight= » » provider= »manual »]
class Race {
String name
Date startDate
String city
BigDecimal distance
BigDecimal cost
Integer maxRunners
static hasMany = [registrations:Registration]
static constraints = {
name blank: false, maxSize: 40
startDate validator: {return(it > newDate())}
city inList:["Paris", "Marseille"]
distance min: 0.0
cost min: 0.0, max: 100.0
maxRunners min: 0, max: 1000
registrations()
}
}
[/pastacode]
[pastacode lang= »java » message= » » highlight= » » provider= »manual »]
class Registration {
Boolean paid
Date dateCreated
static belongsTo = [race:Race, runner:Runner]
static constraints = {
paid()
dateCreated()
race()
runner()
}
}
[/pastacode]
[pastacode lang= »java » message= » » highlight= » » provider= »manual »]
class Runner {
String firstName
String lastName
Date dateOfBirth
String gender
String address
String city
String zipcode
String email
static hasMany = [registrations:Registration]
static constraints = {
firstName blank:false, maxSize: 40
lastName blank:false, maxSize: 40
dateOfBirth validator: {return (it < new Date())}
gender inList:["Homme", "Femme"]
address maxSize: 100
city maxSize: 60
zipcode()
email email:true, unique: true
registrations()
}
}
[/pastacode]
Il ne vous reste plus qu’à regénérer les vues et contrôleurs pour ces trois classes, comme vu dans le deuxième article.
Vous pouvez maintenant lancer l’application, créer une Race, un Runner, puis les relier grâce à une Registration.
Il ne nous reste plus qu’un petit détail à régler, l’affichage des objets. Comme vous pouvez le voir, le label par défaut est le nom de la classe, suivi de son id. Pour changer ceci, il vous suffit de surcharger la méthode toString, tout comme en Java.
[pastacode lang= »java » message= » » highlight= » » provider= »manual »]
class Race {
[...]
String toString(){
"${name}, ${startDate?.format('dd/MM/yyyy')}"
}
}
[/pastacode]
[pastacode lang= »java » message= » » highlight= » » provider= »manual »]
class Runner {
[...]
String toString(){
"${lastName} ${firstName}"
}
}
[/pastacode]
Retournez sur la création d’une Registration et voici le résultat !
Ainsi, nous avons fait le tour dans cet article des différentes relations possibles et mis à jour notre application en conséquence. Vous avez pu voir que le tout reste très simple, rapide et intuitif à implémenter.
Pour le prochain article, nous aborderons et analyserons plus en détail les contrôleurs et les vues.
Sources
http://www.infoq.com/minibooks/grails-getting-started