Choses à faire

24h dans une journée, et tant de choses à faire !

La couche Modèle de Rails (Partie 1)

publié le 5 décembre 2009 par Pierre Quillery

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 !

Les textes, illustrations et démonstrations présents sur ce site sont la propriété de leurs auteurs respectifs, sauf mention contraire (photo de la bannière).
Chosesafaire.fr, un site propulsé par Wordpress, vous est proposé par Pierre Quillery & Killian Ebel.

Valid XHTML 1.0 Strict