La couche Modèle de Rails (Partie 1)
Rails, en bon framework qu’il est, propose bien sûr une couche Modèle assez complète à laquelle les deux prochains articles se proposent de vous introduire. Nous couvrirons dans cet article la définition du concept de Modèle, la création d’objets à l’aide de Migrations, validerons les données et créerons des Relations entre ces même Modèles … Copieux programme en perspective !
Tout d’abord, un petit disclaimer s’impose par rapport au contenu de cet article : il s’agit de la suite d’une série d’articles que j’avais commencé sur mon précédent blog sans aller jusqu’au bout. Je ne pense pas reprendre les anciens articles sur ce blog-ci, ce qui n’aurait pas beaucoup de sens dans un premier temps – je vous invite donc à aller consulter la page de présentation de mon projet ainsi que les premiers articles sur l’ancien site.
Un Modèle ?
Le Modèle est, comme nous l’avons précédemment, une des trois couches du modèles MVC. Il s’agit de celle qui est en charge de permettre l’enregistrement et la recherche de données. Typiquement, on utilise pour cela une classe dite d’ORM, de mapping objet-relationnel, qui va nous permettre de nous abstraire d’un SGBD particulier tout en s’intégrant mieux dans un ensemble de classes.
L’idée est de réduire au maximum la distance qui sépare la base de données (l’implémentation du stockage de données) des objets métiers de notre application (un Client, un Article, un Blog, un Commentaire, etc.). C’est également dans la couche Modèle que nous allons définir facilement les relations entre ces derniers (un Article a plusieurs Commentaires, etc.) ainsi que la validation des données (la date de naissance d’un utilisateur donné doit toujours être sous la forme jj/mm/aaaa, etc.).
Créons une nouvelle classe de Modèle à l’aide des Migrations
Tout d’abord, qu’est ce qu’une migration ? Il s’agit d’un fichier qui va contenir la définition d’une ou plusieurs tables ainsi que des champs qui les composent. Ils peuvent être créés soit automatiquement, (lorsque vous ajoutez une classe métier) soit par un plugin … Vous pouvez également en générer à tout moment.
L’idée est de placer dans un fichier texte facile à lire et à éditer la structure de votre base de données. Rails s’occupera ensuite de générer les commandes SQL qui correspondent dans la base correspondant à votre environnement courant (nous verrons cela plus loin). Vous pouvez ainsi facilement versionner votre base.
Créons donc un classe Modèle de test à l’aide de la console Rails. Comme vous le voyez, je précise immédiatement les champs que je connais de ce Modèle : ils seront ajoutés automatiquement par Rails dans le fichier de migration correspondant que nous complèterons manuellement tout à l’heure.
script/generate model Article titre:string date_publication:datetime contenu:text timestamps
Ça y est : quelques fichiers ont été créés, notamment app/models/article.rb qui est la classe de notre Modèle à propremment parler, et qui ne contient rien pour l’instant et db/migrate/xxxx_create_article.rb, notre Migration.
Voyons maintenant le contenu de ce deuxième fichier :
class CreateArticles < ActiveRecord::Migration
# Méthode appelée pour mettre en place la migration
def self.up
create_table :articles do |t|
t.string :titre
t.text :contenu
t.datetime :date_publication
# Va créer un champ auteur_id que nous utiliserons un peu plus loin
t.references :auteur
# Ajout automatique de champs tels que created_at, modified_at, etc.
# Ces champs seront ensuite gérés par Rails automatiquement.
t.timestamps
end
end
# Méthode permettant d'enlever la migration au besoin
def self.down
drop_table :articles
end
end
Nous pouvons éditer le fichier pour ajouter éventuellement des champs supplémentaires puis déclencher la migration en utilisant la ligne de commande Rails. Nous pourrons alors utiliser le fichier de Modèle et poursuivre notre application.
rake db:migrate
Validation dans le Modèle
Nous allons maintenant travailler avec la classe Modèle qui se trouve dans le répertoire app/models et ajouter du code pour faciliter la validation des champs qui s’y trouvent. Cela signifie que nous pourrons ainsi nous assurer par exemple du bon formattage d’un email, d’une date de naissance, de la présence d’un titre etc.
Cette validation empêchera par défaut toute création d’enregistrement ou mise à jour. Elle ajoutera le cas échéant des messages au modèle (personnalisables et même disponibles en plusieurs langues via l’internationalisation du framework) que vous pourrez simplement montrer à l’utilisateur pour lui permettre de corriger facilement sa saisie.
class Article < ActiveRecord::Base # Le titre de l'article est obligatoire (il ne doit pas être vide) validate_presence_of :titre # Le titre ne devrait pas être trop court ... validates_length_of :titre, :minimum => 30 # etc . end
Voici une page où vous trouverez toutes les méthodes de validation à votre disposition, les méthodes par défaut couvrent une bonne partie des besoins les plus évidents.
Nous verrons dans le prochain article comment exploiter efficacement ces messages de validation pour les afficher à l’internaute lors de sa saisie.
Créons des Relations
La création de relations est une partie intéressante des possibilités que Rails vous offre pour la couche Modèle : vous pouvez ainsi exprimer directement les principaux types de relations dont vous avez besoin pour reproduire tout schéma de base dans votre application.
Précisons d’abord que pour fonctionner, Rails a tout de même besoin que vous respectiez ses propres convention de nommage des champs pour lui permettre de retrouver ses petits et inférer ce que vous voulez faire. Ainsi, les champs de clés étrangères seront toujours nommées champ_id.
Je vais maintenant présenter les principaux types de relations disponibles, mais toutes ces méthodes sont très personnalisables, voyez dans la documentation pour en apprendre plus à leur sujet.
Une à Plusieurs
Un des cas le plus fréquent est la relation Une à Plusieurs : un Auteur a plusieurs Articles, un Article a plusieurs Commentaires, etc. Voici comment l’écrire dans les classes e Modèle (je pars du principe que les modèles correspondants ont été créés et migrés vers la base) :
# Fichier app/models/commentaire.rb class Commentaire < ActiveRecord::Base # C'est cette table qui contient la clé étrangère article_id, # on écrit donc belongs_to puis le nom du Modèle à lier au singulier belongs_to :article end # Fichier app/models/article.rb class Article < ActiveRecord::Base # Deuxième côté de la relation, on écrit has_many puis le # nom du Modèle au pluriel. has_many :commentaires end
Une à Une
Moins fréquent, vous pouvez restreindre cette relation pour créer une relation Une à Une en changeant le has_many par un has_one, comme dans cet exemple :
# Fichier app/models/utilisateur.rb class Utilisateur < ActiveRecord::Base # C'est cette table qui contient la clé étrangère profil_id belongs_to :profil end # Fichier app/models/profil.rb class Profil < ActiveRecord::Base # Deuxième côté de la relation, on écrit has_many puis le # nom du Modèle au singulier. has_many :utilisateur end
Plusieurs à Plusieurs et Relations Composites
Vous trouverez sur Internet de nombreux tutoriaux qui vous présenteront l’emploi de la technique dite du habtm ou has_and_belongs_to ; je ferais pour ma part l’impasse sur cette dernière qui est maintenant globalement dépréciée et qui ne devrait plus être utilisée dans la plupart des cas.
Voici donc la bonne façon de voir les choses pour créer une relation Plusieurs à Plusieurs viable, c’est à dire grâce au has_many :through :
# Fichier app/models/utilisateur.rb class Utilisateur < ActiveRecord::Base has_many :abonnements has_many :magazines, :through => :abonnements end # Fichier app/models/abonnement.rb class Abonnement < ActiveRecord::Base # Notez qu'on emploie dans cet objet deux clés étrangères belongs_to :utilisateur belongs_to :magazine end class Magazine < ActiveRecord::Base has_many :abonnements has_many :utilisateurs, :through => :abonnements end
Rien ne vous empêche d’ajouter des champs supplémentaires à votre objet Abonnement si vous souhaitez enrichir la relation ; on pourrait imaginer enregistrer la durée de l’abonnement par exemple. Ce n’était pas possible avec l’ancienne technique (habtm), et c’est une des raisons pour laquelle elle tombe petit à petit en désuétude.
Vous pouvez, sur le même principe, créer des relations composites (ou polymorphiques) qui pourront vous servir à factoriser pas mal de code en créeant une interface commune auxquels des Modèles de différents types pourront s’accrocher. C’est un peu complexe à expliquer, mais un exemple vaut mieux qu’un long discours :
class Coordonnee < ActiveRecord::Base # Il faut deux champs supplémentaires dans la table coordonnees : # - localisable_id, un entier # - localisable_type, une chaîne # => Utilisez la syntaxe suivante dans la Migration : # t.references :localisable, :polymorphic => true belongs_to :localisable, :polymorphic => true end class Utilisateur < ActiveRecord::Base has_many :localisations, :as => :localisable end class Societe < ActiveRecord::Base has_many :localisations, :as => :localisable end
Grâce à cette syntaxe, les Utilisateurs et les Societes partagent la possibilité d’avoir un objet Coordonnees qui leur est lié, ce qui serait plus complexe à mettre en place sinon. Nous pourrions facilement faire évoluer ce modèle au besoin, c’est ce qui fait sa force.
Dans quelques jours, nous verrons la suite de notre programme sur les Modèles dans Rails en parlant des différents types de base de données disponibles et leur configuration, des requêtes préparées, etc. Voir l’article suivant !
[...] la suite du premier article qui parlait de la couche Modèle dans Rails, n’hésitez pas à aller y jeter un œil si vous ne l’avez pas encore lu ! Ce post fait partie d’une série de tutoriels dont [...]