Ruby on Rails - le modèle has_and_belongs_to_many

01 February 2010      Commentaires (0)    

Rails 2.3.2 && Ruby 1.8.7

Imaginons une petite application tout bête pour enregistrer des projets et les membres qui y participent dans une base de données. Les membres peuvent être sur différents projets et les projets peuvent avoir plusieurs membres. Pour simplifier le tutoriel, on n'enregistre que le nom du projet et le pseudo du membre.

Ce petit tuto sans prétention explique comment procéder pas à pas. Il implique tout de même que vous sachiez créer un projet et configurer le fichier database.yml dans le répertoire config.

Echaffaudage des deux modèles

On va utiliser l'échafaudage pour créer... pratiquement tout ce dont on a besoin : les modèles, les controllers, les vues et la base des formulaires.

ruby script/generate scaffold Projet nom:string

puis

ruby script/generate scaffold Membre pseudo:string

Attention :

Le nom du modèle doit commencer par une majuscule et être un mot au singulier. Tous les types des propriétés (nom et pseudo) commencent par une minuscule. La création de la table, plus tard, ne fonctionnera pas s'il y a des majuscules qui trainent.

Modification des modèles

On va ensuite connecter les deux modèles. On a dit que chaque projet peut avoir plusieurs membres et chaque membre peut participer à plusieurs projets. C'est le parfait exemple d'une relation has_and_belongs_to_many.

On modifie donc les deux modèles (membre.rb et projet.rb dans app/model) :

class Project < ActiveRecord::Base
  has_and_belongs_to_many :membres
end

class Member < ActiveRecord::Base
  has_and_belongs_to_many :projets
end

Attention : notez que le nom du modèle est au pluriel.

Création de la table de jointure

Ce genre de relation exige ce qu'on appelle une table de jointure. C'est une nouvelle table dans la base de données qui enregistre chaque couple. A chaque fois qu'un nouveau membre s'inscrit à un projet, une nouvelle ligne est créée dans cette table.

Rails possède une convention de nommage pour les tables de jointure : il faut appeler cette table du nom des modèles concernés (pour nous : membre et projet) au pluriel, dans l'odre alphétique, séparés par un underscore.

Notre table s'appellera donc membres_projets

Pour créer la migration qui créera la table dans la base :

ruby script/generate migration create_membres_projets

On retrouve ce fichier dans le répertoire db/migration. Il faut le construire de cette manière :

class CreateMembresProjets < ActiveRecord::Migration
  def self.up
    create_table :membres_projets, :id => false do |t|
      t.integer :membre_id
      t.integer :projet_id
    end
  end

  def self.down
    drop_table : membres_projets
  end
end

Attention :

:id => false est très important. S'il n'y est pas, la table ne peut pas fonctionner. En effet, une table de jointure n'a pas d'index incrémenté automatiquement à chaque nouvelle entrée et n'a donc pas d'id.

Enregistrez le tout. Il est temps de construire les tables :

rake db:migrate

Pour la suite des évènements, testez le tout et créez quelques membres et quelques projets.

ruby script/server

Puis rendez vous sur http://localhost:3000/membres et http://localhost:3000/projets

Modification du formulaire

Il va ensuite falloir inscrire les membres aux projets par le biais des formulaires.

Dans le controller des membres, on se met la liste des projets à disposition pour les vues, et ce dans l'ordre alphétique - tant qu'à faire - pour les retrouver plus facilement.

# Dans app/controllers/membres_controller.rb
def edit
  @membre = Membre.find(params[:id])
  @projets = Projet.find(:all, :order => "nom")
end

Puis, dans la vue (app/views/membres/edit.erb.html), on ajoute la formule magique pour créer la balise select à choix multiple :

<h1>Editing membre</h1>

<% form_for(@membre) do |f| %>
  <%= f.error_messages %>

  <p>
    <%= f.label :pseudo %><br />
    <%= f.text_field :pseudo %>
  </p>
  <p>Projets :</p><br />
  <%= collection_select(:membre, :projet_ids, @projets, :id, :nom, {:prompt => " "}, { :multiple => true })
  <p>
    <%= f.submit 'Update' %>
  </p>
<% end %>

<%= link_to 'Show', @membre %> |
<%= link_to 'Back', membres_path %>

Le paramètre prompt est facultatif et permet simplement d'avoir une ligne de texte (ou vide) sans valeur associée.

Le paramètre multiple est celui qui permet de faire plusieurs choix dans la liste. Si on l'omet, on se retrouve avec un menu déroulant.

Modification des aperçus

Maintenant, il faut afficher tout ça. On doit pouvoir consulter qui sont les membres inscrits à un projet et quels sont les projets auxquels un membre est inscrit.

Pour les membres (app/views/membres/show.erb.html) :

<p>
  <b>Pseudo:</b>
  <%=h @membre.pseudo %>
</p>
<p>Inscrits aux projets suivants</p>
<ul>
<% for projet in @membre.projets %>
  <li><%=h projet.nom %></li>
<% end %>
</ul>

<%= link_to 'Edit', edit_membre_path(@membre) %> |
<%= link_to 'Back', membres_path %>

Et pour les projets (app/views/projets/show.erb.html) :

<p>
  <b>Nom:</b>
  <%=h @projet.nom %>
</p>
<p>Membres inscrits :</p>
<ul>
<% for membre in @projet.membres %>
  <li><%=h membre.pseudo %></li>
<% end %>
</ul>

<%= link_to 'Edit', edit_projet_path(@projet) %> |
<%= link_to 'Back', projets_path %>

Et voilà !

Ruby on Rails - le modèle has_and_belongs_to_many Left_arrow

Right_arrow Rubrique Tutos
> Laissez un p'tit mot ! <

Votre nom :

Votre e-mail :

Votre adresse e-mail ne sera pas affichée sur le site.



Ceci est un contrôle anti-robotspam : veuillez ne pas remplir le champ qui suit.