2011-10-13 62 views
9

我的工作具有模型UserProject,并且User可以分配给多个Project秒的应用程序,通过ProjectUser,与角色(如开发人员,设计师)。Rails 3中的has_many:通过加入+表条件/作用域

Project 
    has_many :project_users 
    has_many :users, :through => :project_users 

User 
    has_many :project_users 
    has_many :projects, :through => :project_users 

ProjectUser (user_id, project_id, role) 
    belongs_to :user 
    belongs_to :project 

我可以打电话@project.users@user.projects,但既然有不同的角色,我想成为一个更具体一点与关系。理想情况下,我希望能够做到以下几点:

@project.developers 
    # returns @project.users, but only where ProjectUser.role = 'Developer' 

@project.designers << @user 
    # creates a ProjectUser for @project, @user with role 'Designer' 

@user.development_projects 
    # returns projects where @user is assigned as a 'Developer' 

@user.design_projects << @project 
    # creates a ProjectUser for @project, @user with role 'Designer' 

目前,我有以下代码:

has_many :developers, :through => :project_users, :source => :user, 
         :class_name => "User", 
         :conditions => ['project_users.role = ?','Developer'] 

但这只是确实在获取单向的,并且不给我别的 - 我不能建立或分配任何东西。

我试图一些更复杂的逻辑,我认为可能的工作,但希望得到一些指点:

has_many :developer_assignments, :source => :project_user, 
           :conditions => { :role => 'Developer' } 
has_many :developers, :through => :developer_assignments # class_name? 

有什么建议?谢谢!

回答

1

这听起来像你正在寻找的是RoR的single table inheritancenamed scopes的组合。

查看下面的article关于多态关联的一个很好的例子。这应该可以帮助你达到以下:

@project.developers 
    # returns @project.users, but only where ProjectUser.role = 'Developer' 

@project.designers << @user 
    # creates a ProjectUser for @project, @user with role 'Designer' 

作用域会给你一个干净的方式来实现@user.development_projects但也有可能获得<<运营商需要更多的挂羊头卖狗肉。

12

has_many接受可定义/覆盖关联方法的块。这将允许您为<<创建自定义方法。我为你创建了一个小例子,你可以用类似的方式创建构建。根据要求@project.developers << @user

# Project.rb 
has_many :developers, :through => :project_users, :source => :user, 
     :conditions => "project_users.role = 'developer'" do 
     def <<(developer) 
      proxy_owner.project_users.create(:role => 'developer', :user => developer) 
     end 
     end 

现在你可以添加一个新的开发你的项目有。 @project.developers为您提供所有开发人员。

如果您有很多角色,则动态创建这些has_many语句可能会很有用。

# Project.rb 
ROLES = ['developer','contractor'] 

ROLES.each do |role|   
    self.class_eval <<-eos 
    has_many :#{role.downcase}s, :through => :project_users, :source => :user, 
      :conditions => "project_users.role = '#{role}'" do 
      def <<(user) 
       proxy_owner.project_users.create(:role => '#{role}', :user => user) 
      end 
      end 
    eos 
end 

回顾上面的一切似乎并不像做事方式轨道。范围这应该使得构建和创建命令成为可能,而不用重新定义所有的东西。

希望这会有所帮助!

+0

谢谢您的回答。它解决了我的问题,但并未按照我希望的方式这样做 - 通过在“ProjectUsers”模型上使用范围。 声明':conditions =>“project_users.role ='#{role}'”'看起来好像不是很有道理,因为我喜欢调用像':conditions => {:scope =>:developer}' 。我仍然肯定这是可能的。无论哪种方式,我都会奖励你为你付出的努力,尽管这个答案不会被标记为正确的。感谢您的输入! – Jeriko

+0

谢谢。我真的希望'has_many:designers,:through =>:project_users,:source =>:user,:conditions => {:project_users => {:role =>:designer}}'自动工作,但显然是嵌套条件哈希不在构建和创建范围内。这是我能想到的最好的。 – HectorMalot

+0

以上是什么'proxy_owner'?是指对assiociation的另一方,类似于你用':extend'得到的'proxy_association'? – sbeam

0

您是否尝试过使用scopes呢?它不会让你做< <。但它简化了查询。

尝试:

Project 
    scope :developers, lambda { 
    includes(:project_users).where("project_users.role = ?", "developer") 
    } 

您将能够获得使用所有开发人员:@project.developers