Les autres réponses semblent négliger les adhésions que vous avez mentionnées. Si ce sont des objets réels dont vous avez un enregistrement, alors ce que vous choisissez de faire dépend de la taille de vos tables. Si elles ne sont pas très grand, alors la solution « plus OO » serait probablement ressembler à quelque chose comme ceci:
class Project < ActiveRecord::Base
has_many :memberships
has_many :users, :through => :memberships
def user_companies
self.users.map {|user| user.companies}.flatten.uniq
end
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
class User < ActiveRecord::Base
has_many :memberships
has_many :projects, :through => :memberships
belongs_to :company
end
class Company < ActiveRecord::Base
has_many :users
end
Cela pourrait ne pas effectuer grand, car il tire beaucoup de données à partir de la base de données et fait ensuite tous les filtrage en mémoire, mais c'est assez intuitif à lire. Si vous vouliez fourrer tout le calcul vers le bas dans la base de données, je pense qu'une bonne solution ressemblerait probablement quelque chose comme ça dans la classe de projet à la place:
def user_companies
Company.find_by_sql("SELECT company.*
FROM companies, users, memberships
WHERE companies.id = users.company_id
AND users.id = memberships.user_id
AND memberships.project_id = #{self.id}")
end
Il est un peu moins propre, mais mettra le plus du traitement le plus proche les données, et à seulement une table de trois jointures ne devrait pas finir par générer un tel nombre énorme de tuples que votre SGBD s'effondre à l'évidence.
J'ai trouvé cette question intéressante - avec has_many vous pouvez utiliser has_many: through, mais avec has_and_belongs_to_many vous devez être un peu plus créatif. – kikito
http://en.wikipedia.org/wiki/Law_of_Demeter –
@Arnis: La loi de Demeter est respectée ici. Seuls les objets composants directs O sont utilisés. Voir ma réponse ci-dessous. – kikito