2010-03-09 5 views
4

J'ai quelques projets. Ces projets ont des utilisateurs grâce à des adhésions.Relations profondes dans les rails

Cependant, ces utilisateurs appartiennent à des sociétés. La question est, comment puis-je savoir quelles entreprises peuvent accéder à un projet?

Idéalement, je serais en mesure de faire des projets.users.companies, mais cela ne fonctionnera pas.

Existe-t-il une façon agréable et agréable de le faire?

+0

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

+0

http://en.wikipedia.org/wiki/Law_of_Demeter –

+0

@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

Répondre

0

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.

0

Vous devez pouvoir définir les relations pour autoriser: project.users.companies.

Les associations sont:

Project has_one User belongs_to Company 
+0

Je ne sais pas si cette syntaxe fonctionne. –

+0

Ah, c'est censé être du code psuedo pour démontrer les associations. Pas de code réel en tant que tel. –

+0

Cela ne fonctionnera pas pour moi, car les projets et les utilisateurs sont plusieurs à plusieurs. –

0

Je pense que vous pouvez faire quelque chose comme ça.

class Project < ActiveRecord::Base 
    def self.companies 
    Company.all(:conditions => { :users => { :project_id => @project.id } }, :include => :users) 
    end 
end 

Mais cela fait un moment que j'ai utilisé ces fonctionnalités, donc je peux être rouillé.

Modifier: cela peut ne pas fonctionner. Je ne sais pas si j'ai obtenu le droit :include ou :join. Mais comme je l'ai dit, je suis rouillé.

1

Je suppose que vous avez ceci:

class Project < ActiveRecord::Base 
    has_and_belongs_to_many :users 
end 

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

class Company < ActiveRecord::Base 
    has_many :users 
end 

Et vous voulez obtenir project.companies. Le moins douloureux je peux imaginer est:

class Project < ActiveRecord::Base 
    has_and_belongs_to_many :users 
    def companies 
    Company.all(
     :joins => {:users => :projects}, 
     :conditions => {'projects_users.project_id' => self.id} 
    ).uniq 
    end 
end 

Notez le uniq à la fin. Cela supprimera les entreprises dupliquées.

Questions connexes