Modèles et unicité

Yop,

J’ai les 4 tables suivantes :

class Project < ActiveRecord::Base
has_many :project_permissions, :dependent => :destro
has_many :users, :through => :project_permissions
belongs_to :company

class User < ActiveRecord::Base
belongs_to :company
has_many :projects, :through => :project_permissions
has_many :project_permissions, :dependent => :destroy

class Company < ActiveRecord::Base

has_many :users, :dependent => :destroy
has_many :projects, :dependent => :destroy
has_many :jobs, :through => :company_jobs
has_many :company_jobs, :dependent => :destroy

class Job < ActiveRecord::Base
has_many :companies, :through => :company_jobs
has_many :company_jobs, :dependent => :destroy

(les tables company_jobs et project_permissions sont des tables de
jointure)

Pour franciser tout ceci, en gros, un projet appartient à une entreprise
et a plusieurs utilisateurs (lesquels appartiennent à différentes
entreprises).
Une entreprise peut offrir plusieurs services (Jobs) et un service peut
être fait dans différentes entreprises.

Le cadre étant posé, voici mon soucis :
Je souhaite afficher dans un select_box toutes les entreprises d’un
projet.
J’ai donc créer une petite méthode dans Project.rb :

def get_companies
self.users.find(:all).collect{|p| [p.company.name, p.company_id]}
end

Cette méthode a pour but de faire un tableau de tableaux pour la
select_box.
Exemple:

[ [Entreprise 1, id1], [Entreprise 2, id2, [Entreprise 3, id3]]

Seulement, lorsqu’une entreprises fournit plusieurs services, elle peut
avoir plusieurs utilisateurs.
Et à ce moment, dans mon tableau apparait plusieurs fois le même nom de
l’entreprise.
Du coup, ma select_box est composée de doublons (dans le cas de 2
services) ou plus…ce qui est assez gênant.
Je ne vois pas comment corriger ça :confused:

Merci d’avance pour vos conseils :wink:

Essaye de mettre :

class Project < AR::B
has_many :companies, :through => :users
end

Après tu peux faire :
project.companies

Et pour ton select du coup :
collection_select :ton_form, :company_id, @companies, :id, :name

Michel B.

2009/8/3 Matthieu H. [email protected]

Michel B. wrote:

Essaye de mettre :

class Project < AR::B
has_many :companies, :through => :users
end

Après tu peux faire :
project.companies

Et pour ton select du coup :
collection_select :ton_form, :company_id, @companies, :id, :name

Michel B.

2009/8/3 Matthieu H. [email protected]

merci pour ta réponse rapide, mais ça m’oblige à rajouter un champ
project_id dans la table User.

Non.

has_many :companies, :through => :users
Ca dit que ton modèle projet est lié indirectement à la table companies
en
passant par l’association de la table Users nommée elle aussi
:companies, il
fait la jointure lui-même, pas de champs à rajouter.

Michel B.

2009/8/3 Matthieu H. [email protected]

Michel B. wrote:

Non.

has_many :companies, :through => :users
Ca dit que ton modèle projet est lié indirectement à la table companies
en
passant par l’association de la table Users nommée elle aussi
:companies, il
fait la jointure lui-même, pas de champs à rajouter.

Michel B.

2009/8/3 Matthieu H. [email protected]

Pourtant, voici la réponse formelle de mon terminal (qui n’est pas en
accord avec toi ^^) :

p.companies
Company Load (0.0ms) Mysql::Error: Unknown column ‘users.project_id’
in ‘where clause’: SELECT companies.* FROM companies INNER JOIN
users ON companies.id = users.company_id WHERE ((users.project_id =
4))
ActiveRecord::StatementInvalid: Mysql::Error: Unknown column
‘users.project_id’ in ‘where clause’: SELECT companies.* FROM
companies INNER JOIN users ON companies.id = users.company_id
WHERE ((users.project_id = 4))

Si tu as bien :

class Project < AR::Base
has_many :project_permissions
has_many :users, :through => :project_permissions
has_many :companies, :through => :users
end

class ProjectPermission < AR::Base
belongs_to :user
belongs_to :project
end

class Users < AR::B
has_many :projects, :through => :project_permissions
belongs_to :company
end

class Company < AR::B
end

Avec dans tes tables correspondantes :
projects
id

projects_permissions
user_id
project_id

users
id
company_id
company
id

Ca devrait marcher.

Maintenant voyons voir

p.companies
Company Load (0.0ms) Mysql::Error: Unknown column ‘users.project_id’
in ‘where clause’: SELECT companies.* FROM companies INNER JOIN
users ON companies.id = users.company_id WHERE ((users.project_id =
4))
ActiveRecord::StatementInvalid: Mysql::Error: Unknown column
‘users.project_id’ in ‘where clause’: SELECT companies.* FROM
companies INNER JOIN users ON companies.id = users.company_id
WHERE ((users.project_id = 4))

Ah, tiens, oui, on dirait bien qu’il essaye de trouver l’association
dans
“users” plutôt que dans la table d’association, peut-être justement
parce
qu’il lui manque un objet AR::B pour gérer l’association justement.

Toujours pas, et c’est étonnant d’ailleurs car project.users et
user.company fonctionnent bien.

C’est user.projects qui ne doit pas fonctionner, selon ton message
d’erreur… Tu es sûr que tu n’as pas oublié ça :
class Users < AR::B
has_many :projects, :through => :project_permissions
end

Parce que ce que ton message d’erreur te dit, essentiellement, c’est
qu’il
n’arrive pas à trouver les users qui correspondent à un project en
cherchant
à partir d’une hypothétique clef users.project_id, ce qui laisse
soupçonner
qu’il n’a pas trouvé la liaison has_many :projects et qu’il ne sait pas
qu’elle doit passer par project_permissions.project_id au lieu de
users.project_id ; or dans le code que tu avais envoyé au départ cette
liaison n’apparaissait pas.

Michel B.

2009/8/4 Matthieu H. [email protected]

Michel B. wrote:

C’est user.projects qui ne doit pas fonctionner, selon ton message
d’erreur… Tu es sûr que tu n’as pas oublié ça :
class Users < AR::B
has_many :projects, :through => :project_permissions
end

2009/8/4 Matthieu H. [email protected]

Cette ligne est dans mon code depuis le début :confused:
D’ailleurs :

u = User.find(1)
SQL (0.2ms) SET NAMES ‘utf8’
SQL (0.2ms) SET SQL_AUTO_IS_NULL=0
User Columns (2.5ms) SHOW FIELDS FROM users
User Load (0.4ms) SELECT * FROM users WHERE (users.id = 1)
=> #<User id: 1, login: “admin”, identity_url: nil, name: “Anthony
Mathis”, email: “[email protected]”, crypted_password:
“191f1e48877bde6d34a97100f4951dce50685005”, salt:
“824ee4c773d7fa147d688b23fdd3300bb04f3011”, remember_token: nil,
activation_code: nil, state: “active”, send_notifications: true,
receive_notifications: true, remember_token_expires_at: nil,
password_reset_code: “bfd796a069095dfbc3a71bdbc52d8b78fa0ab604”,
activated_at: “2009-03-12 12:16:40”, deleted_at: nil, created_at:
“2009-03-09 11:54:02”, updated_at: “2009-05-18 12:22:32”, company_id: 1>

u.projects
Project Load (0.7ms) SELECT projects.* FROM projects INNER JOIN
project_permissions ON projects.id = project_permissions.project_id
WHERE ((project_permissions.user_id = 1))
Project Columns (1.9ms) SHOW FIELDS FROM projects
=> [#<Project id: 4, name: “monbati”, user_id: 1, company_id: 1,
customer_id: 0, description: nil, adress: “18 rue Timken”, cp: “68000”,
ville: “colmar”, started_at: nil, completed_at: nil, created_at:
“2009-03-12 12:57:28”, updated_at: “2009-03-13 07:57:05”>, #<Project id:
5, name: “Artitest”, user_id: 1, company_id: 1, customer_id: 0,
description: nil, adress: “dd”, cp: “75000”, ville: “Paris”, started_at:
nil, completed_at: nil, created_at: “2009-03-12 13:47:02”, updated_at:
“2009-03-12 13:47:02”>]

C’est à ne rien y comprendre.
Pourtant je suis complètement d’accord avec toi, en y réfléchissant, un
petit has_many: companies, through => users devrait fonctionner :confused:

Bon, ben dans ce cas je ne vois qu’une chose, faire une appli de test
avec
purement ces liaisons à côté, et voir si ça marche. Si ça marche, c’est
qu’il doit y avoir un truc qui perturbe dans ton code, si ça ne marche
pas
c’est un bug de rails.

Michel B.

2009/8/4 Matthieu H. [email protected]

Michel B. wrote:

Bon, ben dans ce cas je ne vois qu’une chose, faire une appli de test
avec
purement ces liaisons à côté, et voir si ça marche. Si ça marche, c’est
qu’il doit y avoir un truc qui perturbe dans ton code, si ça ne marche
pas
c’est un bug de rails.

Michel B.

2009/8/4 Matthieu H. [email protected]

Yep, c’est ce que je vais faire.
En tout cas merci de l’attention que tu portes à mon sujet :wink:

Disons, si c’est un bug de Rails ça m’intéresse de savoir ^^°

Michel B.

2009/8/4 Matthieu H. [email protected]

Je retombe sur le même problème.
Je te mets exactement le contenu de mes fichiers:

class Company < ActiveRecord::Base
has_many :users
end

class Project < ActiveRecord::Base
has_many :project_permissions
has_many :users, :through => :project_permissions
has_many :companies, :through => :users
end

class User < ActiveRecord::Base
has_many :projects, :through => :project_permissions
belongs_to :company
has_many :project_permissions
end

class ProjectPermission < ActiveRecord::Base
belongs_to :user
belongs_to :project
end

Contenu des tables:
companies: id, name
users: id, name, company_id
projects: id, name
project_permissions: user_id, project_id

D’après ma console:
user.projects --> fonctionne
project.users --> fonctionne
user.company – fonctionne
company.users --> fonctionne
project.companies --> fonctionne pas ^^

Alors il s’agit bel et bien d’un soucis de Rails. Ca pourrait être une
bonne
idée de le rapporter…

Michel B.

2009/8/4 Matthieu H. [email protected]

Pour info, j’ai rajouté un id_company dans la table project_permissions,
et je fais un simpl has_many : companies, :through =>
project_permissions dans Project.rb.

Et j’ai réglé mon petit problème d’unicité avec un :uniq => true.

Michel B. wrote:

Alors il s’agit bel et bien d’un soucis de Rails. Ca pourrait être une
bonne
idée de le rapporter…

Michel B.

2009/8/4 Matthieu H. [email protected]

bordel ^^ fallait que je tombe sur un bug
Merci encore pour ta contribution.
Bonne continuation :wink:

Matthieu H.

C’est aussi mon avis. A qui la faute ? :slight_smile:

Ouais. C’est dommage d’avoir à rajouter une info redondante dans la BDD
quand même.

Michel B.

2009/8/4 Matthieu H. [email protected]

Au bug. D’où l’idée de la signaler.

Michel B.

2009/8/4 Matthieu H. [email protected]

Meshak wrote:

On 3 ao�t, 14:59, Matthieu H. [email protected]
wrote:

(les tables company_jobs et project_permissions sont des tables de
jointure)

Juste une remarque, je ne sais pas si c’est vraiment une convention,
mais j’ai toujours nomm� les tables de jointures comme �a :

  • noms des 2 tables au pluriel
  • noms des tables par ordre alphab�tique

Ce qui �vite de d�finir le :through.

Pour franciser tout ceci, en gros, un projet appartient � une entreprise
et a plusieurs utilisateurs (lesquels appartiennent � diff�rentes
entreprises).
Une entreprise peut offrir plusieurs services (Jobs) et un service peut
�tre fait dans diff�rentes entreprises.

Question 1 : un projet peut-il �tre li� � plusieurs utilisateurs ? (On
dirait bien que “oui”)

Question 2 : “project_permissions” est-elle une simple table de
jointure ou poss�de-t-elle d’autres champs ?

S’il s’agit de simples tables de jointure, je te conseille de les
renommer en “companies_jobs” et “projects_users”.
De ce fait, �a te simplifie pas mal de choses :

class Project < ActiveRecord::Base
belongs_to :company
has_and_belongs_to_many :users

class User < ActiveRecord::Base
belongs_to :company
has_and_belongs_to_many :projects

class Company < ActiveRecord::Base
has_and_belongs_to_many :jobs
has_many :projects, :dependent => :destroy
has_many :users, :dependent => :destroy

class Job < ActiveRecord::Base
has_and_belongs_to_many :companies


Julien Vignolles

Certaines de mes tables de jointures comportent en effet plusieurs
champs, mais vu la dimension du projet sur lequel je travaille, inutile
de tout préciser, j’en aurais eu pour des pages d’explications.
J’ai fais mes recherches sur les relations de type HABTM, mais il semble
que le has_many, :through soit plus adapté aux tables de jointure ayant
plusieurs champs.

En ce qui concerne le nom des tables, c’est pas aussi simple que ça dans
la mesure où mon travail est un “stage”, et que je dois ajouter du code
à un projet en cours.
A moi donc de m’adapter, et c’est vrai que c’est pas toujours évident :stuck_out_tongue:
Je te remercie néanmoins de ta remarque, j’en prends note :wink: